package imguploader import ( "bytes" "context" "fmt" "io/ioutil" "net" "net/http" "net/url" "path" "strings" "time" "github.com/grafana/grafana/pkg/util" ) type WebdavUploader struct { url string username string password string public_url string } var netTransport = &http.Transport{ Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 60 * time.Second, }).Dial, TLSHandshakeTimeout: 5 * time.Second, } var netClient = &http.Client{ Timeout: time.Second * 60, Transport: netTransport, } func (u *WebdavUploader) PublicURL(filename string) string { if strings.Contains(u.public_url, "${file}") { return strings.ReplaceAll(u.public_url, "${file}", filename) } publicURL, _ := url.Parse(u.public_url) publicURL.Path = path.Join(publicURL.Path, filename) return publicURL.String() } func (u *WebdavUploader) Upload(ctx context.Context, imgToUpload string) (string, error) { url, _ := url.Parse(u.url) filename, err := util.GetRandomString(20) if err != nil { return "", err } filename += pngExt url.Path = path.Join(url.Path, filename) // We can ignore the gosec G304 warning on this one because `imgToUpload` comes // from alert notifiers and is only used to upload images generated by alerting. // nolint:gosec imgData, err := ioutil.ReadFile(imgToUpload) if err != nil { return "", err } req, err := http.NewRequest("PUT", url.String(), bytes.NewReader(imgData)) if err != nil { return "", err } if ctx != nil { req = req.WithContext(ctx) } if u.username != "" { req.SetBasicAuth(u.username, u.password) } res, err := netClient.Do(req) if err != nil { return "", err } defer func() { if err := res.Body.Close(); err != nil { logger.Warn("Failed to close response body", "err", err) } }() if res.StatusCode != http.StatusCreated { body, err := ioutil.ReadAll(res.Body) if err != nil { return "", fmt.Errorf("failed to read response body: %w", err) } return "", fmt.Errorf("failed to upload image, statuscode: %d, body: %s", res.StatusCode, body) } if u.public_url != "" { return u.PublicURL(filename), nil } return url.String(), nil } func NewWebdavImageUploader(url, username, password, public_url string) (*WebdavUploader, error) { return &WebdavUploader{ url: url, username: username, password: password, public_url: public_url, }, nil }