summaryrefslogtreecommitdiffstats
path: root/modules/base
diff options
context:
space:
mode:
Diffstat (limited to 'modules/base')
-rw-r--r--modules/base/tool.go49
-rw-r--r--modules/base/tool_test.go52
2 files changed, 96 insertions, 5 deletions
diff --git a/modules/base/tool.go b/modules/base/tool.go
index 7ac572b85b..c497bee44a 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -15,6 +15,7 @@ import (
"net/http"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strconv"
"strings"
@@ -28,6 +29,15 @@ import (
"github.com/dustin/go-humanize"
)
+// Use at most this many bytes to determine Content Type.
+const sniffLen = 512
+
+// SVGMimeType MIME type of SVG images.
+const SVGMimeType = "image/svg+xml"
+
+var svgTagRegex = regexp.MustCompile(`(?s)\A\s*(?:<!--.*?-->\s*)*<svg\b`)
+var svgTagInXMLRegex = regexp.MustCompile(`(?s)\A<\?xml\b.*?\?>\s*(?:<!--.*?-->\s*)*<svg\b`)
+
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5(str string) string {
m := md5.New()
@@ -265,32 +275,61 @@ func IsLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
+// DetectContentType extends http.DetectContentType with more content types.
+func DetectContentType(data []byte) string {
+ ct := http.DetectContentType(data)
+
+ if len(data) > sniffLen {
+ data = data[:sniffLen]
+ }
+
+ if setting.UI.SVG.Enabled &&
+ ((strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) ||
+ strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data)) {
+
+ // SVG is unsupported. https://github.com/golang/go/issues/15888
+ return SVGMimeType
+ }
+ return ct
+}
+
+// IsRepresentableAsText returns true if file content can be represented as
+// plain text or is empty.
+func IsRepresentableAsText(data []byte) bool {
+ return IsTextFile(data) || IsSVGImageFile(data)
+}
+
// IsTextFile returns true if file content format is plain text or empty.
func IsTextFile(data []byte) bool {
if len(data) == 0 {
return true
}
- return strings.Contains(http.DetectContentType(data), "text/")
+ return strings.Contains(DetectContentType(data), "text/")
}
// IsImageFile detects if data is an image format
func IsImageFile(data []byte) bool {
- return strings.Contains(http.DetectContentType(data), "image/")
+ return strings.Contains(DetectContentType(data), "image/")
+}
+
+// IsSVGImageFile detects if data is an SVG image format
+func IsSVGImageFile(data []byte) bool {
+ return strings.Contains(DetectContentType(data), SVGMimeType)
}
// IsPDFFile detects if data is a pdf format
func IsPDFFile(data []byte) bool {
- return strings.Contains(http.DetectContentType(data), "application/pdf")
+ return strings.Contains(DetectContentType(data), "application/pdf")
}
// IsVideoFile detects if data is an video format
func IsVideoFile(data []byte) bool {
- return strings.Contains(http.DetectContentType(data), "video/")
+ return strings.Contains(DetectContentType(data), "video/")
}
// IsAudioFile detects if data is an video format
func IsAudioFile(data []byte) bool {
- return strings.Contains(http.DetectContentType(data), "audio/")
+ return strings.Contains(DetectContentType(data), "audio/")
}
// EntryIcon returns the octicon class for displaying files/directories
diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go
index 0b708dafdb..cda1685da7 100644
--- a/modules/base/tool_test.go
+++ b/modules/base/tool_test.go
@@ -183,11 +183,63 @@ func TestIsLetter(t *testing.T) {
assert.False(t, IsLetter('$'))
}
+func TestDetectContentTypeLongerThanSniffLen(t *testing.T) {
+ // Pre-condition: Shorter than sniffLen detects SVG.
+ assert.Equal(t, "image/svg+xml", DetectContentType([]byte(`<!-- Comment --><svg></svg>`)))
+ // Longer than sniffLen detects something else.
+ assert.Equal(t, "text/plain; charset=utf-8", DetectContentType([]byte(`<!--
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment
+Comment Comment Comment --><svg></svg>`)))
+}
+
func TestIsTextFile(t *testing.T) {
assert.True(t, IsTextFile([]byte{}))
assert.True(t, IsTextFile([]byte("lorem ipsum")))
}
+func TestIsSVGImageFile(t *testing.T) {
+ assert.True(t, IsSVGImageFile([]byte("<svg></svg>")))
+ assert.True(t, IsSVGImageFile([]byte(" <svg></svg>")))
+ assert.True(t, IsSVGImageFile([]byte(`<svg width="100"></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte("<svg/>")))
+ assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<!-- Comment -->
+ <svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<!-- Multiple -->
+ <!-- Comments -->
+ <svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<!-- Multiline
+ Comment -->
+ <svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?>
+ <!-- Comment -->
+ <svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?>
+ <!-- Multiple -->
+ <!-- Comments -->
+ <svg></svg>`)))
+ assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?>
+ <!-- Multline
+ Comment -->
+ <svg></svg>`)))
+ assert.False(t, IsSVGImageFile([]byte{}))
+ assert.False(t, IsSVGImageFile([]byte("svg")))
+ assert.False(t, IsSVGImageFile([]byte("<svgfoo></svgfoo>")))
+ assert.False(t, IsSVGImageFile([]byte("text<svg></svg>")))
+ assert.False(t, IsSVGImageFile([]byte("<html><body><svg></svg></body></html>")))
+ assert.False(t, IsSVGImageFile([]byte(`<script>"<svg></svg>"</script>`)))
+ assert.False(t, IsSVGImageFile([]byte(`<!-- <svg></svg> inside comment -->
+ <foo></foo>`)))
+ assert.False(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?>
+ <!-- <svg></svg> inside comment -->
+ <foo></foo>`)))
+}
+
func TestFormatNumberSI(t *testing.T) {
assert.Equal(t, "125", FormatNumberSI(int(125)))
assert.Equal(t, "1.3k", FormatNumberSI(int64(1317)))