From 0ebb968074ca8974c127cf0ff44a68cd8eeabaa8 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Fri, 16 Dec 2022 20:22:21 -0800 Subject: [PATCH] Handle subdirectories of emoji in copying, fetching and deleting. For #1916 --- controllers/admin/emoji.go | 14 ++++---- core/data/emoji.go | 63 ++++++++++++++++++++++++++------- web/pages/admin/chat/emojis.tsx | 7 ++-- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/controllers/admin/emoji.go b/controllers/admin/emoji.go index de99acb85..43028e6b7 100644 --- a/controllers/admin/emoji.go +++ b/controllers/admin/emoji.go @@ -37,10 +37,10 @@ func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) { } // Prevent path traversal attacks - var emojiFileName = filepath.Base(emoji.Name) - var targetPath = filepath.Join(config.CustomEmojiPath, emojiFileName) + emojiFileName := filepath.Base(emoji.Name) + targetPath := filepath.Join(config.CustomEmojiPath, emojiFileName) - err = os.MkdirAll(config.CustomEmojiPath, 0700) + err = os.MkdirAll(config.CustomEmojiPath, 0o700) if err != nil { controllers.WriteSimpleResponse(w, false, err.Error()) return @@ -76,17 +76,17 @@ func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) { return } - var emojiFileName = filepath.Base(emoji.Name) - var targetPath = filepath.Join(config.CustomEmojiPath, emojiFileName) + // var emojiFileName = filepath.Base(emoji.Name) + targetPath := filepath.Join(config.CustomEmojiPath, emoji.Name) if err := os.Remove(targetPath); err != nil { if os.IsNotExist(err) { - controllers.WriteSimpleResponse(w, false, fmt.Sprintf("Emoji %q doesn't exist", emojiFileName)) + controllers.WriteSimpleResponse(w, false, fmt.Sprintf("Emoji %q doesn't exist", emoji.Name)) } else { controllers.WriteSimpleResponse(w, false, err.Error()) } return } - controllers.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been deleted", emojiFileName)) + controllers.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been deleted", emoji.Name)) } diff --git a/core/data/emoji.go b/core/data/emoji.go index 1233a08f3..b2cbbaba4 100644 --- a/core/data/emoji.go +++ b/core/data/emoji.go @@ -11,25 +11,30 @@ import ( "github.com/owncast/owncast/models" "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) // GetEmojiList returns a list of custom emoji from the emoji directory. func GetEmojiList() []models.CustomEmoji { - var emojiFS = os.DirFS(config.CustomEmojiPath) + emojiFS := os.DirFS(config.CustomEmojiPath) emojiResponse := make([]models.CustomEmoji, 0) - files, err := fs.Glob(emojiFS, "*") - if err != nil { - log.Errorln(err) - return emojiResponse + walkFunction := func(path string, d os.DirEntry, err error) error { + if d.IsDir() { + return nil + } + + emojiPath := filepath.Join(config.EmojiDir, path) + singleEmoji := models.CustomEmoji{Name: d.Name(), URL: emojiPath} + emojiResponse = append(emojiResponse, singleEmoji) + return nil } - for _, name := range files { - emojiPath := filepath.Join(config.EmojiDir, name) - singleEmoji := models.CustomEmoji{Name: name, URL: emojiPath} - emojiResponse = append(emojiResponse, singleEmoji) + if err := fs.WalkDir(emojiFS, ".", walkFunction); err != nil { + log.Errorln("unable to fetch emojis: " + err.Error()) + return emojiResponse } return emojiResponse @@ -38,6 +43,11 @@ func GetEmojiList() []models.CustomEmoji { // SetupEmojiDirectory sets up the custom emoji directory by copying all built-in // emojis if the directory does not yet exist. func SetupEmojiDirectory() (err error) { + type emojiDirectory struct { + path string + isDir bool + } + if utils.DoesFileExists(config.CustomEmojiPath) { return nil } @@ -47,14 +57,42 @@ func SetupEmojiDirectory() (err error) { } staticFS := static.GetEmoji() - files, err := fs.Glob(staticFS, "*") + files := []emojiDirectory{} + + walkFunction := func(path string, d os.DirEntry, err error) error { + if path == "." { + return nil + } + + if d.Name() == "LICENSE.md" { + return nil + } + + files = append(files, emojiDirectory{path: path, isDir: d.IsDir()}) + return nil + } + + if err := fs.WalkDir(staticFS, ".", walkFunction); err != nil { + log.Errorln("unable to fetch emojis: " + err.Error()) + return errors.Wrap(err, "unable to fetch embedded emoji files") + } + if err != nil { return fmt.Errorf("unable to read built-in emoji files: %w", err) } // Now copy all built-in emojis to the custom emoji directory - for _, name := range files { - emojiPath := filepath.Join(config.CustomEmojiPath, filepath.Base(name)) + for _, path := range files { + emojiPath := filepath.Join(config.CustomEmojiPath, path.path) + + if path.isDir { + if err := os.Mkdir(emojiPath, 0o700); err != nil { + return errors.Wrap(err, "unable to create emoji directory, check permissions?: "+path.path) + } + continue + } + + memFile, err := staticFS.Open(path.path) // nolint:gosec diskFile, err := os.Create(emojiPath) @@ -62,7 +100,6 @@ func SetupEmojiDirectory() (err error) { return fmt.Errorf("unable to create custom emoji file on disk: %w", err) } - memFile, err := staticFS.Open(name) if err != nil { _ = diskFile.Close() return fmt.Errorf("unable to open built-in emoji file: %w", err) diff --git a/web/pages/admin/chat/emojis.tsx b/web/pages/admin/chat/emojis.tsx index e40b7dcd6..f3e320045 100644 --- a/web/pages/admin/chat/emojis.tsx +++ b/web/pages/admin/chat/emojis.tsx @@ -51,7 +51,10 @@ const Emoji = () => { getEmojis(); }, []); - async function handleDelete(name: string) { + async function handleDelete(fullPath: string) { + const name = `/${fullPath.split('/').slice(3).join('/')}`; + console.log(name); + setLoading(true); setSubmitStatus(createInputStatus(STATUS_PROCESSING, 'Deleting emoji...')); @@ -128,7 +131,7 @@ const Emoji = () => { key: 'delete', render: (text, record) => ( -