// fossilscheme.go resolves Fossil's own URI schemes in links — forum:,
// wiki:, ticket:, checkin: and info: — which imported content is full
// of (`[the thread](forum:/forumpost/2ed79a01ae)`). Fossil resolves them
// against the repository the text lives in; so do we, through the same
// Resolver seam as every other reference. Like attach:, the scheme is
// admitted by the sanitizer only so this pass can see it: resolved it
// becomes a quarry route, unresolved it becomes the label the author
// wrote, and no such URL ever reaches a reader.
package markup
import (
"regexp"
"strings"
)
// fossilSchemes are the schemes rewritten here, spelled once for the
// sanitizer, the link pass and the wiki-bracket form.
var fossilSchemes = []string{"forum", "wiki", "ticket", "checkin", "info"}
var fossilSchemeAnchor = regexp.MustCompile(`<a href="(` + strings.Join(fossilSchemes, "|") + `):([^"]*)"([^>]*)>(.*?)</a>`)
// resolveFossilScheme maps scheme:rest to a quarry URL, or "" when the
// target is not in this repository.
func resolveFossilScheme(r Resolver, scheme, rest string) string {
if r == nil || rest == "" {
return ""
}
if scheme == "wiki" {
return r.ResolveWiki(rest)
}
// forum:/forumpost/X and ticket:/tktview/X carry Fossil's own
// path; the short forms name the object alone.
path, id := rest, strings.TrimPrefix(rest, "/")
if !strings.HasPrefix(rest, "/") {
switch scheme {
case "forum":
path = "/forumpost/" + rest
case "ticket":
path = "/tktview/" + rest
default:
path = ""
}
}
if path != "" {
if u := r.ResolvePath(path); u != "" {
return u
}
}
if scheme == "forum" || scheme == "wiki" || !isHexToken(id) {
return ""
}
return r.ResolveHash(id)
}
// rewriteFossilSchemes replaces every scheme link in rendered HTML. It
// runs even when r is nil: an unresolvable scheme on the page is worse
// than no link at all.
func rewriteFossilSchemes(html []byte, r Resolver) []byte {
return fossilSchemeAnchor.ReplaceAllFunc(html, func(m []byte) []byte {
sub := fossilSchemeAnchor.FindSubmatch(m)
scheme, rest := string(sub[1]), attachName(string(sub[2]))
if u := resolveFossilScheme(r, scheme, rest); u != "" {
return []byte(`<a href="` + escapeAttr(u) + `"` + string(sub[3]) + `>` + string(sub[4]) + `</a>`)
}
return sub[4]
})
}