github.com/adacta-ru/mattermost-server/v6@v6.0.0/services/imageproxy/atmos_camo.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package imageproxy
     5  
     6  import (
     7  	"crypto/hmac"
     8  	"crypto/sha1"
     9  	"encoding/hex"
    10  	"io"
    11  	"net/http"
    12  	"net/url"
    13  )
    14  
    15  type AtmosCamoBackend struct {
    16  	proxy     *ImageProxy
    17  	siteURL   *url.URL
    18  	remoteURL *url.URL
    19  }
    20  
    21  func makeAtmosCamoBackend(proxy *ImageProxy) *AtmosCamoBackend {
    22  	// We deliberately ignore the error because it's from config.json.
    23  	// The function returns a nil pointer in case of error, and we handle it when it's used.
    24  	siteURL, _ := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL)
    25  	remoteURL, _ := url.Parse(*proxy.ConfigService.Config().ImageProxySettings.RemoteImageProxyURL)
    26  
    27  	return &AtmosCamoBackend{
    28  		proxy:     proxy,
    29  		siteURL:   siteURL,
    30  		remoteURL: remoteURL,
    31  	}
    32  }
    33  
    34  func (backend *AtmosCamoBackend) GetImage(w http.ResponseWriter, r *http.Request, imageURL string) {
    35  	http.Redirect(w, r, backend.getAtmosCamoImageURL(imageURL), http.StatusFound)
    36  }
    37  
    38  func (backend *AtmosCamoBackend) GetImageDirect(imageURL string) (io.ReadCloser, string, error) {
    39  	req, err := http.NewRequest("GET", backend.getAtmosCamoImageURL(imageURL), nil)
    40  	if err != nil {
    41  		return nil, "", Error{err}
    42  	}
    43  
    44  	client := backend.proxy.HTTPService.MakeClient(false)
    45  
    46  	resp, err := client.Do(req)
    47  	if err != nil {
    48  		return nil, "", Error{err}
    49  	}
    50  
    51  	// Note that we don't do any additional validation of the received data since we expect the image proxy to do that
    52  	return resp.Body, resp.Header.Get("Content-Type"), nil
    53  }
    54  
    55  func (backend *AtmosCamoBackend) getAtmosCamoImageURL(imageURL string) string {
    56  	cfg := *backend.proxy.ConfigService.Config()
    57  	options := *cfg.ImageProxySettings.RemoteImageProxyOptions
    58  
    59  	if imageURL == "" || backend.siteURL == nil {
    60  		return imageURL
    61  	}
    62  
    63  	// Parse url, return siteURL in case of failure.
    64  	// Also if the URL is opaque.
    65  	parsedURL, err := url.Parse(imageURL)
    66  	if err != nil || parsedURL.Opaque != "" {
    67  		return backend.siteURL.String()
    68  	}
    69  
    70  	// If host is same as siteURL host/ remoteURL host, return.
    71  	if parsedURL.Host == backend.siteURL.Host || parsedURL.Host == backend.remoteURL.Host {
    72  		return parsedURL.String()
    73  	}
    74  
    75  	// Handle protocol-relative URLs.
    76  	if parsedURL.Scheme == "" {
    77  		parsedURL.Scheme = backend.siteURL.Scheme
    78  	}
    79  
    80  	// If it's a relative URL, fill up the hostname and scheme and return.
    81  	if parsedURL.Host == "" {
    82  		parsedURL.Host = backend.siteURL.Host
    83  		return parsedURL.String()
    84  	}
    85  
    86  	urlBytes := []byte(parsedURL.String())
    87  	mac := hmac.New(sha1.New, []byte(options))
    88  	mac.Write(urlBytes)
    89  	digest := hex.EncodeToString(mac.Sum(nil))
    90  
    91  	return backend.remoteURL.String() + "/" + digest + "/" + hex.EncodeToString(urlBytes)
    92  }