diff options
author | Simon Ser <contact@emersion.fr> | 2020-02-25 15:45:43 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2020-02-25 15:45:43 +0100 |
commit | a8a3c82579ac244dde56320224f6bd15f17427f7 (patch) | |
tree | b0f841a6f0013def4674620ba43ec27077b044c1 | |
parent | 8c4fd20e2765e112518a383229a70a27b7984da3 (diff) | |
download | alps-a8a3c82579ac244dde56320224f6bd15f17427f7.tar.gz alps-a8a3c82579ac244dde56320224f6bd15f17427f7.zip |
plugins/viewhtml: add image proxy
-rw-r--r-- | plugins/viewhtml/plugin.go | 49 | ||||
-rw-r--r-- | plugins/viewhtml/sanitize.go | 29 |
2 files changed, 71 insertions, 7 deletions
diff --git a/plugins/viewhtml/plugin.go b/plugins/viewhtml/plugin.go index f22364e..b34372f 100644 --- a/plugins/viewhtml/plugin.go +++ b/plugins/viewhtml/plugin.go @@ -1,10 +1,59 @@ package koushinviewhtml import ( + "io" + "mime" + "net/http" + "net/url" + "strconv" + "strings" + "git.sr.ht/~emersion/koushin" + "github.com/labstack/echo/v4" +) + +var ( + proxyEnabled = true + proxyMaxSize = 5 * 1024 * 1024 // 5 MiB ) func init() { p := koushin.GoPlugin{Name: "viewhtml"} + + p.GET("/proxy", func(ctx *koushin.Context) error { + if !proxyEnabled { + return echo.NewHTTPError(http.StatusForbidden, "proxy disabled") + } + + u, err := url.Parse(ctx.QueryParam("src")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "invalid URL") + } + + if u.Scheme != "https" { + return echo.NewHTTPError(http.StatusBadRequest, "invalid scheme") + } + + resp, err := http.Get(u.String()) + if err != nil { + return err + } + defer resp.Body.Close() + + mediaType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil || !strings.HasPrefix(mediaType, "image/") { + return echo.NewHTTPError(http.StatusBadRequest, "invalid resource type") + } + + size, err := strconv.Atoi(resp.Header.Get("Content-Length")) + if err != nil || size > proxyMaxSize { + return echo.NewHTTPError(http.StatusBadRequest, "invalid resource length") + } + + ctx.Response().Header().Set("Content-Length", strconv.Itoa(size)) + lr := io.LimitedReader{resp.Body, int64(proxyMaxSize)} + return ctx.Stream(http.StatusOK, mediaType, &lr) + }) + koushin.RegisterPluginLoader(p.Loader()) } diff --git a/plugins/viewhtml/sanitize.go b/plugins/viewhtml/sanitize.go index c7de703..a931fe1 100644 --- a/plugins/viewhtml/sanitize.go +++ b/plugins/viewhtml/sanitize.go @@ -80,17 +80,32 @@ func (san *sanitizer) sanitizeImageURL(src string) string { return "about:blank" } + switch strings.ToLower(u.Scheme) { // TODO: mid support? - if !strings.EqualFold(u.Scheme, "cid") || san.msg == nil { - return "about:blank" - } + case "cid": + if san.msg == nil { + return "about:blank" + } + + part := san.msg.PartByID(u.Opaque) + if part == nil || !strings.HasPrefix(part.MIMEType, "image/") { + return "about:blank" + } + + return part.URL(true).String() + case "https": + if !proxyEnabled { + return "about:blank" + } - part := san.msg.PartByID(u.Opaque) - if part == nil || !strings.HasPrefix(part.MIMEType, "image/") { + proxyURL := url.URL{Path: "/proxy"} + proxyQuery := make(url.Values) + proxyQuery.Set("src", u.String()) + proxyURL.RawQuery = proxyQuery.Encode() + return proxyURL.String() + default: return "about:blank" } - - return part.URL(true).String() } func (san *sanitizer) sanitizeCSSDecls(decls []*css.Declaration) []*css.Declaration { |