diff --git a/config.1.toml b/config.1.toml new file mode 100644 index 00000000..1153c9dd --- /dev/null +++ b/config.1.toml @@ -0,0 +1,122 @@ +[serverConfig] + + ServerPort = ":8000" #leave format as is it expects a string with colon + ServerAddr = "" #blank will bind to default IP address, usually fine to leave be + LogLevel = "Info" # Options = Debug, Info, Warn, Error, Fatal, Panic + LogOutput = "stdout" #Options = file, stdout #file will print it to logs/server.log + + SeedRatioStop = 1.50 #automatically stops the torrent after it reaches this seeding ratio + + #Relative or absolute path accepted, the server will convert any relative path to an absolute path. + DefaultMoveFolder = 'downloaded' #default path that a finished torrent is symlinked to after completion. Torrents added via RSS will default here + TorrentWatchFolder = 'torrentUpload' #folder path that is watched for .torrent files and adds them automatically every 5 minutes + + #Limits your upload and download speed globally, all are averages and not burst protected (usually burst on start). + #Low = ~.05MB/s, Medium = ~.5MB/s, High = ~1.5MB/s + UploadRateLimit = "Unlimited" #Options are "Low", "Medium", "High", "Unlimited" #Unlimited is default + DownloadRateLimit = "Unlimited" + +[goTorrentWebUI] + #Basic goTorrentWebUI authentication (not terribly secure, implemented in JS, password is hashed to SHA256, not salted, basically don't depend on this if you require very good security) + WebUIAuth = true # bool, if false no authentication is required for the webUI + WebUIUser = "admin" + WebUIPassword = "Password1" + + +[notifications] + + PushBulletToken = "" #add your pushbullet api token here to notify of torrent completion to pushbullet + +[reverseProxy] + #This is for setting up goTorrent behind a reverse Proxy (with SSL, reverse proxy with no SSL will require editing the WSS connection to a WS connection manually) + ProxyEnabled = false #bool, either false or true + #URL is CASE SENSITIVE + BaseURL = "yoursubdomain.domain.org/subroute/" # MUST be in the format (if you have a subdomain, and must have trailing slash) "yoursubdomain.domain.org/subroute/" + +[EncryptionPolicy] + + DisableEncryption = false + ForceEncryption = false + PreferNoEncryption = true + +[torrentClientConfig] + DownloadDir = 'downloading' #the full OR relative path where the torrent server stores in-progress torrents + + Seed = true #boolean #seed after download + + # Never send chunks to peers. + NoUpload = false #boolean + + #User-provided Client peer ID. If not present, one is generated automatically. + PeerID = "" #string + + #The address to listen for new uTP and TCP bittorrent protocol connections. DHT shares a UDP socket with uTP unless configured otherwise. + ListenAddr = "" #Leave Blank for default, syntax "HOST:PORT" + + #Don't announce to trackers. This only leaves DHT to discover peers. + DisableTrackers = false #boolean + + DisablePEX = false # boolean + + # Don't create a DHT. + NoDHT = false #boolean + + #For the bittorrent protocol. + DisableUTP = false #bool + + #For the bittorrent protocol. + DisableTCP = false #bool + + #Called to instantiate storage for each added torrent. Builtin backends + # are in the storage package. If not set, the "file" implementation is used. + DefaultStorage = "storage.ClientImpl" + + #encryption policy + IPBlocklist = "" #of type iplist.Ranger + + DisableIPv6 = false #boolean + + Debug = false #boolean + + #HTTP *http.Client + + HTTPUserAgent = "" # HTTPUserAgent changes default UserAgent for HTTP requests + + ExtendedHandshakeClientVersion = "" + + Bep20 = "" + + # Overrides the default DHT configuration, see dhtServerConfig #advanced.. so be careful + DHTConfig = "" # default is "dht.ServerConfig" + +[dhtServerConfig] + # Set NodeId Manually. Caller must ensure that if NodeId does not conform to DHT Security Extensions, that NoSecurity is also set. + NodeId = "" #[20]byte + + Conn = "" # https:#godoc.org/net#PacketConn #not implemented + + # Don't respond to queries from other nodes. + Passive = false # boolean + + # the default addresses are "router.utorrent.com:6881","router.bittorrent.com:6881","dht.transmissionbt.com:6881","dht.aelitis.com:6881", + #https:#github.com/anacrolix/dht/blob/master/dht.go + StartingNodes = "dht.GlobalBootstrapAddrs" + + #Disable the DHT security extension: http:#www.libtorrent.org/dht_sec.html. + NoSecurity = false + + #Initial IP blocklist to use. Applied before serving and bootstrapping begins. + IPBlocklist = "" #of type iplist.Ranger + + #Used to secure the server's ID. Defaults to the Conn's LocalAddr(). Set to the IP that remote nodes will see, + #as that IP is what they'll use to validate our ID. + PublicIP = "" #net.IP + + #Hook received queries. Return true if you don't want to propagate to the default handlers. + OnQuery = "func(query *krpc.Msg, source net.Addr) (propagate bool)" + + #Called when a peer successfully announces to us. + OnAnnouncePeer = "func(infoHash metainfo.Hash, peer Peer)" + + #How long to wait before re-sending queries that haven't received a response. Defaults to a random value between 4.5 and 5.5s. + QueryResendDelay = "func() time.Duration" \ No newline at end of file diff --git a/config.toml b/config.toml index f405a7dc..dc614d96 100644 --- a/config.toml +++ b/config.toml @@ -8,7 +8,7 @@ SeedRatioStop = 1.50 #automatically stops the torrent after it reaches this seeding ratio #Relative or absolute path accepted, the server will convert any relative path to an absolute path. - DefaultMoveFolder = 'downloaded' #default path that a finished torrent is symlinked to after completion. Torrents added via RSS will default here + DefaultMoveFolder = 'Z:\downloads' #default path that a finished torrent is symlinked to after completion. Torrents added via RSS will default here TorrentWatchFolder = 'torrentUpload' #folder path that is watched for .torrent files and adds them automatically every 5 minutes #Limits your upload and download speed globally, all are averages and not burst protected (usually burst on start). @@ -18,19 +18,20 @@ [goTorrentWebUI] #Basic goTorrentWebUI authentication (not terribly secure, implemented in JS, password is hashed to SHA256, not salted, basically don't depend on this if you require very good security) - WebUIAuth = true # bool, if false no authentication is required for the webUI + WebUIAuth = false # bool, if false no authentication is required for the webUI WebUIUser = "admin" WebUIPassword = "Password1" [notifications] - PushBulletToken = "" #add your pushbullet api token here to notify of torrent completion to pushbullet + PushBulletToken = "o.8sUHemPkTCaty3u7KnyvEBN19EkeT63g" #add your pushbullet api token here to notify of torrent completion to pushbullet [reverseProxy] #This is for setting up goTorrent behind a reverse Proxy (with SSL, reverse proxy with no SSL will require editing the WSS connection to a WS connection manually) - ProxyEnabled = false #bool, either false or true - BaseURL = "yoursubdomain.domain.org/subroute/" # MUST be in the format (if you have a subdomain, and must have trailing slash) "yoursubdomain.domain.org/subroute/" + ProxyEnabled = true #bool, either false or true + #URL is CASE SENSITIVE + BaseURL = "derajnet.duckdns.org/gopher/" # MUST be in the format (if you have a subdomain, and must have trailing slash) "yoursubdomain.domain.org/subroute/" [EncryptionPolicy] diff --git a/engine/engine.go b/engine/engine.go index 40a89c39..5cda8ed2 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -173,7 +173,6 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To } torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray - fmt.Println("TorrentUPloadLimit", torrentLocalStorage.TorrentUploadLimit) Storage.AddTorrentLocalStorage(torrentDbStorage, torrentLocalStorage) //writing all of the data to the database clientTorrent.DownloadAll() //starting the download CreateServerPushMessage(ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "success", Payload: "Torrent added!"}, Conn) @@ -267,7 +266,6 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto tickUpdateStruct.UploadedBytes = fullClientDB.TotalUploadedBytes tickUpdateStruct.TorrentStatus = fullClientDB.Status tickUpdateStruct.Hash = fullClientDB.TorrentHashString //needed for index - fmt.Println("Status", tickUpdateStruct.TorrentStatus) Storage.UpdateStorageTick(db, tickUpdateStruct) RunningTorrentArray = append(RunningTorrentArray, *fullClientDB) diff --git a/engine/engineHelpers.go b/engine/engineHelpers.go index 05737216..16b4977a 100644 --- a/engine/engineHelpers.go +++ b/engine/engineHelpers.go @@ -69,8 +69,8 @@ func CalculateTorrentSpeed(t *torrent.Torrent, c *ClientDB, oc ClientDB) { dbU := float32(bytesUpload - oc.DataBytesWritten) rateUpload := dbU * (float32(time.Second) / dt) if rate >= 0 { - rate = rate / 1024 / 1024 //creating integer to calculate ETA - c.DownloadSpeed = fmt.Sprintf("%.2f", rate) + rateMB := rate / 1024 / 1024 //creating MB to calculate ETA + c.DownloadSpeed = fmt.Sprintf("%.2f", rateMB) c.DownloadSpeed = c.DownloadSpeed + " MB/s" c.downloadSpeedInt = int64(rate) } @@ -117,13 +117,12 @@ func CalculateCompletedSize(tFromStorage *Storage.TorrentLocal, activeTorrent *t //CalculateTorrentETA is used to estimate the remaining dl time of the torrent based on the speed that the MB are being downloaded func CalculateTorrentETA(tSize int64, tBytesCompleted int64, c *ClientDB) { missingBytes := tSize - tBytesCompleted - missingMB := missingBytes / 1024 / 1024 - if missingMB == 0 { + if missingBytes == 0 { c.ETA = "Done" } else if c.downloadSpeedInt == 0 { c.ETA = "N/A" } else { - ETASeconds := missingMB / c.downloadSpeedInt + ETASeconds := missingBytes / c.downloadSpeedInt str := secondsToMinutes(ETASeconds) //converting seconds to minutes + seconds c.ETA = str } diff --git a/main.go b/main.go index 793173a2..5f6894e6 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "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" @@ -84,7 +85,8 @@ func handleAuthentication(conn *websocket.Conn, db *storm.DB) { func main() { Engine.Logger = Logger //Injecting the logger into all the packages Storage.Logger = Logger - Config := Engine.FullClientSettingsNew() //grabbing from settings.go + Settings.Logger = Logger + Config := Settings.FullClientSettingsNew() //grabbing from settings.go if Config.LoggingOutput == "file" { _, err := os.Stat("logs") if os.IsNotExist(err) { @@ -131,19 +133,19 @@ func main() { fmt.Println("Error", err) fmt.Println("MAIN TOKEN: %+v\n", tokens) tokens.ID = 3 //creating the initial store - claims := Engine.GoTorrentClaims{ + claims := Settings.GoTorrentClaims{ "goTorrentWebUI", jwt.StandardClaims{ Issuer: "goTorrentServer", }, } - signingkey := Engine.GenerateSigningKey() //Running this will invalidate any certs you already issued!! + signingkey := Settings.GenerateSigningKey() //Running this will invalidate any certs you already issued!! fmt.Println("SigningKey", signingkey) - authString := Engine.GenerateToken(claims, signingkey) + authString := Settings.GenerateToken(claims, signingkey) tokens.SigningKey = signingkey fmt.Println("ClientToken: ", authString) - Engine.GenerateClientConfigFile(Config, authString) //if first run generate the client config file - + Settings.GenerateClientConfigFile(Config, authString) //if first run generate the client config file + tokens.FirstToken = authString tokens.TokenNames = append(tokens.TokenNames, Storage.SingleToken{"firstClient"}) err := ioutil.WriteFile("clientAuth.txt", []byte(authString), 0755) if err != nil { @@ -152,6 +154,16 @@ func main() { db.Save(&tokens) //Writing all of that to the database } + oldConfig, err := Storage.FetchConfig(db) + if err != nil { + Logger.WithFields(logrus.Fields{"error": err}).Info("Assuming first run as no config found in database") + } else { + if oldConfig != 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...") @@ -278,9 +290,9 @@ func main() { case "changeStorageValue": Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Storage Location Update") newStorageLocation := payloadData["StorageValue"].(string) - hashes := payloadData["ChangeStorageHashes"].([]string) + hashes := payloadData["ChangeStorageHashes"].([]interface{}) for _, singleHash := range hashes { - singleTorrent := Storage.FetchTorrentFromStorage(db, singleHash) + singleTorrent := Storage.FetchTorrentFromStorage(db, singleHash.(string)) oldPath := singleTorrent.StoragePath newStorageLocationAbs, err := filepath.Abs(filepath.ToSlash(newStorageLocation)) if err != nil { @@ -292,7 +304,7 @@ func main() { 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, db, true, oldPath) + Engine.MoveAndLeaveSymlink(Config, singleHash.(string), db, true, oldPath) } } @@ -378,9 +390,9 @@ func main() { storageValue, _ = filepath.Abs(filepath.ToSlash(Config.DefaultMoveFolder)) } } - magnetLinks := payloadData["MagnetLinks"].([]string) + magnetLinks := payloadData["MagnetLinks"].([]interface{}) for _, magnetLink := range magnetLinks { - clientTorrent, err := tclient.AddMagnet(magnetLink) //reading the payload into the torrent client + 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) @@ -446,6 +458,7 @@ func main() { oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String()) oldTorrentInfo.TorrentStatus = "Stopped" oldTorrentInfo.MaxConnections = 0 + fmt.Println("Running Command...", "oldMax=singleTorrent.SetMaxEstablishedConns(0)") oldMax := singleTorrent.SetMaxEstablishedConns(0) //Forcing the max amount of connections allowed to zero effectively stopping it Logger.WithFields(logrus.Fields{"oldMaxConnections": oldMax, "torrent": singleTorrent}).Info("Forcing connections to zero for torrent") Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status @@ -517,7 +530,7 @@ func main() { case "setFilePriority": //TODO disable if the file is already at 100%? priorityRequested := payloadData["FilePriority"].(string) torrentHash := payloadData["TorrentHash"].(string) - fileList := payloadData["FilePaths"].([]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") diff --git a/engine/authentication_helper.go b/settings/authentication_helper.go similarity index 98% rename from engine/authentication_helper.go rename to settings/authentication_helper.go index a39b1889..4bee47d5 100644 --- a/engine/authentication_helper.go +++ b/settings/authentication_helper.go @@ -1,4 +1,4 @@ -package engine +package settings import ( "crypto/rand" diff --git a/engine/clientConnectGenerate.go b/settings/clientConnectGenerate.go similarity index 98% rename from engine/clientConnectGenerate.go rename to settings/clientConnectGenerate.go index 85d754c0..312ca92a 100644 --- a/engine/clientConnectGenerate.go +++ b/settings/clientConnectGenerate.go @@ -1,4 +1,4 @@ -package engine +package settings import ( "io/ioutil" diff --git a/engine/settings.go b/settings/settings.go similarity index 98% rename from engine/settings.go rename to settings/settings.go index e57c8854..b445b5eb 100644 --- a/engine/settings.go +++ b/settings/settings.go @@ -1,4 +1,4 @@ -package engine +package settings import ( "crypto/sha256" @@ -14,6 +14,9 @@ import ( "github.com/spf13/viper" ) +//Logger is the injected variable for global logger +var Logger *logrus.Logger + //FullClientSettings contains all of the settings for our entire application type FullClientSettings struct { LoggingLevel logrus.Level diff --git a/storage/storage.go b/storage/storage.go index a507abc4..dea65308 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -20,6 +20,7 @@ type IssuedTokensList struct { ID int `storm:"id,unique"` //storm requires unique ID (will be 3) to save although there will only be one of these SigningKey []byte TokenNames []SingleToken + FirstToken string `storm:omitempty` } //SingleToken stores a single token and all of the associated information @@ -83,6 +84,25 @@ type TorrentLocal struct { TorrentFilePriority []TorrentFilePriority } +//SaveConfig saves the config to the database to compare for changes to settings.toml on restart +func SaveConfig(torrentStorage *storm.DB, config interface{}) { + err := torrentStorage.Save(&config) + if err != nil { + Logger.WithFields(logrus.Fields{"database": torrentStorage, "error": err}).Error("Error saving Config to database!") + } +} + +//FetchConfig fetches the client config from the database +func FetchConfig(torrentStorage *storm.DB) (Engine.FullClientSettings, error) { + config := Engine.FullClientSettings{} + err := torrentStorage.All(&config) + if err != nil { + Logger.WithFields(logrus.Fields{"database": torrentStorage, "error": err}).Error("Unable to read Database into configFile!") + return config, err + } + return config, err +} + //FetchAllStoredTorrents is called to read in ALL local stored torrents in the boltdb database (called on server restart) func FetchAllStoredTorrents(torrentStorage *storm.DB) (torrentLocalArray []*TorrentLocal) { torrentLocalArray = []*TorrentLocal{} //creating the array of the torrentlocal struct