diff --git a/server/settings/btsets.go b/server/settings/btsets.go index e31307a..62b8acd 100644 --- a/server/settings/btsets.go +++ b/server/settings/btsets.go @@ -76,6 +76,10 @@ func loadBTSets() { log.TLogln("Error unmarshal btsets", err) } + SetDefault() +} + +func SetDefault() { sets := new(BTSets) sets.EnableDebug = false sets.DisableUTP = true diff --git a/server/torr/apihelper.go b/server/torr/apihelper.go index 333f6a0..d6c4d75 100644 --- a/server/torr/apihelper.go +++ b/server/torr/apihelper.go @@ -73,6 +73,7 @@ func GetTorrent(hashHex string) *Torrent { if tr != nil { tr.Title = tor.Title tr.Poster = tor.Poster + tr.Data = tor.Data tr.Size = tor.Size tr.Timestamp = tor.Timestamp tr.GotInfo() @@ -128,6 +129,15 @@ func SetSettings(set *sets.BTSets) { bts.Connect() } +func SetDefSettings() { + if sets.ReadOnly { + return + } + bts.Disconnect() + sets.SetDefault() + bts.Connect() +} + func Shutdown() { bts.Disconnect() sets.CloseDB() diff --git a/server/torr/stream.go b/server/torr/stream.go index 0766dea..92c5b96 100644 --- a/server/torr/stream.go +++ b/server/torr/stream.go @@ -46,20 +46,6 @@ func (t *Torrent) Stream(fileID int, req *http.Request, resp http.ResponseWriter reader := t.NewReader(file) - //off := int64(0) - //buf := make([]byte, 32*1024) - //for true { - // n, err := reader.Read(buf) - // if err != nil { - // fmt.Println("error read", err) - // break - // } - // off = off + int64(n) - // if off%(200*1024*1024) == 0 { - // time.Sleep(time.Second * 15) - // } - //} - log.Println("Connect client") sets.SetViewed(&sets.Viewed{t.Hash().HexString(), fileID}) diff --git a/server/web/api/settings.go b/server/web/api/settings.go index 2d7c3a8..0269dcc 100644 --- a/server/web/api/settings.go +++ b/server/web/api/settings.go @@ -10,7 +10,7 @@ import ( "server/torr" ) -//Action: get, set +//Action: get, set, def type setsReqJS struct { requestI Sets *sets.BTSets `json:"sets,omitempty"` @@ -27,11 +27,14 @@ func settings(c *gin.Context) { if req.Action == "get" { c.JSON(200, sets.BTsets) return - } - if req.Action == "set" { + } else if req.Action == "set" { torr.SetSettings(req.Sets) c.Status(200) return + } else if req.Action == "def" { + torr.SetDefSettings() + c.Status(200) + return } c.AbortWithError(http.StatusBadRequest, errors.New("action is empty")) } diff --git a/server/web/api/stream.go b/server/web/api/stream.go index 1f56fd0..99de164 100644 --- a/server/web/api/stream.go +++ b/server/web/api/stream.go @@ -36,6 +36,17 @@ func stream(c *gin.Context) { _, play := c.GetQuery("play") title := c.Query("title") poster := c.Query("poster") + notAuth := c.GetBool("not_auth") + + if notAuth && play { + streamNoAuth(c) + return + } + if notAuth { + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } if link == "" { c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty")) @@ -111,3 +122,65 @@ func stream(c *gin.Context) { return } } + +func streamNoAuth(c *gin.Context) { + link := c.Query("link") + indexStr := c.Query("index") + _, preload := c.GetQuery("preload") + title := c.Query("title") + poster := c.Query("poster") + + if link == "" { + c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty")) + return + } + + if title == "" { + title = c.Param("fname") + title, _ = url.PathUnescape(title) + title = strings.TrimLeft(title, "/") + } else { + title, _ = url.QueryUnescape(title) + } + + link, _ = url.QueryUnescape(link) + poster, _ = url.QueryUnescape(poster) + + spec, err := utils.ParseLink(link) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + tor := torr.GetTorrent(spec.InfoHash.HexString()) + if tor == nil { + c.AbortWithError(http.StatusNotFound, errors.New("Torrent not found")) + return + } + + if !tor.GotInfo() { + c.AbortWithError(http.StatusInternalServerError, errors.New("timeout connection torrent")) + return + } + + // find file + index := -1 + if len(tor.Files()) == 1 { + index = 1 + } else { + ind, err := strconv.Atoi(indexStr) + if err == nil { + index = ind + } + } + if index == -1 { // if file index not set and play file exec + c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is empty or wrong")) + return + } + // preload torrent + if preload { + torr.Preload(tor, index) + } + + tor.Stream(index, c.Request, c.Writer) +} diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index 9a2b588..4aae796 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -1,9 +1,14 @@ package auth import ( + "encoding/base64" "encoding/json" "io/ioutil" + "net/http" "path/filepath" + "reflect" + "strings" + "unsafe" "github.com/gin-gonic/gin" @@ -19,7 +24,7 @@ func SetupAuth(engine *gin.Engine) *gin.RouterGroup { if accs == nil { return nil } - return engine.Group("/", gin.BasicAuth(accs)) + return engine.Group("/", BasicAuth(accs)) } func getAccounts() gin.Accounts { @@ -34,3 +39,62 @@ func getAccounts() gin.Accounts { } return accs } + +type authPair struct { + value string + user string +} +type authPairs []authPair + +func (a authPairs) searchCredential(authValue string) (string, bool) { + if authValue == "" { + return "", false + } + for _, pair := range a { + if pair.value == authValue { + return pair.user, true + } + } + return "", false +} + +func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { + pairs := processAccounts(accounts) + return func(c *gin.Context) { + user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) + if !found { + if strings.HasPrefix(c.FullPath(), "/stream") { + c.Set("not_auth", true) + return + } + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + c.Set(gin.AuthUserKey, user) + } +} + +func processAccounts(accounts gin.Accounts) authPairs { + pairs := make(authPairs, 0, len(accounts)) + for user, password := range accounts { + value := authorizationHeader(user, password) + pairs = append(pairs, authPair{ + value: value, + user: user, + }) + } + return pairs +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base)) +} + +func StringToBytes(s string) (b []byte) { + sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len + return b +}