image_proxy.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // Copyright 2020 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package proxy // import "miniflux.app/proxy"
  5. import (
  6. "regexp"
  7. "strings"
  8. "miniflux.app/config"
  9. "miniflux.app/url"
  10. "github.com/PuerkitoBio/goquery"
  11. "github.com/gorilla/mux"
  12. )
  13. var regexSplitSrcset = regexp.MustCompile(`,\s+`)
  14. // ImageProxyRewriter replaces image URLs with internal proxy URLs.
  15. func ImageProxyRewriter(router *mux.Router, data string) string {
  16. proxyImages := config.Opts.ProxyImages()
  17. if proxyImages == "none" {
  18. return data
  19. }
  20. doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
  21. if err != nil {
  22. return data
  23. }
  24. doc.Find("img").Each(func(i int, img *goquery.Selection) {
  25. if srcAttr, ok := img.Attr("src"); ok {
  26. if !isDataURL(srcAttr) && (proxyImages == "all" || !url.IsHTTPS(srcAttr)) {
  27. img.SetAttr("src", ProxifyURL(router, srcAttr))
  28. }
  29. }
  30. if srcsetAttr, ok := img.Attr("srcset"); ok {
  31. if proxyImages == "all" || !url.IsHTTPS(srcsetAttr) {
  32. proxifySourceSet(img, router, srcsetAttr)
  33. }
  34. }
  35. })
  36. doc.Find("picture source").Each(func(i int, sourceElement *goquery.Selection) {
  37. if srcsetAttr, ok := sourceElement.Attr("srcset"); ok {
  38. if proxyImages == "all" || !url.IsHTTPS(srcsetAttr) {
  39. proxifySourceSet(sourceElement, router, srcsetAttr)
  40. }
  41. }
  42. })
  43. output, err := doc.Find("body").First().Html()
  44. if err != nil {
  45. return data
  46. }
  47. return output
  48. }
  49. func proxifySourceSet(element *goquery.Selection, router *mux.Router, attributeValue string) {
  50. var proxifiedSources []string
  51. for _, source := range regexSplitSrcset.Split(attributeValue, -1) {
  52. parts := strings.Split(strings.TrimSpace(source), " ")
  53. nbParts := len(parts)
  54. if nbParts > 0 {
  55. rewrittenSource := parts[0]
  56. if !isDataURL(rewrittenSource) {
  57. rewrittenSource = ProxifyURL(router, rewrittenSource)
  58. }
  59. if nbParts > 1 {
  60. rewrittenSource += " " + parts[1]
  61. }
  62. proxifiedSources = append(proxifiedSources, rewrittenSource)
  63. }
  64. }
  65. if len(proxifiedSources) > 0 {
  66. element.SetAttr("srcset", strings.Join(proxifiedSources, ", "))
  67. }
  68. }
  69. func isDataURL(s string) bool {
  70. return strings.HasPrefix(s, "data:")
  71. }