Skip to content
quarry Artifact [96d0b27dca]
Login

Artifact [96d0b27dca]

Artifact 96d0b27dcaa3de9d7c600c1c36776060f6d04004e2b11f473232793a0ad5f94a:


// 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]
	})
}