summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJames Mills <prologic@shortcircuit.net.au>2020-03-21 09:55:06 +1000
committerJames Mills <prologic@shortcircuit.net.au>2020-03-21 09:55:06 +1000
commit4dae45a68ff1ff32a251a5835b2a2723ae125d1c (patch)
treeb3abe641f39f50ac444aee2bb617d7b4ab44187d /pkg
parent76fa613b7eaf1c8626d2a51901c623af7bf1a665 (diff)
Restructured source code layout, Added rice support for templates and static assets
Diffstat (limited to 'pkg')
-rw-r--r--pkg/app/app.go222
-rw-r--r--pkg/app/config.go89
-rw-r--r--pkg/app/feed.go77
-rw-r--r--pkg/app/listener.go37
-rw-r--r--pkg/app/tor.go45
-rw-r--r--pkg/app/watcher.go63
-rw-r--r--pkg/media/library.go122
-rw-r--r--pkg/media/media.go2
-rw-r--r--pkg/media/path.go7
-rw-r--r--pkg/media/playlist.go19
-rw-r--r--pkg/media/video.go77
-rw-r--r--pkg/onionkey/key.go24
-rw-r--r--pkg/onionkey/v3.go78
13 files changed, 0 insertions, 862 deletions
diff --git a/pkg/app/app.go b/pkg/app/app.go
deleted file mode 100644
index 0efd22a..0000000
--- a/pkg/app/app.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Package app manages main application server.
-package app
-
-import (
- "errors"
- "fmt"
- "html/template"
- "log"
- "net"
- "net/http"
- "path"
-
- "github.com/fsnotify/fsnotify"
- "github.com/gorilla/mux"
- "github.com/wybiral/tube/pkg/media"
- "github.com/wybiral/tube/pkg/onionkey"
-)
-
-// App represents main application.
-type App struct {
- Config *Config
- Library *media.Library
- Watcher *fsnotify.Watcher
- Templates *template.Template
- Feed []byte
- Tor *tor
- Listener net.Listener
- Router *mux.Router
-}
-
-// NewApp returns a new instance of App from Config.
-func NewApp(cfg *Config) (*App, error) {
- if cfg == nil {
- cfg = DefaultConfig()
- }
- a := &App{
- Config: cfg,
- }
- // Setup Library
- a.Library = media.NewLibrary()
- // Setup Watcher
- w, err := fsnotify.NewWatcher()
- if err != nil {
- return nil, err
- }
- a.Watcher = w
- // Setup Listener
- ln, err := newListener(cfg.Server)
- if err != nil {
- return nil, err
- }
- a.Listener = ln
- // Setup Templates
- a.Templates = template.Must(template.ParseGlob("templates/*"))
- // Setup Tor
- if cfg.Tor.Enable {
- t, err := newTor(cfg.Tor)
- if err != nil {
- return nil, err
- }
- a.Tor = t
- }
- // Setup Router
- r := mux.NewRouter().StrictSlash(true)
- r.HandleFunc("/", a.indexHandler).Methods("GET")
- r.HandleFunc("/v/{id}.mp4", a.videoHandler).Methods("GET")
- r.HandleFunc("/v/{prefix}/{id}.mp4", a.videoHandler).Methods("GET")
- r.HandleFunc("/t/{id}", a.thumbHandler).Methods("GET")
- r.HandleFunc("/t/{prefix}/{id}", a.thumbHandler).Methods("GET")
- r.HandleFunc("/v/{id}", a.pageHandler).Methods("GET")
- r.HandleFunc("/v/{prefix}/{id}", a.pageHandler).Methods("GET")
- r.HandleFunc("/feed.xml", a.rssHandler).Methods("GET")
- // Static file handler
- fsHandler := http.StripPrefix(
- "/static/",
- http.FileServer(http.Dir("./static/")),
- )
- r.PathPrefix("/static/").Handler(fsHandler).Methods("GET")
- a.Router = r
- return a, nil
-}
-
-// Run imports the library and starts server.
-func (a *App) Run() error {
- if a.Tor != nil {
- var err error
- cs := a.Config.Server
- key := a.Tor.OnionKey
- if key == nil {
- key, err = onionkey.GenerateKey()
- if err != nil {
- return err
- }
- a.Tor.OnionKey = key
- }
- onion, err := key.Onion()
- if err != nil {
- return err
- }
- onion.Ports[80] = fmt.Sprintf("%s:%d", cs.Host, cs.Port)
- err = a.Tor.Controller.AddOnion(onion)
- if err != nil {
- return errors.New("unable to start Tor onion service")
- }
- log.Printf("Onion service: http://%s.onion", onion.ServiceID)
- }
- for _, pc := range a.Config.Library {
- p := &media.Path{
- Path: pc.Path,
- Prefix: pc.Prefix,
- }
- err := a.Library.AddPath(p)
- if err != nil {
- return err
- }
- err = a.Library.Import(p)
- if err != nil {
- return err
- }
- a.Watcher.Add(p.Path)
- }
- buildFeed(a)
- go startWatcher(a)
- return http.Serve(a.Listener, a.Router)
-}
-
-// HTTP handler for /
-func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("/")
- pl := a.Library.Playlist()
- if len(pl) > 0 {
- http.Redirect(w, r, "/v/"+pl[0].ID, 302)
- } else {
- a.Templates.ExecuteTemplate(w, "index.html", &struct {
- Playing *media.Video
- Playlist media.Playlist
- }{
- Playing: &media.Video{ID: ""},
- Playlist: a.Library.Playlist(),
- })
- }
-}
-
-// HTTP handler for /v/id
-func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- id := vars["id"]
- prefix, ok := vars["prefix"]
- if ok {
- id = path.Join(prefix, id)
- }
- log.Printf("/v/%s", id)
- playing, ok := a.Library.Videos[id]
- if !ok {
- a.Templates.ExecuteTemplate(w, "index.html", &struct {
- Playing *media.Video
- Playlist media.Playlist
- }{
- Playing: &media.Video{ID: ""},
- Playlist: a.Library.Playlist(),
- })
- return
- }
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- a.Templates.ExecuteTemplate(w, "index.html", &struct {
- Playing *media.Video
- Playlist media.Playlist
- }{
- Playing: playing,
- Playlist: a.Library.Playlist(),
- })
-}
-
-// HTTP handler for /v/id.mp4
-func (a *App) videoHandler(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- id := vars["id"]
- prefix, ok := vars["prefix"]
- if ok {
- id = path.Join(prefix, id)
- }
- log.Printf("/v/%s", id)
- m, ok := a.Library.Videos[id]
- if !ok {
- return
- }
- title := m.Title
- disposition := "attachment; filename=\"" + title + ".mp4\""
- w.Header().Set("Content-Disposition", disposition)
- w.Header().Set("Content-Type", "video/mp4")
- http.ServeFile(w, r, m.Path)
-}
-
-// HTTP handler for /t/id
-func (a *App) thumbHandler(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- id := vars["id"]
- prefix, ok := vars["prefix"]
- if ok {
- id = path.Join(prefix, id)
- }
- log.Printf("/t/%s", id)
- m, ok := a.Library.Videos[id]
- if !ok {
- return
- }
- w.Header().Set("Cache-Control", "public, max-age=7776000")
- if m.ThumbType == "" {
- w.Header().Set("Content-Type", "image/jpeg")
- http.ServeFile(w, r, "static/defaulticon.jpg")
- } else {
- w.Header().Set("Content-Type", m.ThumbType)
- w.Write(m.Thumb)
- }
-}
-
-// HTTP handler for /feed.xml
-func (a *App) rssHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Cache-Control", "public, max-age=7776000")
- w.Header().Set("Content-Type", "text/xml")
- w.Write(a.Feed)
-}
diff --git a/pkg/app/config.go b/pkg/app/config.go
deleted file mode 100644
index ec27435..0000000
--- a/pkg/app/config.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package app
-
-import (
- "encoding/json"
- "os"
-)
-
-// Config settings for main App.
-type Config struct {
- Library []*PathConfig `json:"library"`
- Server *ServerConfig `json:"server"`
- Feed *FeedConfig `json:"feed"`
- Tor *TorConfig `json:"tor,omitempty"`
-}
-
-// PathConfig settings for media library path.
-type PathConfig struct {
- Path string `json:"path"`
- Prefix string `json:"prefix"`
-}
-
-// ServerConfig settings for App Server.
-type ServerConfig struct {
- Host string `json:"host"`
- Port int `json:"port"`
-}
-
-// FeedConfig settings for App Feed.
-type FeedConfig struct {
- ExternalURL string `json:"external_url"`
- Title string `json:"title"`
- Link string `json:"link"`
- Description string `json:"description"`
- Author struct {
- Name string `json:"name"`
- Email string `json:"email"`
- } `json:"author"`
- Copyright string `json:"copyright"`
-}
-
-// TorConfig stores tor configuration.
-type TorConfig struct {
- Enable bool `json:"enable"`
- Controller *TorControllerConfig `json:"controller"`
-}
-
-// TorControllerConfig stores tor controller configuration.
-type TorControllerConfig struct {
- Host string `json:"host"`
- Port int `json:"port"`
- Password string `json:"password,omitempty"`
-}
-
-// DefaultConfig returns Config initialized with default values.
-func DefaultConfig() *Config {
- return &Config{
- Library: []*PathConfig{
- &PathConfig{
- Path: "videos",
- Prefix: "",
- },
- },
- Server: &ServerConfig{
- Host: "127.0.0.1",
- Port: 0,
- },
- Feed: &FeedConfig{
- ExternalURL: "http://localhost",
- },
- Tor: &TorConfig{
- Enable: false,
- Controller: &TorControllerConfig{
- Host: "127.0.0.1",
- Port: 9051,
- },
- },
- }
-}
-
-// ReadFile reads a JSON file into Config.
-func (c *Config) ReadFile(path string) error {
- f, err := os.Open(path)
- if err != nil {
- return err
- }
- defer f.Close()
- d := json.NewDecoder(f)
- return d.Decode(c)
-}
diff --git a/pkg/app/feed.go b/pkg/app/feed.go
deleted file mode 100644
index e4c1ce2..0000000
--- a/pkg/app/feed.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package app
-
-import (
- "fmt"
- "net/url"
- "os"
- "path"
- "strconv"
- "time"
-
- "github.com/wybiral/feeds"
-)
-
-// buildFeed creates RSS feed attribute for App based on Library contents.
-func buildFeed(a *App) {
- cfg := a.Config.Feed
- now := time.Now()
- f := &feeds.Feed{
- Title: cfg.Title,
- Link: &feeds.Link{Href: cfg.Link},
- Description: cfg.Description,
- Author: &feeds.Author{
- Name: cfg.Author.Name,
- Email: cfg.Author.Email,
- },
- Created: now,
- Copyright: cfg.Copyright,
- }
- var externalURL string
- if len(cfg.ExternalURL) > 0 {
- externalURL = cfg.ExternalURL
- } else if a.Tor != nil {
- onion, err := a.Tor.OnionKey.Onion()
- if err != nil {
- return
- }
- externalURL = fmt.Sprintf("http://%s.onion", onion.ServiceID)
- } else {
- hostname, err := os.Hostname()
- if err != nil {
- host := a.Config.Server.Host
- port := a.Config.Server.Port
- externalURL = fmt.Sprintf("http://%s:%d", host, port)
- } else {
- externalURL = fmt.Sprintf("http://%s", hostname)
- }
- }
- for _, v := range a.Library.Playlist() {
- u, err := url.Parse(externalURL)
- if err != nil {
- return
- }
- u.Path = path.Join(u.Path, "v", v.ID)
- id := u.String()
- f.Items = append(f.Items, &feeds.Item{
- Id: id,
- Title: v.Title,
- Link: &feeds.Link{Href: id},
- Description: v.Description,
- Enclosure: &feeds.Enclosure{
- Url: id + ".mp4",
- Length: strconv.FormatInt(v.Size, 10),
- Type: "video/mp4",
- },
- Author: &feeds.Author{
- Name: cfg.Author.Name,
- Email: cfg.Author.Email,
- },
- Created: v.Timestamp,
- })
- }
- feed, err := f.ToRss()
- if err != nil {
- return
- }
- a.Feed = []byte(feed)
-}
diff --git a/pkg/app/listener.go b/pkg/app/listener.go
deleted file mode 100644
index a4236d8..0000000
--- a/pkg/app/listener.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Instead of using the default new.Listener this file will construct a custom
-// one. The main purpose for this is to have more control over the settings
-// (like keep-alive) and to retrieve the assigned port when using port 0.
-
-package app
-
-import (
- "fmt"
- "net"
- "time"
-)
-
-func newListener(cfg *ServerConfig) (net.Listener, error) {
- addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
- ln, err := net.Listen("tcp", addr)
- if err != nil {
- return nil, err
- }
- // set actual port on config object (in case original port was 0)
- cfg.Port = ln.Addr().(*net.TCPAddr).Port
- return tcpListener{ln.(*net.TCPListener)}, nil
-}
-
-// custom TCP listener with keep-alive timeout
-type tcpListener struct {
- *net.TCPListener
-}
-
-func (ln tcpListener) Accept() (net.Conn, error) {
- tc, err := ln.AcceptTCP()
- if err != nil {
- return nil, err
- }
- tc.SetKeepAlive(true)
- tc.SetKeepAlivePeriod(3 * time.Minute)
- return tc, nil
-}
diff --git a/pkg/app/tor.go b/pkg/app/tor.go
deleted file mode 100644
index d06751f..0000000
--- a/pkg/app/tor.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package app
-
-import (
- "errors"
- "fmt"
- "os"
-
- "github.com/wybiral/torgo"
- "github.com/wybiral/tube/pkg/onionkey"
-)
-
-type tor struct {
- OnionKey onionkey.Key
- Controller *torgo.Controller
-}
-
-func newTor(ct *TorConfig) (*tor, error) {
- addr := fmt.Sprintf("%s:%d", ct.Controller.Host, ct.Controller.Port)
- ctrl, err := torgo.NewController(addr)
- if err != nil {
- return nil, errors.New("unable to connect to Tor controller")
- }
- if len(ct.Controller.Password) > 0 {
- err = ctrl.AuthenticatePassword(ct.Controller.Password)
- } else {
- err = ctrl.AuthenticateCookie()
- if err != nil {
- err = ctrl.AuthenticateNone()
- }
- }
- if err != nil {
- return nil, errors.New("unable to authenticate to Tor controller")
- }
- key, err := onionkey.ReadFile("onion.key")
- if os.IsNotExist(err) {
- key = nil
- } else if err != nil {
- return nil, err
- }
- t := &tor{
- Controller: ctrl,
- OnionKey: key,
- }
- return t, nil
-}
diff --git a/pkg/app/watcher.go b/pkg/app/watcher.go
deleted file mode 100644
index 74db715..0000000
--- a/pkg/app/watcher.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package app
-
-import (
- "time"
-
- fs "github.com/fsnotify/fsnotify"
-)
-
-// This is the amount of time to wait after changes before reacting to them.
-// Debounce is done because moving files into the watched directories causes
-// many rapid "Write" events to fire which would cause excessive Remove/Add
-// method calls on the Library. To avoid this we accumulate the changes and
-// only perform them once the events have stopped for this amount of time.
-const debounceTimeout = time.Second * 5
-
-// create, write, and chmod all require an add event
-const addFlags = fs.Create | fs.Write | fs.Chmod
-
-// remove, rename, write, and chmod all require a remove event
-const removeFlags = fs.Remove | fs.Rename | fs.Write | fs.Chmod
-
-// watch library paths and update Library with changes.
-func startWatcher(a *App) {
- timer := time.NewTimer(debounceTimeout)
- addEvents := make(map[string]struct{})
- removeEvents := make(map[string]struct{})
- for {
- select {
- case e := <-a.Watcher.Events:
- if e.Op&removeFlags != 0 {
- removeEvents[e.Name] = struct{}{}
- }
- if e.Op&addFlags != 0 {
- addEvents[e.Name] = struct{}{}
- }
- // reset timer
- timer.Reset(debounceTimeout)
- case <-timer.C:
- eventCount := len(removeEvents) + len(addEvents)
- // handle remove events first
- if len(removeEvents) > 0 {
- for p := range removeEvents {
- a.Library.Remove(p)
- }
- // clear map
- removeEvents = make(map[string]struct{})
- }
- // then handle add events
- if len(addEvents) > 0 {
- for p := range addEvents {
- a.Library.Add(p)
- }
- // clear map
- addEvents = make(map[string]struct{})
- }
- if eventCount > 0 {
- buildFeed(a)
- }
- // reset timer
- timer.Reset(debounceTimeout)
- }
- }
-}
diff --git a/pkg/media/library.go b/pkg/media/library.go
deleted file mode 100644
index 87fb223..0000000
--- a/pkg/media/library.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package media
-
-import (
- "errors"
- "io/ioutil"
- "log"
- "path"
- "path/filepath"
- "sort"
- "strings"
- "sync"
-)
-
-// Library manages importing and retrieving video data.
-type Library struct {
- mu sync.RWMutex
- Paths map[string]*Path
- Videos map[string]*Video
-}
-
-// NewLibrary returns new instance of Library.
-func NewLibrary() *Library {
- lib := &Library{
- Paths: make(map[string]*Path),
- Videos: make(map[string]*Video),
- }
- return lib
-}
-
-// AddPath adds a media path to the library.
-func (lib *Library) AddPath(p *Path) error {
- lib.mu.Lock()
- defer lib.mu.Unlock()
- // make sure new path doesn't collide with existing ones
- for _, p2 := range lib.Paths {
- if p.Path == p2.Path {
- return errors.New("media: duplicate library path")
- }
- if p.Prefix == p2.Prefix {
- return errors.New("media: duplicate library prefix")
- }
- }
- lib.Paths[p.Path] = p
- return nil
-}
-
-// Import adds all valid videos from a given path.
-func (lib *Library) Import(p *Path) error {
- files, err := ioutil.ReadDir(p.Path)
- if err != nil {
- return err
- }
- for _, info := range files {
- err = lib.Add(path.Join(p.Path, info.Name()))
- if err != nil {
- // Ignore files that can't be parsed
- continue
- }
- }
- return nil
-}
-
-// Add adds a single video from a given file path.
-func (lib *Library) Add(fp string) error {
- lib.mu.Lock()
- defer lib.mu.Unlock()
- fp = filepath.ToSlash(fp)
- d := path.Dir(fp)
- p, ok := lib.Paths[d]
- if !ok {
- return errors.New("media: path not found")
- }
- n := path.Base(fp)
- v, err := ParseVideo(p, n)
- if err != nil {
- return err
- }
- lib.Videos[v.ID] = v
- log.Println("Added:", v.Path)
- return nil
-}
-
-// Remove removes a single video from a given file path.
-func (lib *Library) Remove(fp string) {
- lib.mu.Lock()
- defer lib.mu.Unlock()
- fp = filepath.ToSlash(fp)
- d := path.Dir(fp)
- p, ok := lib.Paths[d]
- if !ok {
- return
- }
- n := path.Base(fp)
- // ID is name without extension
- idx := strings.LastIndex(n, ".")
- if idx == -1 {
- idx = len(n)
- }
- id := n[:idx]
- if len(p.Prefix) > 0 {
- id = path.Join(p.Prefix, id)
- }
- v, ok := lib.Videos[id]
- if ok {
- delete(lib.Videos, id)
- log.Println("Removed:", v.Path)
- }
-}
-
-// Playlist returns a sorted Playlist of all videos.
-func (lib *Library) Playlist() Playlist {
- lib.mu.RLock()
- defer lib.mu.RUnlock()
- pl := make(Playlist, len(lib.Videos))
- i := 0
- for _, v := range lib.Videos {
- pl[i] = v
- i++
- }
- sort.Sort(pl)
- return pl
-}
diff --git a/pkg/media/media.go b/pkg/media/media.go
deleted file mode 100644
index 6fa6725..0000000
--- a/pkg/media/media.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package media manages video library functionality.
-package media
diff --git a/pkg/media/path.go b/pkg/media/path.go
deleted file mode 100644
index 1248ed5..0000000
--- a/pkg/media/path.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package media
-
-// Path represents a media library path.
-type Path struct {
- Path string
- Prefix string
-}
diff --git a/pkg/media/playlist.go b/pkg/media/playlist.go
deleted file mode 100644
index cf8edcf..0000000
--- a/pkg/media/playlist.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package media
-
-// Playlist holds an array of videos capable of sorting by Timestamp.
-type Playlist []*Video
-
-// Len returns length of array (for sorting).
-func (p Playlist) Len() int {
- return len(p)
-}
-
-// Swap swaps two values in array by index (for sorting).
-func (p Playlist) Swap(i, j int) {
- p[i], p[j] = p[j], p[i]
-}
-
-// Less returns true if p[i] Timestamp is after p[j] (for sorting).
-func (p Playlist) Less(i, j int) bool {
- return p[i].Timestamp.After(p[j].Timestamp)
-}
diff --git a/pkg/media/video.go b/pkg/media/video.go
deleted file mode 100644
index 9340e86..0000000
--- a/pkg/media/video.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package media
-
-import (
- "os"
- "path"
- "strings"
- "time"
-
- "github.com/dhowden/tag"
-)
-
-// Video represents metadata for a single video.
-type Video struct {
- ID string
- Title string
- Album string
- Description string
- Thumb []byte
- ThumbType string
- Modified string
- Size int64
- Path string
- Timestamp time.Time
-}
-
-// ParseVideo parses a video file's metadata and returns a Video.
-func ParseVideo(p *Path, name string) (*Video, error) {
- pth := path.Join(p.Path, name)
- f, err := os.Open(pth)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- info, err := f.Stat()
- if err != nil {
- return nil, err
- }
- size := info.Size()
- timestamp := info.ModTime()
- modified := timestamp.Format("2006-01-02 03:04 PM")
- // ID is name without extension
- idx := strings.LastIndex(name, ".")
- if idx == -1 {
- idx = len(name)
- }
- id := name[:idx]
- if len(p.Prefix) > 0 {
- // if there's a prefix prepend it to the ID
- id = path.Join(p.Prefix, name[:idx])
- }
- m, err := tag.ReadFrom(f)
- if err != nil {
- return nil, err
- }
- title := m.Title()
- // Default title is filename
- if title == "" {
- title = name
- }
- v := &Video{
- ID: id,
- Title: title,
- Album: m.Album(),
- Description: m.Comment(),
- Modified: modified,
- Size: size,
- Path: pth,
- Timestamp: timestamp,
- }
- // Add thumbnail (if exists)
- pic := m.Picture()
- if pic != nil {
- v.Thumb = pic.Data
- v.ThumbType = pic.MIMEType
- }
- return v, nil
-}
diff --git a/pkg/onionkey/key.go b/pkg/onionkey/key.go
deleted file mode 100644
index b91d43a..0000000
--- a/pkg/onionkey/key.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Package onionkey manages onion service key generation, serialization, and
-// service ID calculation. Currently only supports version 3 onions.
-package onionkey
-
-import (
- "github.com/wybiral/torgo"
-)
-
-// Key is generic interface type for Tor onion keys.
-type Key interface {
- WriteFile(path string) error
- Onion() (*torgo.Onion, error)
- ServiceID() string
-}
-
-// GenerateKey generates a Tor onion key.
-func GenerateKey() (Key, error) {
- return generateV3()
-}
-
-// ReadFile reads a Tor onion key from file path.
-func ReadFile(path string) (Key, error) {
- return readV3(path)
-}
diff --git a/pkg/onionkey/v3.go b/pkg/onionkey/v3.go
deleted file mode 100644
index cbd6c73..0000000
--- a/pkg/onionkey/v3.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Implements Tor v3 onion key based on ed25519.
-
-package onionkey
-
-import (
- "crypto/rand"
- "encoding/base32"
- "encoding/base64"
- "errors"
- "io/ioutil"
- "os"
- "strings"
-
- "github.com/wybiral/torgo"
- "golang.org/x/crypto/ed25519"
- "golang.org/x/crypto/sha3"
-)
-
-type v3Key ed25519.PrivateKey
-
-func generateV3() (v3Key, error) {
- _, key, err := ed25519.GenerateKey(rand.Reader)
- return v3Key(key), err
-}
-
-func readV3(path string) (v3Key, error) {
- raw, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, err
- }
- pk := strings.TrimSpace(string(raw))
- parts := strings.SplitN(pk, ":", 2)
- if parts[0] != "v3" {
- return nil, errors.New("Invalid key type")
- }
- seed, err := base64.StdEncoding.DecodeString(parts[1])
- if err != nil {
- return nil, err
- }
- key := ed25519.NewKeyFromSeed(seed)
- return v3Key(key), nil
-}
-
-func (k v3Key) Onion() (*torgo.Onion, error) {
- return torgo.OnionFromEd25519(ed25519.PrivateKey(k))
-}
-
-func (k v3Key) WriteFile(path string) error {
- seed := ed25519.PrivateKey(k).Seed()
- b64 := base64.StdEncoding.EncodeToString(seed)
- f, err := os.Create(path)
- if err != nil {
- return err
- }
- defer f.Close()
- _, err = f.WriteString("v3:" + b64)
- if err != nil {
- return err
- }
- return nil
-}
-
-func (k v3Key) ServiceID() string {
- // Get ed25519 public key
- pub := ed25519.PrivateKey(k).Public().(ed25519.PublicKey)
- // Calculate check digits
- checkstr := []byte(".onion checksum")
- checkstr = append(checkstr, pub...)
- checkstr = append(checkstr, 0x03)
- checksum := sha3.Sum256(checkstr)
- checkdigits := checksum[:2]
- // Calculate service ID
- combined := pub[:]
- combined = append(combined, checkdigits...)
- combined = append(combined, 0x03)
- serviceID := base32.StdEncoding.EncodeToString(combined)
- return strings.ToLower(serviceID)
-}