From 355cef56f729447cdbfe0d4d83063b13c320545b Mon Sep 17 00:00:00 2001 From: wrench Date: Mon, 27 Nov 2023 16:06:09 +0530 Subject: [PATCH] feat(hashing): add hash to generated links --- README.md | 1 + commands/stream.go | 21 ++++++++++++++++++--- config/config.go | 13 +++++++++++++ routes/stream.go | 17 +++++++++++++++++ types/file.go | 27 ++++++++++++++++++++++++++- types/part.go | 9 --------- utils/hashing.go | 18 ++++++++++++++++++ 7 files changed, 93 insertions(+), 13 deletions(-) delete mode 100644 types/part.go create mode 100644 utils/hashing.go diff --git a/README.md b/README.md index 3d8a903c..e11ac4a4 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ In addition to the mandatory variables, you can also set the following optional - `HOST` : A Fully Qualified Domain Name if present or use your server IP. (eg. `https://example.com` or `http://14.1.154.2:8080`) +- `HASH_LENGTH` : This is the custom hash length for generated URLs. The hash length must be greater than 5 and less than or equal to 32. The default value is 6. ## Contributing diff --git a/commands/stream.go b/commands/stream.go index f3cc8638..b2b9a78d 100644 --- a/commands/stream.go +++ b/commands/stream.go @@ -26,10 +26,11 @@ func sendLink(ctx *ext.Context, u *ext.Update) error { if u.EffectiveMessage.Media == nil { return dispatcher.EndGroups } - if len(u.Entities.Chats) != 0 { + chatId := u.EffectiveChat().GetID() + peerChatId := storage.GetPeerById(chatId) + if peerChatId.Type != int(storage.TypeUser) { return dispatcher.EndGroups } - chatId := u.EffectiveChat().GetID() peer := storage.GetPeerById(config.ValueOf.LogChannelID) switch storage.EntityType(peer.Type) { case storage.TypeChat: @@ -57,7 +58,21 @@ func sendLink(ctx *ext.Context, u *ext.Update) error { ctx.Reply(u, fmt.Sprintf("Error - %s", err.Error()), nil) return dispatcher.EndGroups } - link := fmt.Sprintf("%s/stream/%d", config.ValueOf.Host, messageID) + doc := update.(*tg.Updates).Updates[1].(*tg.UpdateNewChannelMessage).Message.(*tg.Message).Media + file, err := utils.FileFromMedia(doc) + if err != nil { + ctx.Reply(u, fmt.Sprintf("Error - %s", err.Error()), nil) + return dispatcher.EndGroups + } + fullHash := utils.PackFile( + file.FileName, + file.FileSize, + file.MimeType, + file.ID, + ) + + hash := utils.GetShortHash(fullHash) + link := fmt.Sprintf("%s/stream/%d?hash=%s", config.ValueOf.Host, messageID, hash) ctx.Reply(u, link, nil) return dispatcher.EndGroups } diff --git a/config/config.go b/config/config.go index 5f7f6210..d87510fd 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,7 @@ type config struct { Dev bool `envconfig:"DEV" default:"false"` Port int `envconfig:"PORT" default:"8080"` Host string `envconfig:"HOST" default:"http://localhost:8080"` + HashLength int `envconfig:"HASH_LENGTH" default:"6"` } func (c *config) setupEnvVars() { @@ -33,6 +34,18 @@ func Load(log *zap.Logger) { defer log.Info("Loaded config") ValueOf.setupEnvVars() ValueOf.LogChannelID = int64(stripInt(log, int(ValueOf.LogChannelID))) + if ValueOf.HashLength == 0 { + log.Sugar().Info("HASH_LENGTH can't be 0, defaulting to 6") + ValueOf.HashLength = 6 + } + if ValueOf.HashLength > 32 { + log.Sugar().Info("HASH_LENGTH can't be more than 32, changing to 32") + ValueOf.HashLength = 32 + } + if ValueOf.HashLength < 5 { + log.Sugar().Info("HASH_LENGTH can't be less than 5, defaulting to 6") + ValueOf.HashLength = 6 + } } func stripInt(log *zap.Logger, a int) int { diff --git a/routes/stream.go b/routes/stream.go index 85e0a985..50c5ea94 100644 --- a/routes/stream.go +++ b/routes/stream.go @@ -33,6 +33,12 @@ func getStreamRoute(ctx *gin.Context) { return } + authHash := ctx.Query("hash") + if authHash == "" { + http.Error(w, "missing hash param", http.StatusBadRequest) + return + } + ctx.Header("Accept-Ranges", "bytes") var start, end int64 rangeHeader := r.Header.Get("Range") @@ -48,6 +54,17 @@ func getStreamRoute(ctx *gin.Context) { return } + expectedHash := utils.PackFile( + file.FileName, + file.FileSize, + file.MimeType, + file.ID, + ) + if !utils.CheckHash(authHash, expectedHash) { + http.Error(w, "invalid hash", http.StatusBadRequest) + return + } + if rangeHeader == "" { start = 0 end = file.FileSize - 1 diff --git a/types/file.go b/types/file.go index 88059075..d860a3d4 100644 --- a/types/file.go +++ b/types/file.go @@ -1,6 +1,13 @@ package types -import "github.com/gotd/td/tg" +import ( + "bytes" + "crypto/md5" + "encoding/gob" + "fmt" + + "github.com/gotd/td/tg" +) type File struct { Location *tg.InputDocumentFileLocation @@ -9,3 +16,21 @@ type File struct { MimeType string ID int64 } + +type HashableFileStruct struct { + FileName string + FileSize int64 + MimeType string + FileID int64 +} + +func (f *HashableFileStruct) Pack() string { + var b bytes.Buffer + gob.NewEncoder(&b).Encode(f) + return FromBytes(b.Bytes()) +} + +func FromBytes(data []byte) string { + result := md5.Sum(data) + return fmt.Sprintf("%x", result) +} diff --git a/types/part.go b/types/part.go deleted file mode 100644 index b158a8f3..00000000 --- a/types/part.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -import "github.com/gotd/td/tg" - -type Part struct { - Location *tg.InputDocumentFileLocation - Start int64 - End int64 -} diff --git a/utils/hashing.go b/utils/hashing.go new file mode 100644 index 00000000..8a018cf2 --- /dev/null +++ b/utils/hashing.go @@ -0,0 +1,18 @@ +package utils + +import ( + "EverythingSuckz/fsb/config" + "EverythingSuckz/fsb/types" +) + +func PackFile(fileName string, fileSize int64, mimeType string, fileID int64) string { + return (&types.HashableFileStruct{FileName: fileName, FileSize: fileSize, MimeType: mimeType, FileID: fileID}).Pack() +} + +func GetShortHash(fullHash string) string { + return fullHash[:config.ValueOf.HashLength] +} + +func CheckHash(inputHash string, expectedHash string) bool { + return inputHash == GetShortHash(expectedHash) +}