Files
i2p.plugins.tor-updater/onion/onion.go
2022-02-28 21:15:35 -05:00

132 lines
3.3 KiB
Go

package i2pdotonion
import (
"context"
"embed"
"fmt"
"io/fs"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"github.com/cretz/bine/tor"
)
//go:embed www/*
var content embed.FS
type I2POnionService struct {
OnionService net.Listener
ServeDir string
}
func NewOnionService(dir string) (*I2POnionService, error) {
ios := &I2POnionService{
ServeDir: filepath.Join(dir, "www"),
}
if err := ios.UnpackSite(); err != nil {
return nil, err
}
return ios, nil
}
func (ios *I2POnionService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := path.Clean(r.URL.Path)
if path == "/" {
path = "/index.html"
}
fmt.Println("ServeHTTP:", path)
path = filepath.Join(ios.ServeDir, path)
fmt.Println("ServeHTTP:", path)
finfo, err := os.Stat(path)
if err != nil {
http.NotFound(w, r)
return
}
if finfo.IsDir() {
http.NotFound(w, r)
}
http.ServeFile(w, r, path)
}
func (ios *I2POnionService) StandardHTML() string {
return ""
}
func (ios *I2POnionService) Listen(net, addr string) (net.Listener, error) {
if ios.OnionService != nil {
return ios.OnionService, nil
}
fmt.Println("Starting and registering onion service, please wait a couple of minutes...")
tb, err := tor.Start(context.Background(), nil)
if err != nil {
log.Panicf("Unable to start Tor: %v", err)
}
//defer tb.Close()
//Wait at most a few minutes to publish the service
//listenCtx, listenCancel
listenCtx := context.Background() //context.WithTimeout(context.Background(), 3*time.Minute)
//defer listenCancel()
//Create a v3 onion service to listen on any port but show as 80
ios.OnionService, err = tb.Listen(listenCtx, &tor.ListenConf{Version3: true, RemotePorts: []int{80}})
if err != nil {
log.Panicf("Unable to create onion service: %v", err)
}
return ios.OnionService, nil
}
func (ios *I2POnionService) Serve(l net.Listener) error {
ios.OnionService = l
log.Printf("Serve: %s", ios.OnionService.Addr())
return http.Serve(ios.OnionService, ios)
}
func (ios *I2POnionService) ListenAndServe() error {
var err error
ios.OnionService, err = ios.Listen("", "")
if err != nil {
return err
}
log.Printf("ListenAndServe: %s", ios.OnionService.Addr())
return http.Serve(ios.OnionService, ios)
}
func (ios *I2POnionService) UnpackSite() error {
docroot := ios.ServeDir
fmt.Println("UnpackSite: ", docroot)
if dir, err := os.Stat(docroot); err == nil && dir.IsDir() {
return nil
}
os.MkdirAll(docroot, 0755)
//unpack the contents to the docroot
return fs.WalkDir(content, ".", func(embedpath string, d fs.DirEntry, err error) error {
fp := filepath.Join(docroot)
if err != nil {
log.Fatal(err)
}
fmt.Println("UnpackSite: ", embedpath, fp, embedpath)
if d.IsDir() {
log.Println("UnpackSite: mkdir", filepath.Join(fp, strings.Replace(embedpath, "www", "", -1)))
os.MkdirAll(filepath.Join(fp, strings.Replace(embedpath, "www", "", -1)), 0755)
} else {
log.Println("UnpackSite: copy", filepath.Join(fp, strings.Replace(embedpath, "www", "", -1)))
fullpath := path.Join(embedpath)
bytes, err := content.ReadFile(fullpath)
if err != nil {
return err
}
unpack := filepath.Join(fp, strings.Replace(embedpath, "www", "", -1))
if err := ioutil.WriteFile(unpack, bytes, 0644); err != nil {
return err
}
}
return nil
})
}