diff --git a/common/files.go b/common/files.go index 684c80a5..2d24ea89 100644 --- a/common/files.go +++ b/common/files.go @@ -216,7 +216,10 @@ func (list SFileList) JSTmplInit() error { path = tmplName + ".js" DebugLog("js path: ", path) var ext = filepath.Ext("/tmpl_client/" + path) - gzipData := compressBytesGzip(data) + gzipData, err := compressBytesGzip(data) + if err != nil { + return err + } list.Set("/static/"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) @@ -239,7 +242,10 @@ func (list SFileList) Init() error { path = strings.TrimPrefix(path, "public/") var ext = filepath.Ext("/public/" + path) - gzipData := compressBytesGzip(data) + gzipData, err := compressBytesGzip(data) + if err != nil { + return err + } list.Set("/static/"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) @@ -264,7 +270,10 @@ func (list SFileList) Add(path string, prefix string) error { var ext = filepath.Ext(path) path = strings.TrimPrefix(path, prefix) - gzipData := compressBytesGzip(data) + gzipData, err := compressBytesGzip(data) + if err != nil { + return err + } list.Set("/static"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) @@ -285,10 +294,19 @@ func (list SFileList) Set(name string, data SFile) { list[name] = data } -func compressBytesGzip(in []byte) []byte { +func compressBytesGzip(in []byte) ([]byte, error) { var buff bytes.Buffer - gz := gzip.NewWriter(&buff) - _, _ = gz.Write(in) // TODO: What if this errors? What circumstances could it error under? Should we add a second return value? - _ = gz.Close() - return buff.Bytes() + gz, err := gzip.NewWriterLevel(&buff, gzip.BestCompression) + if err != nil { + return nil, err + } + _, err = gz.Write(in) + if err != nil { + return nil, err + } + err = gz.Close() + if err != nil { + return nil, err + } + return buff.Bytes(), nil } diff --git a/common/theme.go b/common/theme.go index 93c26223..6db00251 100644 --- a/common/theme.go +++ b/common/theme.go @@ -110,7 +110,11 @@ func (theme *Theme) AddThemeStaticFiles() error { } path = strings.TrimPrefix(path, "themes/"+theme.Name+"/public") - gzipData := compressBytesGzip(data) + gzipData, err := compressBytesGzip(data) + if err != nil { + return err + } + StaticFiles.Set("/static/"+theme.Name+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) DebugLog("Added the '/" + theme.Name + path + "' static file for theme " + theme.Name + ".") diff --git a/gen_router.go b/gen_router.go index 493da1de..86e0e7f3 100644 --- a/gen_router.go +++ b/gen_router.go @@ -6,9 +6,11 @@ import ( "log" "strings" "strconv" + "compress/gzip" "sync" "sync/atomic" "errors" + "io" "os" "net/http" @@ -528,6 +530,15 @@ func init() { counters.SetReverseOSMapEnum(reverseOSMapEnum) } +type gzipResponseWriter struct { + io.Writer + http.ResponseWriter +} + +func (w gzipResponseWriter) Write(b []byte) (int, error) { + return w.Writer.Write(b) +} + type WriterIntercept struct { w http.ResponseWriter code int @@ -668,12 +679,6 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - h := w.Header() - h.Set("X-Frame-Options", "deny") - h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing - // TODO: Set the content policy header - h.Set("X-Content-Type-Options", "nosniff") - // TODO: Cover more suspicious strings and at a lower layer than this for _, char := range req.URL.Path { if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) { @@ -698,6 +703,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } + + if prefix != "/ws" { + h := w.Header() + h.Set("X-Frame-Options", "deny") + h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing + // TODO: Set the content policy header + h.Set("X-Content-Type-Options", "nosniff") + } if common.Dev.SuperDebug { router.DumpRequest(req,"before routes.StaticFile") @@ -874,6 +887,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { "routeMapEnum: ", routeMapEnum) } + // Disable Gzip when SSL is disabled for security reasons? + if prefix != "/ws" && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Type", "text/html") + gz := gzip.NewWriter(w) + defer gz.Close() + w = gzipResponseWriter{Writer: gz, ResponseWriter: w} + } router.routeSwitch(w, req, user, prefix, extraData) } @@ -2033,8 +2054,9 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u case "/uploads": if extraData == "" { common.NotFound(w,req,nil) - return + return } + w.Header().Del("Content-Type") counters.RouteViewCounter.Bump(121) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? diff --git a/router_gen/main.go b/router_gen/main.go index 880446e8..61eb4a19 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -224,9 +224,11 @@ import ( "log" "strings" "strconv" + "compress/gzip" "sync" "sync/atomic" "errors" + "io" "os" "net/http" @@ -305,6 +307,15 @@ func init() { counters.SetReverseOSMapEnum(reverseOSMapEnum) } +type gzipResponseWriter struct { + io.Writer + http.ResponseWriter +} + +func (w gzipResponseWriter) Write(b []byte) (int, error) { + return w.Writer.Write(b) +} + type WriterIntercept struct { w http.ResponseWriter code int @@ -445,12 +456,6 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - h := w.Header() - h.Set("X-Frame-Options", "deny") - h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing - // TODO: Set the content policy header - h.Set("X-Content-Type-Options", "nosniff") - // TODO: Cover more suspicious strings and at a lower layer than this for _, char := range req.URL.Path { if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) { @@ -475,6 +480,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } + + if prefix != "/ws" { + h := w.Header() + h.Set("X-Frame-Options", "deny") + h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing + // TODO: Set the content policy header + h.Set("X-Content-Type-Options", "nosniff") + } if common.Dev.SuperDebug { router.DumpRequest(req,"before routes.StaticFile") @@ -651,6 +664,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { "routeMapEnum: ", routeMapEnum) } + // Disable Gzip when SSL is disabled for security reasons? + if prefix != "/ws" && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Type", "text/html") + gz := gzip.NewWriter(w) + defer gz.Close() + w = gzipResponseWriter{Writer: gz, ResponseWriter: w} + } router.routeSwitch(w, req, user, prefix, extraData) } @@ -666,8 +687,9 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u case "/uploads": if extraData == "" { common.NotFound(w,req,nil) - return + return } + w.Header().Del("Content-Type") counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.UploadedFile" }}) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? diff --git a/templates/topic_alt.html b/templates/topic_alt.html index 537f8206..976faf24 100644 --- a/templates/topic_alt.html +++ b/templates/topic_alt.html @@ -72,6 +72,7 @@
{{.Topic.ContentHTML}}
+
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Perms.LikeItem}}{{end}} {{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}} @@ -86,6 +87,7 @@ {{end}} +
{{.Topic.RelativeCreatedAt}} diff --git a/templates/topic_alt_posts.html b/templates/topic_alt_posts.html index f482250c..8a2d39c2 100644 --- a/templates/topic_alt_posts.html +++ b/templates/topic_alt_posts.html @@ -14,6 +14,7 @@ {{/** TODO: We might end up with
s in the inline editor, fix this **/}}
{{.ContentHtml}}
+
{{if $.CurrentUser.Loggedin}} {{if $.CurrentUser.Perms.LikeItem}}{{end}} {{if not $.Topic.IsClosed or $.CurrentUser.Perms.CloseTopic}} @@ -24,6 +25,7 @@ {{end}} +
{{.RelativeCreatedAt}} diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index 911640a3..7c4c2083 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -1022,6 +1022,9 @@ textarea { font-size: 14px; display: inline-block; } +.action_button_left { + display: flex; +} .action_button_right { display: inline-flex; margin-left: auto; diff --git a/themes/nox/public/main.css b/themes/nox/public/main.css index 41979149..86fdddc4 100644 --- a/themes/nox/public/main.css +++ b/themes/nox/public/main.css @@ -586,6 +586,9 @@ button, .formbutton, .panel_right_button { font-size: 15px; color: #dddddd; } +.post_item .action_button_left { + display: flex; +} .post_item .action_button_right { margin-left: auto; display: flex; @@ -657,6 +660,26 @@ button, .formbutton, .panel_right_button { display: none; } + .post_item .content_container { + background-color: transparent; + padding: 0px; + } + .post_item .user_content { + background-color: #444444; + padding: 16px; + } + .post_item .button_container { + display: block; + margin-top: 8px; + } + .post_item .action_button_left { + display: block; + background-color: #444444; + padding: 10px; + } + .ip_item.hide_on_mobile { + display: none; + } .post_item, .topic_reply_container { flex-direction: column; }