Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion modules/markup/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
// it is still accepted by the CommonMark specification, as well as the HTML5 spec:
// http://spec.commonmark.org/0.28/#email-address
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
v.emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
// At the moment, we use stricter rule for rendering purpose: only allow the "name" part starting after the word boundary
v.emailRegex = regexp.MustCompile(`\b([-\w.!#$%&'*+/=?^{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)\b`)

// emojiShortCodeRegex find emoji by alias like :smile:
v.emojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
Expand Down
14 changes: 13 additions & 1 deletion modules/markup/html_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package markup

import "golang.org/x/net/html"
import (
"strings"

"golang.org/x/net/html"
)

// emailAddressProcessor replaces raw email addresses with a mailto: link.
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
Expand All @@ -14,6 +18,14 @@ func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
return
}

var nextByte byte
if len(node.Data) > m[3] {
nextByte = node.Data[m[3]]
}
if strings.IndexByte(":/", nextByte) != -1 {
// for cases: "[email protected]:owner/repo.git", "https://[email protected]/owner/repo.git"
return
}
mail := node.Data[m[2]:m[3]]
replaceContent(node, m[2], m[3], createLink(ctx, "mailto:"+mail, mail, "" /*mailto*/))
node = node.NextSibling.NextSibling
Expand Down
31 changes: 23 additions & 8 deletions modules/markup/html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,10 @@ func TestRender_email(t *testing.T) {
test := func(input, expected string) {
res, err := markup.RenderString(markup.NewTestRenderContext().WithRelativePath("a.md"), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res), "input: %s", input)
}
// Text that should be turned into email link

// Text that should be turned into email link
test(
"[email protected]",
`<p><a href="mailto:[email protected]" rel="nofollow">[email protected]</a></p>`)
Expand Down Expand Up @@ -260,28 +260,43 @@ func TestRender_email(t *testing.T) {
<a href="mailto:[email protected]" rel="nofollow">[email protected]</a>?
<a href="mailto:[email protected]" rel="nofollow">[email protected]</a>!</p>`)

// match GitHub behavior
test("email@[email protected]", `<p>email@<a href="mailto:[email protected]" rel="nofollow">[email protected]</a></p>`)

// match GitHub behavior
test(`"[email protected]"`, `<p>&#34;<a href="mailto:[email protected]" rel="nofollow">[email protected]</a>&#34;</p>`)

// Test that should *not* be turned into email links
test(
"\"[email protected]\"",
`<p>&#34;[email protected]&#34;</p>`)
test(
"/home/gitea/mailstore/info@gitea/com",
`<p>/home/gitea/mailstore/info@gitea/com</p>`)
test(
"[email protected]:go-gitea/gitea.git",
`<p>[email protected]:go-gitea/gitea.git</p>`)
test(
"https://foo:[email protected]",
`<p><a href="https://foo:[email protected]" rel="nofollow">https://foo:[email protected]</a></p>`)
test(
"gitea@3",
`<p>gitea@3</p>`)
test(
"[email protected]",
`<p>[email protected]</p>`)
test(
"email@[email protected]",
`<p>email@[email protected]</p>`)
test(
"[email protected]",
`<p>[email protected]</p>`)

cases := []struct {
input, expected string
}{
// match GitHub behavior
{"[email protected]", `<p>?<a href="mailto:[email protected]" rel="nofollow">[email protected]</a></p>`},
{"*[email protected]", `<p>*<a href="mailto:[email protected]" rel="nofollow">[email protected]</a></p>`},
{"[email protected]", `<p>~<a href="mailto:[email protected]" rel="nofollow">[email protected]</a></p>`},
}
for _, c := range cases {
test(c.input, c.expected)
}
}

func TestRender_emoji(t *testing.T) {
Expand Down
Loading