github.com/cjdelisle/matterfoss@v5.11.1+incompatible/services/imageproxy/local.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package imageproxy 5 6 import ( 7 "errors" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "net/url" 13 "strings" 14 "time" 15 16 "github.com/mattermost/mattermost-server/mlog" 17 "github.com/mattermost/mattermost-server/services/httpservice" 18 "willnorris.com/go/imageproxy" 19 ) 20 21 var imageContentTypes = []string{ 22 "image/bmp", "image/cgm", "image/g3fax", "image/gif", "image/ief", "image/jp2", 23 "image/jpeg", "image/jpg", "image/pict", "image/png", "image/prs.btif", "image/svg+xml", 24 "image/tiff", "image/vnd.adobe.photoshop", "image/vnd.djvu", "image/vnd.dwg", 25 "image/vnd.dxf", "image/vnd.fastbidsheet", "image/vnd.fpx", "image/vnd.fst", 26 "image/vnd.fujixerox.edmics-mmr", "image/vnd.fujixerox.edmics-rlc", 27 "image/vnd.microsoft.icon", "image/vnd.ms-modi", "image/vnd.net-fpx", "image/vnd.wap.wbmp", 28 "image/vnd.xiff", "image/webp", "image/x-cmu-raster", "image/x-cmx", "image/x-icon", 29 "image/x-macpaint", "image/x-pcx", "image/x-pict", "image/x-portable-anymap", 30 "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", 31 "image/x-quicktime", "image/x-rgb", "image/x-xbitmap", "image/x-xpixmap", "image/x-xwindowdump", 32 } 33 34 var ErrLocalRequestFailed = Error{errors.New("imageproxy.LocalBackend: failed to request proxied image")} 35 36 type LocalBackend struct { 37 proxy *ImageProxy 38 39 // The underlying image proxy implementation provided by the third party library 40 impl *imageproxy.Proxy 41 } 42 43 func makeLocalBackend(proxy *ImageProxy) *LocalBackend { 44 impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil) 45 46 baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL) 47 if err != nil { 48 mlog.Error("Failed to set base URL for image proxy. Relative image links may not work.", mlog.Err(err)) 49 } else { 50 impl.DefaultBaseURL = baseURL 51 } 52 53 impl.Timeout = time.Duration(httpservice.RequestTimeout) 54 impl.ContentTypes = imageContentTypes 55 56 return &LocalBackend{ 57 proxy: proxy, 58 impl: impl, 59 } 60 } 61 62 func (backend *LocalBackend) GetImage(w http.ResponseWriter, r *http.Request, imageURL string) { 63 // The interface to the proxy only exposes a ServeHTTP method, so fake a request to it 64 req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil) 65 if err != nil { 66 // http.NewRequest should only return an error on an invalid URL 67 mlog.Error("Failed to create request for proxied image", mlog.String("url", imageURL), mlog.Err(err)) 68 69 w.WriteHeader(http.StatusBadRequest) 70 w.Write([]byte{}) 71 return 72 } 73 74 w.Header().Set("X-Frame-Options", "deny") 75 w.Header().Set("X-XSS-Protection", "1; mode=block") 76 w.Header().Set("X-Content-Type-Options", "nosniff") 77 w.Header().Set("Content-Security-Policy", "default-src 'none'; img-src data:; style-src 'unsafe-inline'") 78 79 backend.impl.ServeHTTP(w, req) 80 } 81 82 func (backend *LocalBackend) GetImageDirect(imageURL string) (io.ReadCloser, string, error) { 83 // The interface to the proxy only exposes a ServeHTTP method, so fake a request to it 84 req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil) 85 if err != nil { 86 return nil, "", Error{err} 87 } 88 89 recorder := httptest.NewRecorder() 90 91 backend.impl.ServeHTTP(recorder, req) 92 93 if recorder.Code != http.StatusOK { 94 return nil, "", ErrLocalRequestFailed 95 } 96 97 return ioutil.NopCloser(recorder.Body), recorder.Header().Get("Content-Type"), nil 98 } 99 100 func (backend *LocalBackend) GetProxiedImageURL(imageURL string) string { 101 siteURL := *backend.proxy.ConfigService.Config().ServiceSettings.SiteURL 102 103 if imageURL == "" || imageURL[0] == '/' || strings.HasPrefix(imageURL, siteURL) { 104 return imageURL 105 } 106 107 return siteURL + "/api/v4/image?url=" + url.QueryEscape(imageURL) 108 } 109 110 func (backend *LocalBackend) GetUnproxiedImageURL(proxiedURL string) string { 111 siteURL := *backend.proxy.ConfigService.Config().ServiceSettings.SiteURL 112 113 if !strings.HasPrefix(proxiedURL, siteURL+"/api/v4/image?url=") { 114 return proxiedURL 115 } 116 117 parsed, err := url.Parse(proxiedURL) 118 if err != nil { 119 return proxiedURL 120 } 121 122 u := parsed.Query()["url"] 123 if len(u) == 0 { 124 return proxiedURL 125 } 126 127 return u[0] 128 }