diff --git a/common/common.go b/common/common.go index 87ee475b..cfc3b918 100644 --- a/common/common.go +++ b/common/common.go @@ -42,17 +42,18 @@ type StringList []string // ? - Should we allow users to upload .php or .go files? It could cause security issues. We could store them with a mangled extension to render them inert // TODO: Let admins manage this from the Control Panel +// apng is commented out for now, as we have no way of re-encoding it into a smaller file var AllowedFileExts = StringList{ "png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", /*"apng",*/ // images - "txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", // text + "txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", /*"go","php",*/ // text "mp3", "mp4", "avi", "wmv", "webm", // video "otf", "woff2", "woff", "ttf", "eot", // fonts } var ImageFileExts = StringList{ - "png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", "apng", + "png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", /* "apng",*/ } var ArchiveFileExts = StringList{ "bz2", "zip", "gz", "7z", "tar", "cab", diff --git a/common/extend.go b/common/extend.go index 9ca31050..36179966 100644 --- a/common/extend.go +++ b/common/extend.go @@ -12,6 +12,7 @@ import ( "errors" "log" "net/http" + "sync/atomic" "../query_gen/lib" ) @@ -39,6 +40,28 @@ func buildPlugin(plugin *Plugin) { plugin.Data = nil } +var hookTableBox atomic.Value + +// ! HookTable is a work in progress, do not use it yet +// TODO: Test how fast it is to indirect hooks off the hook table as opposed to using them normally or using an interface{} for the hooks +// TODO: Can we filter the HookTable for each request down to only hooks the request actually uses? +// TODO: Make the RunXHook functions methods on HookTable +// TODO: Have plugins update hooks on a mutex guarded map and create a copy of that map in a serial global goroutine which gets thrown in the atomic.Value +type HookTable struct { + Hooks map[string][]func(interface{}) interface{} + Vhooks map[string]func(...interface{}) interface{} + VhookSkippable map[string]func(...interface{}) (bool, RouteError) + Sshooks map[string][]func(string) string + PreRenderHooks map[string][]func(http.ResponseWriter, *http.Request, *User, interface{}) bool + + // For future use: + messageHooks map[string][]func(Message, PageInt, ...interface{}) interface{} +} + +func init() { + hookTableBox.Store(new(HookTable)) +} + // Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with? var Hooks = map[string][]func(interface{}) interface{}{ "forums_frow_assign": nil, @@ -153,7 +176,8 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User "pre_render_panel_themes": nil, "pre_render_panel_modlogs": nil, - "pre_render_error": nil, // Note: This hook isn't run for a few errors whose templates are computed at startup and reused, such as InternalError. This hook is also not available in JS mode. + "pre_render_error": nil, // Note: This hook isn't run for a few errors whose templates are computed at startup and reused, such as InternalError. This hook is also not available in JS mode. + // ^-- I don't know if it's run for InternalError, but it isn't computed at startup anymore "pre_render_security_error": nil, } @@ -426,14 +450,21 @@ func InitPlugins() { // ? - Are the following functions racey? func RunHook(name string, data interface{}) interface{} { - for _, hook := range Hooks[name] { - data = hook(data) + hooks, ok := Hooks[name] + if ok { + for _, hook := range hooks { + data = hook(data) + } } return data } func RunHookNoreturn(name string, data interface{}) { - for _, hook := range Hooks[name] { + hooks, ok := Hooks[name] + if !ok { + return + } + for _, hook := range hooks { _ = hook(data) } } @@ -479,24 +510,33 @@ func RunTaskHook(name string) error { // Trying to get a teeny bit of type-safety where-ever possible, especially for such a critical set of hooks func RunSshook(name string, data string) string { - for _, hook := range Sshooks[name] { - data = hook(data) + ssHooks, ok := Sshooks[name] + if ok { + for _, hook := range ssHooks { + data = hook(data) + } } return data } func RunPreRenderHook(name string, w http.ResponseWriter, r *http.Request, user *User, data interface{}) (halt bool) { // This hook runs on ALL PreRender hooks - for _, hook := range PreRenderHooks["pre_render"] { - if hook(w, r, user, data) { - return true + preRenderHooks, ok := PreRenderHooks["pre_render"] + if ok { + for _, hook := range preRenderHooks { + if hook(w, r, user, data) { + return true + } } } // The actual PreRender hook - for _, hook := range PreRenderHooks[name] { - if hook(w, r, user, data) { - return true + preRenderHooks, ok = PreRenderHooks[name] + if ok { + for _, hook := range preRenderHooks { + if hook(w, r, user, data) { + return true + } } } return false diff --git a/gosora_example.service b/gosora_example.service index 1a6b4e47..eff0e003 100644 --- a/gosora_example.service +++ b/gosora_example.service @@ -15,5 +15,8 @@ WorkingDirectory=/home/gosora # Make sure you manually run pre-run-linux before you start the service ExecStart=/home/gosora/Gosora +# Let's hope this doesn't blow up on me +ProtectSystem=full + [Install] WantedBy=multi-user.target \ No newline at end of file