diff --git a/get/get.go b/get/get.go index 773574c..5479e7e 100644 --- a/get/get.go +++ b/get/get.go @@ -77,6 +77,7 @@ type TBDownloader struct { Mirror string Verbose bool Profile *embed.FS + listener net.Listener } // OS is the operating system of the TBDownloader. @@ -118,12 +119,13 @@ func (t *TBDownloader) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Serve runs ServeHTTP on an I2P listener func (t *TBDownloader) Serve() { - samlistener, err := sam.I2PListener("tor-mirror", "127.0.0.1:7656", "tor-mirror") + var err error + t.listener, err = sam.I2PListener("tor-mirror", "127.0.0.1:7656", "tor-mirror") if err != nil { log.Fatal(err) } - defer samlistener.Close() - http.Serve(samlistener, t) + defer t.listener.Close() + http.Serve(t.listener, t) } // GetRuntimePair returns the runtime.GOOS and runtime.GOARCH pair. diff --git a/get/torrent.go b/get/torrent.go new file mode 100644 index 0000000..d26f8a5 --- /dev/null +++ b/get/torrent.go @@ -0,0 +1,155 @@ +package tbget + +import ( + "fmt" + "io/ioutil" + "log" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/eyedeekay/sam3/i2pkeys" + cp "github.com/otiai10/copy" + "github.com/xgfone/bt/bencode" + "github.com/xgfone/bt/metainfo" +) + +func (t *TBDownloader) DownloadedFilesList() ([]string, error) { + files, err := ioutil.ReadDir(t.DownloadPath) + if err != nil { + return nil, err + } + var list []string + for _, f := range files { + list = append(list, f.Name()) + } + return list, nil + +} + +func (t *TBDownloader) GenerateMissingTorrents() error { + files, err := t.DownloadedFilesList() + if err != nil { + return err + } + for _, f := range files { + fp := filepath.Join(t.DownloadPath, f+".torrent") + af := filepath.Join(t.DownloadPath, f) + if !strings.HasSuffix(af, ".torrent") { + os.Remove(fp) + log.Println("Generating torrent for", fp) + meta, err := t.GenerateTorrent(af, nil) + if err != nil { + return err + } + file, err := os.Create(fp) + if err != nil { + return err + } + meta.Write(file) + file.Close() + } + snark, err := FundSnarkDirectory() + if err != nil { + return err + } + sf := filepath.Join(snark, f) + sfp := filepath.Join(snark, f+".torrent") + log.Println("Copying", af, "to", sf) + cp.Copy(af, sf) + log.Println("Copying", fp, "to", sfp) + cp.Copy(fp, sfp) + } + return nil +} + +func (t *TBDownloader) GenerateTorrent(file string, announces []string) (*metainfo.MetaInfo, error) { + info, err := metainfo.NewInfoFromFilePath(file, 5120) + if err != nil { + return nil, fmt.Errorf("GenerateTorrent:", err) + } + info.Name = filepath.Base(file) + + var mi metainfo.MetaInfo + mi.InfoBytes, err = bencode.EncodeBytes(info) + if err != nil { + return nil, fmt.Errorf("GenerateTorrent:", err) + } + + switch len(announces) { + case 0: + mi.Announce = "http://mb5ir7klpc2tj6ha3xhmrs3mseqvanauciuoiamx2mmzujvg67uq.b32.i2p/a" + case 1: + mi.Announce = announces[0] + default: + mi.AnnounceList = metainfo.AnnounceList{announces} + } + var url *url.URL + if t.listener != nil { + url, err = url.Parse("http://" + t.listener.Addr().(i2pkeys.I2PAddr).Base32() + "/" + filepath.Base(file)) + if err != nil { + return nil, fmt.Errorf("GenerateTorrent:", err) + } + if t.Mirror != "" { + mi.URLList = []string{url.String()} + } + } + + return &mi, nil +} + +func FundSnarkDirectory() (string, error) { + // Snark could be at: + // or: $I2P_CONFIG/i2psnark/ + // or: $I2P/i2psnark/ + // or: $HOME/.i2p/i2psnark/ + // or: /var/lib/i2p/i2p-config/i2psnark/ + // or: %LOCALAPPDATA\i2p\i2psnark\ + // or: %APPDATA\i2p\i2psnark\ + + I2P_CONFIG := os.Getenv("I2P_CONFIG") + if I2P_CONFIG != "" { + checkfori2pcustom := filepath.Join(I2P_CONFIG, "i2psnark") + if FileExists(checkfori2pcustom) { + return checkfori2pcustom, nil + } + } + + I2P := os.Getenv("I2P") + if I2P != "" { + checkfori2p := filepath.Join(I2P, "i2psnark") + if FileExists(checkfori2p) { + return checkfori2p, nil + } + } + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + // Start by getting the home directory + switch runtime.GOOS { + case "windows": + checkfori2plocal := filepath.Join(home, "AppData", "Local", "i2p", "i2psnark") + if FileExists(checkfori2plocal) { + return checkfori2plocal, nil + } + checkfori2proaming := filepath.Join(home, "AppData", "Roaming", "i2p", "i2psnark") + if FileExists(checkfori2proaming) { + return checkfori2proaming, nil + } + case "linux": + checkfori2phome := filepath.Join(home, ".i2p", "i2psnark") + if FileExists(checkfori2phome) { + return checkfori2phome, nil + } + checkfori2pservice := filepath.Join("/var/lib/i2p/i2p-config", "i2psnark") + if FileExists(checkfori2pservice) { + return checkfori2pservice, nil + } + case "darwin": + return "", fmt.Errorf("Automatic torrent generation is not supported on MacOS for now, for now copy the files manually.") + } + return "", fmt.Errorf("Unable to find snark directory") +} diff --git a/go.mod b/go.mod index 3be8cc9..3a3dc35 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( github.com/sirupsen/logrus v1.4.2 // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect + github.com/xgfone/bt v0.4.1 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/zieckey/goini v0.0.0-20180118150432-0da17d361d26 // indirect golang.org/x/text v0.3.6 // indirect diff --git a/go.sum b/go.sum index cf84970..96ca222 100644 --- a/go.sum +++ b/go.sum @@ -395,6 +395,8 @@ github.com/veandco/go-sdl2 v0.4.0/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2 github.com/walle/targz v0.0.0-20140417120357-57fe4206da5a/go.mod h1:nccQrXCnc5SjsThFLmL7hYbtT/mHJcuolPifzY5vJqE= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xgfone/bt v0.4.1 h1:51j3Mv9wqn415JsYJTYUIlMgcB3dAdQgnNPXDhwS78E= +github.com/xgfone/bt v0.4.1/go.mod h1:/GuvKo3WdkvlVahN84cjVWyia9ZJoxx/3BFIFZ5eGI4= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/ybbus/jsonrpc v2.1.2+incompatible h1:V4mkE9qhbDQ92/MLMIhlhMSbz8jNXdagC3xBR5NDwaQ= diff --git a/main.go b/main.go index 84a57af..d7bf3f5 100644 --- a/main.go +++ b/main.go @@ -85,7 +85,7 @@ var ( //mirror = flag.String("mirror", "http://dist.torproject.i2p/torbrowser/", "Mirror to use") mirror = flag.String("mirror", "http://dist.torproject.org/torbrowser/", "Mirror to use") /*onion = flag.Bool("onion", false, "Serve an onion site which shows some I2P propaganda, magnet links, your I2P mirror URL if configured")*/ - /*torrent = flag.Bool("torrent", false, "Create a torrent of the downloaded files and seed it over I2P using an Open Tracker")*/ + torrent = flag.Bool("torrent", false, "Create a torrent of the downloaded files and seed it over I2P using an Open Tracker") /*ptop = flag.Bool("p2p", false, "Use bittorrent over I2P to download the initial copy of Tor Browser")*/ ) @@ -233,6 +233,13 @@ func main() { } client.TBS.UnpackI2PAppData() client.TBS.UnpackI2PData() + if *torrent { + log.Println("Generating I2P torrents of Tor packages") + if err := client.TBD.GenerateMissingTorrents(); err != nil { + log.Fatal(err) + } + } + if *i2pbrowser { if err := client.TBS.RunI2PBWithLang(); err != nil { log.Fatal(err)