604 lines
32 KiB
Go
604 lines
32 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
|
|
_ "net/http/pprof"
|
|
|
|
"github.com/anacrolix/torrent"
|
|
"github.com/asdine/storm"
|
|
Engine "github.com/deranjer/goTorrent/engine"
|
|
Settings "github.com/deranjer/goTorrent/settings"
|
|
Storage "github.com/deranjer/goTorrent/storage"
|
|
jwt "github.com/dgrijalva/jwt-go"
|
|
"github.com/gorilla/handlers"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/mmcdole/gofeed"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
//Logger does logging for the entire project
|
|
Logger = logrus.New()
|
|
//Authenticated stores the value of the result of the client that connects to the server
|
|
Authenticated = false
|
|
APP_ID = os.Getenv("APP_ID")
|
|
)
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
func serveHome(w http.ResponseWriter, r *http.Request) {
|
|
s1, _ := template.ParseFiles("templates/home.tmpl")
|
|
s1.ExecuteTemplate(w, "base", map[string]string{"APP_ID": APP_ID})
|
|
}
|
|
|
|
func handleAuthentication(conn *websocket.Conn, db *storm.DB) {
|
|
msg := Engine.Message{}
|
|
err := conn.ReadJSON(&msg)
|
|
conn.WriteJSON(msg) //TODO just for testing, remove
|
|
payloadData, ok := msg.Payload.(map[string]interface{})
|
|
clientAuthToken, tokenOk := payloadData["ClientAuthString"].(string)
|
|
fmt.Println("ClientAuthToken:", clientAuthToken, "TokenOkay", tokenOk, "PayloadData", payloadData, "PayloadData Okay?", ok)
|
|
if ok == false || tokenOk == false {
|
|
authFail := Engine.AuthResponse{MessageType: "authResponse", Payload: "Message Payload in AuthRequest was malformed, closing connection"}
|
|
conn.WriteJSON(authFail)
|
|
conn.Close()
|
|
return
|
|
}
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err, "SuppliedToken": clientAuthToken}).Error("Unable to read authentication message")
|
|
}
|
|
fmt.Println("Authstring", clientAuthToken)
|
|
signingKeyStruct := Storage.FetchJWTTokens(db)
|
|
singingKey := signingKeyStruct.SigningKey
|
|
token, err := jwt.Parse(clientAuthToken, func(token *jwt.Token) (interface{}, error) {
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
return singingKey, nil
|
|
})
|
|
if err != nil {
|
|
authFail := Engine.AuthResponse{MessageType: "authResponse", Payload: "Parsing of Token failed, ensure you have the correct token! Closing Connection"}
|
|
conn.WriteJSON(authFail)
|
|
Logger.WithFields(logrus.Fields{"error": err, "SuppliedToken": token}).Error("Unable to parse token!")
|
|
conn.Close()
|
|
return
|
|
}
|
|
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
|
authTrue := Engine.AuthResponse{MessageType: "authResponse", Payload: "Authentication Verified, proceed with commands."}
|
|
conn.WriteJSON(authTrue)
|
|
fmt.Println("Claims", claims["ClientName"], claims["Issuer"])
|
|
Authenticated = true
|
|
} else {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Error("Authentication Error occured, cannot complete!")
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
Engine.Logger = Logger //Injecting the logger into all the packages
|
|
Storage.Logger = Logger
|
|
Settings.Logger = Logger
|
|
Config := Settings.FullClientSettingsNew() //grabbing from settings.go
|
|
Engine.Config = Config
|
|
if Config.LoggingOutput == "file" {
|
|
_, err := os.Stat("logs")
|
|
if os.IsNotExist(err) {
|
|
err := os.Mkdir("logs", 0755)
|
|
if err != nil {
|
|
fmt.Println("Unable to create 'log' folder for logging.... please check permissions.. forcing output to stdout", err)
|
|
Logger.Out = os.Stdout
|
|
}
|
|
} else {
|
|
os.Remove("logs/server.log") //cleanup the old log on every restart
|
|
file, err := os.OpenFile("logs/server.log", os.O_CREATE|os.O_WRONLY, 0755) //creating the log file
|
|
//defer file.Close() //TODO.. since we write to this constantly how does close work?
|
|
if err != nil {
|
|
fmt.Println("Unable to create file for logging.... please check permissions.. forcing output to stdout")
|
|
Logger.Out = os.Stdout
|
|
}
|
|
fmt.Println("Logging to file logs/server.log")
|
|
Logger.Out = file
|
|
}
|
|
} else {
|
|
Logger.Out = os.Stdout
|
|
}
|
|
Logger.SetLevel(Config.LoggingLevel)
|
|
|
|
httpAddr := Config.HTTPAddr
|
|
os.MkdirAll(Config.TFileUploadFolder, 0755) //creating a directory to store uploaded torrent files
|
|
os.MkdirAll(Config.TorrentWatchFolder, 0755) //creating a directory to watch for added .torrent files
|
|
//Logger.WithFields(logrus.Fields{"Config": Config}).Info("Torrent Client Config has been generated...")
|
|
|
|
tclient, err := torrent.NewClient(&Config.TorrentConfig) //pulling out the torrent specific config to use
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Fatalf("Error creating torrent client: %s")
|
|
}
|
|
fmt.Printf("%+v\n", Config.TorrentConfig)
|
|
db, err := storm.Open("storage.db") //initializing the boltDB store that contains all the added torrents
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Error opening/creating storage.db")
|
|
}
|
|
defer db.Close() //defering closing the database until the program closes
|
|
|
|
tokens := Storage.IssuedTokensList{} //if first run setting up the authentication tokens
|
|
var signingKey []byte
|
|
err = db.One("ID", 3, &tokens)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"RSSFeedStore": tokens, "error": err}).Info("No Tokens database found, assuming first run, generating token...")
|
|
fmt.Println("Error", err)
|
|
fmt.Println("MAIN TOKEN: %+v\n", tokens)
|
|
tokens.ID = 3 //creating the initial store
|
|
claims := Settings.GoTorrentClaims{
|
|
"goTorrentWebUI",
|
|
jwt.StandardClaims{
|
|
Issuer: "goTorrentServer",
|
|
},
|
|
}
|
|
signingKey = Settings.GenerateSigningKey() //Running this will invalidate any certs you already issued!!
|
|
authString := Settings.GenerateToken(claims, signingKey)
|
|
tokens.SigningKey = signingKey
|
|
tokens.FirstToken = authString
|
|
tokens.TokenNames = append(tokens.TokenNames, Storage.SingleToken{"firstClient"})
|
|
err := ioutil.WriteFile("clientAuth.txt", []byte(authString), 0755)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Warn("Unable to write client auth to file..")
|
|
}
|
|
db.Save(&tokens) //Writing all of that to the database
|
|
} else { //Already have a signing key so pulling that signing key out of the database to sign any key requests
|
|
tokens := Storage.FetchJWTTokens(db)
|
|
signingKey = tokens.SigningKey
|
|
}
|
|
|
|
oldConfig, err := Storage.FetchConfig(db)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Info("Assuming first run as no config found in database, client config being generated")
|
|
Settings.GenerateClientConfigFile(Config, tokens.FirstToken) //if first run generate the client config file
|
|
} else {
|
|
if reflect.DeepEqual(oldConfig.ClientConnectSettings, Config.ClientConnectSettings) {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Info("Configs are the same, not regenerating client config")
|
|
} else {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Info("Config has changed, re-writting config")
|
|
Settings.GenerateClientConfigFile(Config, tokens.FirstToken)
|
|
}
|
|
}
|
|
Storage.SaveConfig(db, Config) //Save the config to the database
|
|
|
|
cronEngine := Engine.InitializeCronEngine() //Starting the cron engine for tasks
|
|
Logger.Debug("Cron Engine Initialized...")
|
|
|
|
torrentLocalStorage := Storage.TorrentLocal{} //creating a new struct that stores all of our local storage info
|
|
var RunningTorrentArray = []Engine.ClientDB{} //this stores ALL of the torrents that are running, used for client update pushes combines Local Storage and Running tclient info
|
|
var PreviousTorrentArray = []Engine.ClientDB{}
|
|
|
|
TorrentLocalArray := Storage.FetchAllStoredTorrents(db) //pulling in all the already added torrents - this is an array of ALL of the local storage torrents, they will be added back in via hash
|
|
|
|
if TorrentLocalArray != nil { //the first creation of the running torrent array //since we are adding all of them in we use a coroutine... just allows the web ui to load then it will load in the torrents
|
|
go Engine.CreateInitialTorrentArray(tclient, TorrentLocalArray, db) //adding all of the stored torrents into the torrent client
|
|
} else {
|
|
Logger.Info("Database is empty, no torrents loaded")
|
|
}
|
|
Engine.CheckTorrentWatchFolder(cronEngine, db, tclient, torrentLocalStorage, Config) //Every 5 minutes the engine will check the specified folder for new .torrent files
|
|
Engine.RefreshRSSCron(cronEngine, db, tclient, torrentLocalStorage, Config) // Refresing the RSS feeds on an hourly basis to add torrents that show up in the RSS feed
|
|
|
|
router := mux.NewRouter() //setting up the handler for the web backend
|
|
router.HandleFunc("/", serveHome) //Serving the main page for our SPA
|
|
router.PathPrefix("/static/").Handler(http.FileServer(http.Dir("public")))
|
|
http.Handle("/", router)
|
|
router.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { //TODO, remove this
|
|
TorrentLocalArray = Storage.FetchAllStoredTorrents(db)
|
|
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well
|
|
var torrentlistArray = new(Engine.TorrentList)
|
|
torrentlistArray.MessageType = "torrentList" //setting the type of message
|
|
torrentlistArray.ClientDBstruct = RunningTorrentArray //the full JSON that includes the number of torrents as the root
|
|
torrentlistArray.Totaltorrents = len(RunningTorrentArray)
|
|
torrentlistArrayJSON, _ := json.Marshal(torrentlistArray)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write(torrentlistArrayJSON)
|
|
})
|
|
|
|
router.HandleFunc("/websocket", func(w http.ResponseWriter, r *http.Request) { //websocket is the main data pipe to the frontend
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
fmt.Println("Websocket connection established, awaiting authentication")
|
|
connResponse := Engine.ServerPushMessage{MessageType: "connectResponse", MessageLevel: "Message", Payload: "Websocket Connection Established, awaiting Authentication"}
|
|
conn.WriteJSON(&connResponse)
|
|
defer conn.Close() //defer closing the websocket until done.
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Unable to create websocket!")
|
|
return
|
|
}
|
|
if Authenticated != true {
|
|
handleAuthentication(conn, db)
|
|
} else { //If we are authenticated inject the connection into the other packages
|
|
connResponse := Engine.ServerPushMessage{MessageType: "authResponse", MessageLevel: "Message", Payload: "Already Authenticated... Awaiting Commands"}
|
|
conn.WriteJSON(&connResponse)
|
|
Logger.Info("Authenticated, websocket connection available!")
|
|
}
|
|
Engine.Conn = conn
|
|
Storage.Conn = conn
|
|
|
|
MessageLoop: //Tagging this so we can continue out of it with any errors we encounter that are failing
|
|
for {
|
|
runningTorrents := tclient.Torrents() //getting running torrents here since multiple cases ask for the running torrents
|
|
msg := Engine.Message{}
|
|
err := conn.ReadJSON(&msg)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err, "message": msg}).Error("Unable to read JSON client message")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Malformed JSON request made to server.. ignoring"}, conn)
|
|
break MessageLoop
|
|
}
|
|
var payloadData map[string]interface{}
|
|
if msg.Payload != nil && msg.Payload != "" {
|
|
payloadData = msg.Payload.(map[string]interface{})
|
|
}
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Message From Client")
|
|
switch msg.MessageType { //first handling data requests
|
|
case "authRequest":
|
|
if Authenticated {
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client already authenticated... skipping authentication method")
|
|
} else {
|
|
handleAuthentication(conn, db)
|
|
}
|
|
|
|
case "newAuthToken":
|
|
claims := Settings.GoTorrentClaims{
|
|
payloadData["ClientName"].(string),
|
|
jwt.StandardClaims{
|
|
Issuer: "goTorrentServer",
|
|
},
|
|
}
|
|
Logger.WithFields(logrus.Fields{"clientName": payloadData["ClientName"].(string)}).Info("New Auth Token creation request")
|
|
fmt.Println("Signing Key", signingKey)
|
|
token := Settings.GenerateToken(claims, signingKey)
|
|
tokenReturn := Settings.TokenReturn{TokenReturn: token}
|
|
tokensDB := Storage.FetchJWTTokens(db)
|
|
tokensDB.TokenNames = append(tokens.TokenNames, Storage.SingleToken{payloadData["ClientName"].(string)})
|
|
db.Update(&tokensDB) //adding the new token client name to the database
|
|
conn.WriteJSON(tokenReturn)
|
|
|
|
case "torrentListRequest":
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested TorrentList Update")
|
|
TorrentLocalArray = Storage.FetchAllStoredTorrents(db) //Required to re-read th database since we write to the DB and this will pull the changes from it
|
|
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well
|
|
PreviousTorrentArray = RunningTorrentArray
|
|
torrentlistArray := Engine.TorrentList{MessageType: "torrentList", ClientDBstruct: RunningTorrentArray, Totaltorrents: len(RunningTorrentArray)}
|
|
Logger.WithFields(logrus.Fields{"torrentList": torrentlistArray, "previousTorrentList": PreviousTorrentArray}).Debug("Previous and Current Torrent Lists for sending to client")
|
|
conn.WriteJSON(torrentlistArray)
|
|
|
|
case "torrentFileListRequest": //client requested a filelist update
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested FileList Update")
|
|
fileListArrayRequest := payloadData["FileListHash"].(string)
|
|
FileListArray := Engine.CreateFileListArray(tclient, fileListArrayRequest, db, Config)
|
|
conn.WriteJSON(FileListArray) //writing the JSON to the client
|
|
|
|
case "torrentPeerListRequest":
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested PeerList Update")
|
|
peerListArrayRequest := payloadData["PeerListHash"].(string)
|
|
torrentPeerList := Engine.CreatePeerListArray(tclient, peerListArrayRequest)
|
|
conn.WriteJSON(torrentPeerList)
|
|
|
|
case "fetchTorrentsByLabel": //TODO test this to make sure it works
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Torrents by Label")
|
|
label := payloadData["Label"].(string)
|
|
torrentsByLabel := Storage.FetchTorrentsByLabel(db, label)
|
|
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db)
|
|
labelRunningArray := []Engine.ClientDB{}
|
|
for _, torrent := range RunningTorrentArray { //Ranging over the running torrents and if the hashes match we have torrents by label
|
|
for _, label := range torrentsByLabel {
|
|
if torrent.TorrentHashString == label.Hash {
|
|
labelRunningArray = append(labelRunningArray, torrent)
|
|
}
|
|
}
|
|
}
|
|
conn.WriteJSON(labelRunningArray)
|
|
|
|
case "changeStorageValue":
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Storage Location Update")
|
|
newStorageLocation := payloadData["StorageValue"].(string)
|
|
hashes := payloadData["ChangeStorageHashes"].([]interface{})
|
|
for _, singleHash := range hashes {
|
|
singleTorrent := Storage.FetchTorrentFromStorage(db, singleHash.(string))
|
|
oldPath := singleTorrent.StoragePath
|
|
newStorageLocationAbs, err := filepath.Abs(filepath.ToSlash(newStorageLocation))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"patherr": err, "path": newStorageLocation}).Warn("Unable to create absolute path for storage location, using default")
|
|
singleTorrent.StoragePath = Config.TorrentConfig.DataDir
|
|
} else {
|
|
singleTorrent.StoragePath = newStorageLocationAbs
|
|
}
|
|
Storage.UpdateStorageTick(db, singleTorrent) //push torrent to storage
|
|
if singleTorrent.TorrentMoved == true { //If torrent has already been moved and I change path then move it again... TODO, does this work with symlinks?
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Info("Change Storage Value called")
|
|
Engine.MoveAndLeaveSymlink(Config, singleHash.(string), db, true, oldPath)
|
|
}
|
|
}
|
|
|
|
case "settingsFileRequest":
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Settings File")
|
|
clientSettingsFile := Engine.SettingsFile{MessageType: "settingsFile", Config: Config}
|
|
conn.WriteJSON(clientSettingsFile)
|
|
|
|
case "rssFeedRequest":
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested RSS Update")
|
|
RSSList := Storage.FetchRSSFeeds(db)
|
|
RSSJSONFeed := Engine.RSSJSONList{MessageType: "rssList", TotalRSSFeeds: len(RSSList.RSSFeeds)}
|
|
RSSsingleFeed := Engine.RSSFeedsNames{}
|
|
for _, singleFeed := range RSSList.RSSFeeds {
|
|
RSSsingleFeed.RSSName = singleFeed.Name
|
|
RSSsingleFeed.RSSFeedURL = singleFeed.URL
|
|
RSSJSONFeed.RSSFeeds = append(RSSJSONFeed.RSSFeeds, RSSsingleFeed)
|
|
}
|
|
conn.WriteJSON(RSSJSONFeed)
|
|
|
|
case "addRSSFeed":
|
|
newRSSFeed := payloadData["RSSURL"].(string)
|
|
Logger.WithFields(logrus.Fields{"message": newRSSFeed}).Debug("Client Added RSS Feed")
|
|
fullRSSFeeds := Storage.FetchRSSFeeds(db)
|
|
Logger.WithFields(logrus.Fields{"RSSFeeds": fullRSSFeeds}).Debug("Pulled Full RSS Feeds")
|
|
for _, singleFeed := range fullRSSFeeds.RSSFeeds {
|
|
if newRSSFeed == singleFeed.URL || newRSSFeed == "" {
|
|
Logger.WithFields(logrus.Fields{"RSSFeed": newRSSFeed}).Warn("Empty URL or Duplicate RSS URL to one already in database! Rejecting submission")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Empty URL or Duplicate RSS URL to one already in database!"}, conn)
|
|
continue MessageLoop
|
|
}
|
|
}
|
|
fp := gofeed.NewParser()
|
|
feed, err := fp.ParseURL(newRSSFeed)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"RSSFeed": newRSSFeed}).Warn("Unable to parse the URL as valid RSS.. cannot add RSS...")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to parse the URL as valid RSS.. cannot add RSS..."}, conn)
|
|
continue MessageLoop
|
|
}
|
|
Logger.WithFields(logrus.Fields{"RSSFeedTitle": feed.Title}).Info("Have feed from URL...")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "success", Payload: "Added RSS Feed"}, conn)
|
|
newRSSFeedFull := Storage.SingleRSSFeed{}
|
|
newRSSFeedFull.Name = feed.Title
|
|
newRSSFeedFull.URL = newRSSFeed
|
|
fullRSSFeeds.RSSFeeds = append(fullRSSFeeds.RSSFeeds, newRSSFeedFull) // add the new RSS feed to the stack
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Adding RSS feed..."}, conn)
|
|
Engine.ForceRSSRefresh(db, fullRSSFeeds)
|
|
//forcing an RSS refresh to fully populate all rss feeds
|
|
|
|
case "deleteRSSFeed":
|
|
deleteRSSFeed := payloadData["RSSURL"].(string)
|
|
Logger.WithFields(logrus.Fields{"message": deleteRSSFeed}).Debug("Deleting RSS Feed")
|
|
Storage.DeleteRSSFeed(db, deleteRSSFeed)
|
|
fullRSSFeeds := Storage.FetchRSSFeeds(db)
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Deleting RSS feed..."}, conn)
|
|
Engine.ForceRSSRefresh(db, fullRSSFeeds)
|
|
|
|
case "rssTorrentsRequest":
|
|
RSSFeedURL := payloadData["RSSURL"].(string)
|
|
Logger.WithFields(logrus.Fields{"RSSFeed": RSSFeedURL}).Info("Requesting torrentList for feed..")
|
|
UpdatedRSSFeed := Engine.RefreshSingleRSSFeed(db, Storage.FetchSpecificRSSFeed(db, RSSFeedURL))
|
|
TorrentRSSList := Engine.SingleRSSFeedMessage{MessageType: "rssTorrentList", URL: RSSFeedURL, Name: UpdatedRSSFeed.Name, TotalTorrents: len(UpdatedRSSFeed.Torrents), Torrents: UpdatedRSSFeed.Torrents}
|
|
Logger.WithFields(logrus.Fields{"TorrentRSSList": TorrentRSSList}).Debug("Returning Torrent list from RSSFeed to client")
|
|
conn.WriteJSON(TorrentRSSList)
|
|
|
|
case "magnetLinkSubmit": //if we detect a magnet link we will be adding a magnet torrent
|
|
storageValue, ok := payloadData["StorageValue"].(string)
|
|
if storageValue == "" || ok == false {
|
|
storageValue, err = filepath.Abs(filepath.ToSlash(Config.DefaultMoveFolder))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"err": err, "MagnetLink": Config.DefaultMoveFolder}).Error("Unable to add default Storage Path")
|
|
}
|
|
} else {
|
|
storageValue, err = filepath.Abs(filepath.ToSlash(storageValue))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"err": err, "MagnetLink": storageValue}).Error("Unable to add Storage Path")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add Storage path..."}, conn)
|
|
storageValue, _ = filepath.Abs(filepath.ToSlash(Config.DefaultMoveFolder))
|
|
}
|
|
}
|
|
labelValue, ok := payloadData["Label"].(string)
|
|
if labelValue == "" || ok == false {
|
|
labelValue = "None"
|
|
}
|
|
magnetLinks := payloadData["MagnetLinks"].([]interface{})
|
|
for _, magnetLink := range magnetLinks {
|
|
clientTorrent, err := tclient.AddMagnet(magnetLink.(string)) //reading the payload into the torrent client
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"err": err, "MagnetLink": magnetLink}).Error("Unable to add magnetlink to client!")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add magnetlink to client!"}, conn)
|
|
continue MessageLoop //continue out of the loop entirely for this message since we hit an error
|
|
}
|
|
Logger.WithFields(logrus.Fields{"clientTorrent": clientTorrent, "magnetLink": magnetLink}).Info("Adding torrent to client!")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received MagnetLink"}, conn)
|
|
go Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, "magnet", "", storageValue, labelValue, Config) //starting the torrent and creating local DB entry
|
|
|
|
}
|
|
|
|
case "torrentFileSubmit":
|
|
base64encoded := payloadData["FileData"].(string)
|
|
fileName := payloadData["FileName"].(string)
|
|
storageValue, ok := payloadData["StorageValue"].(string)
|
|
if storageValue == "" || ok == false {
|
|
storageValue, err = filepath.Abs(filepath.ToSlash(Config.DefaultMoveFolder))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"err": err, "MagnetLink": Config.DefaultMoveFolder}).Error("Unable to add Storage Path")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add default Storage Path"}, conn)
|
|
} else {
|
|
storageValue, err = filepath.Abs(filepath.ToSlash(storageValue))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"err": err, "MagnetLink": storageValue}).Error("Unable to add Storage Path")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add Storage Path"}, conn)
|
|
storageValue, _ = filepath.Abs(filepath.ToSlash(Config.DefaultMoveFolder))
|
|
}
|
|
}
|
|
}
|
|
labelValue, ok := payloadData["Label"].(string)
|
|
if labelValue == "" || ok == false {
|
|
labelValue = "None"
|
|
}
|
|
base64file := strings.Split(base64encoded, ",") //Mozilla and Chrome have different payloads, but both start the file after the comma
|
|
file, err := base64.StdEncoding.DecodeString(base64file[1]) //grabbing the second half of the string after the split
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"Error": err, "file": file}).Info("Unable to decode base64 string to file")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to decode base64 string to file"}, conn)
|
|
}
|
|
filePath := filepath.Join(Config.TFileUploadFolder, fileName)
|
|
filePathAbs, err := filepath.Abs(filePath) //creating a full filepath to store the .torrent files
|
|
|
|
err = ioutil.WriteFile(filePathAbs, file, 0755) //Dumping our received file into the filename
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"filepath": filePathAbs, "file Name": fileName, "Error": err}).Error("Unable to write torrent data to file")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to write torrent data to file"}, conn)
|
|
}
|
|
|
|
clientTorrent, err := tclient.AddTorrentFromFile(filePathAbs)
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"filepath": filePathAbs, "Error": err}).Error("Unable to add Torrent to torrent server")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add Torrent to torrent server"}, conn)
|
|
}
|
|
Logger.WithFields(logrus.Fields{"clienttorrent": clientTorrent.Name(), "filename": filePathAbs}).Info("Added torrent")
|
|
go Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, "file", filePathAbs, storageValue, labelValue, Config)
|
|
|
|
case "stopTorrents":
|
|
torrentHashes := payloadData["TorrentHashes"].([]interface{})
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Stop Request"}, conn)
|
|
for _, singleTorrent := range tclient.Torrents() {
|
|
for _, singleSelection := range torrentHashes {
|
|
if singleTorrent.InfoHash().String() == singleSelection {
|
|
Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for stopping torrents")
|
|
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
|
|
oldTorrentInfo.TorrentStatus = "Stopped"
|
|
oldTorrentInfo.MaxConnections = 0
|
|
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
|
|
}
|
|
}
|
|
}
|
|
|
|
case "deleteTorrents":
|
|
torrentHashes := payloadData["TorrentHashes"].([]interface{})
|
|
withData := payloadData["WithData"].(bool) //Checking if torrents should be deleted with data
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Delete Request"}, conn)
|
|
Logger.WithFields(logrus.Fields{"deleteTorrentsPayload": msg.Payload, "torrentlist": msg.Payload, "deleteWithData?": withData}).Info("message for deleting torrents")
|
|
for _, singleTorrent := range runningTorrents {
|
|
for _, singleSelection := range torrentHashes {
|
|
if singleTorrent.InfoHash().String() == singleSelection {
|
|
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
|
|
Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for deleting torrents")
|
|
if withData {
|
|
oldTorrentInfo.TorrentStatus = "DroppedData" //Will be cleaned up the next engine loop since deleting a torrent mid loop can cause issues
|
|
} else {
|
|
oldTorrentInfo.TorrentStatus = "Dropped"
|
|
}
|
|
Storage.UpdateStorageTick(db, oldTorrentInfo)
|
|
}
|
|
}
|
|
}
|
|
|
|
case "startTorrents":
|
|
torrentHashes := payloadData["TorrentHashes"].([]interface{})
|
|
Logger.WithFields(logrus.Fields{"selection": msg.Payload}).Info("Matched for starting torrents")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Start Request"}, conn)
|
|
for _, singleTorrent := range runningTorrents {
|
|
for _, singleSelection := range torrentHashes {
|
|
if singleTorrent.InfoHash().String() == singleSelection {
|
|
Logger.WithFields(logrus.Fields{"infoHash": singleTorrent.InfoHash().String()}).Debug("Found matching torrent to start")
|
|
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
|
|
oldTorrentInfo.TorrentStatus = "Running"
|
|
oldTorrentInfo.MaxConnections = 80
|
|
Logger.WithFields(logrus.Fields{"Torrent": oldTorrentInfo.TorrentName}).Info("Changing database to torrent running with 80 max connections")
|
|
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
|
|
}
|
|
}
|
|
}
|
|
|
|
case "forceUploadTorrents":
|
|
torrentHashes := payloadData["TorrentHashes"].([]interface{})
|
|
Logger.WithFields(logrus.Fields{"selection": msg.Payload}).Info("Matched for force Uploading Torrents")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Force Start Request"}, conn)
|
|
for _, singleTorrent := range runningTorrents {
|
|
for _, singleSelection := range torrentHashes {
|
|
if singleTorrent.InfoHash().String() == singleSelection {
|
|
Logger.WithFields(logrus.Fields{"infoHash": singleTorrent.InfoHash().String()}).Debug("Found matching torrent to force start")
|
|
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
|
|
oldTorrentInfo.TorrentUploadLimit = false // no upload limit for this torrent
|
|
oldTorrentInfo.TorrentStatus = "Running"
|
|
oldTorrentInfo.MaxConnections = 80
|
|
fmt.Println("OldtorrentinfoName", oldTorrentInfo.TorrentName)
|
|
Logger.WithFields(logrus.Fields{"NewMax": oldTorrentInfo.MaxConnections, "Torrent": oldTorrentInfo.TorrentName}).Info("Setting max connection from zero to 80")
|
|
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
|
|
}
|
|
}
|
|
}
|
|
|
|
case "setFilePriority": //TODO disable if the file is already at 100%?
|
|
priorityRequested := payloadData["FilePriority"].(string)
|
|
torrentHash := payloadData["TorrentHash"].(string)
|
|
fileList := payloadData["FilePaths"].([]interface{})
|
|
Logger.WithFields(logrus.Fields{"selection": torrentHash}).Info("Matched for setting file priority")
|
|
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Set Priority Request"}, conn)
|
|
Logger.WithFields(logrus.Fields{"filelist": fileList}).Debug("Full filelist for setting file priority")
|
|
for _, singleTorrent := range runningTorrents {
|
|
if singleTorrent.InfoHash().String() == torrentHash {
|
|
activeTorrentStruct := Storage.FetchTorrentFromStorage(db, torrentHash) //fetching all the data from the db to update certain fields then write it all back
|
|
Logger.WithFields(logrus.Fields{"singleTorrent": singleTorrent}).Debug("Matched for changing file prio torrents")
|
|
for _, file := range singleTorrent.Files() {
|
|
for _, sentFile := range fileList {
|
|
var priorityString string
|
|
if file.Path() == sentFile {
|
|
switch priorityRequested {
|
|
case "High":
|
|
priorityString = "High"
|
|
file.SetPriority(torrent.PiecePriorityHigh)
|
|
case "Normal":
|
|
priorityString = "Normal"
|
|
file.SetPriority(torrent.PiecePriorityNormal)
|
|
case "Cancel":
|
|
priorityString = "Cancel"
|
|
file.SetPriority(torrent.PiecePriorityNone)
|
|
default:
|
|
priorityString = "Normal"
|
|
file.SetPriority(torrent.PiecePriorityNormal)
|
|
}
|
|
for i, specificFile := range activeTorrentStruct.TorrentFilePriority { //searching for that specific file
|
|
if specificFile.TorrentFilePath == file.DisplayPath() {
|
|
activeTorrentStruct.TorrentFilePriority[i].TorrentFilePriority = priorityString //writing just that field to the current struct
|
|
}
|
|
}
|
|
Logger.WithFields(logrus.Fields{"singleTorrent": file.DisplayPath()}).Debug("Setting priority for ", priorityString)
|
|
Storage.UpdateStorageTick(db, activeTorrentStruct) //re-writting essentially that entire struct right back into the database
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
default:
|
|
//conn.Close()
|
|
Logger.WithFields(logrus.Fields{"message": msg}).Info("Unrecognized Message from client... ignoring")
|
|
return
|
|
}
|
|
}
|
|
|
|
})
|
|
if Config.UseProxy {
|
|
err := http.ListenAndServe(httpAddr, handlers.ProxyHeaders(router))
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Unable to listen on the http Server!")
|
|
}
|
|
} else {
|
|
err := http.ListenAndServe(httpAddr, nil) //Can't send proxy headers if not used since that can be a security issue
|
|
if err != nil {
|
|
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Unable to listen on the http Server with no proxy headers!")
|
|
}
|
|
}
|
|
}
|