8ef9dc8353
add topic_ogdesc_assign hook initialise hooks after filling slices in bbcode init hoist poll input checks out of inner loops in CreateTopicSubmit shorten variable names and reduce boilerplate
423 lines
12 KiB
Go
423 lines
12 KiB
Go
package extend
|
|
|
|
import (
|
|
"bytes"
|
|
"math/rand"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
|
|
c "github.com/Azareal/Gosora/common"
|
|
)
|
|
|
|
var bbcodeRandom *rand.Rand
|
|
var bbcodeInvalidNumber []byte
|
|
var bbcodeNoNegative []byte
|
|
var bbcodeMissingTag []byte
|
|
|
|
var bbcodeBold *regexp.Regexp
|
|
var bbcodeItalic *regexp.Regexp
|
|
var bbcodeUnderline *regexp.Regexp
|
|
var bbcodeStrike *regexp.Regexp
|
|
var bbcodeH1 *regexp.Regexp
|
|
var bbcodeURL *regexp.Regexp
|
|
var bbcodeURLLabel *regexp.Regexp
|
|
var bbcodeQuotes *regexp.Regexp
|
|
var bbcodeCode *regexp.Regexp
|
|
var bbcodeSpoiler *regexp.Regexp
|
|
|
|
func init() {
|
|
c.Plugins.Add(&c.Plugin{UName: "bbcode", Name: "BBCode", Author: "Azareal", URL: "https://github.com/Azareal", Init: InitBbcode, Deactivate: deactivateBbcode})
|
|
}
|
|
|
|
func InitBbcode(pl *c.Plugin) error {
|
|
bbcodeInvalidNumber = []byte("<red>[Invalid Number]</red>")
|
|
bbcodeNoNegative = []byte("<red>[No Negative Numbers]</red>")
|
|
bbcodeMissingTag = []byte("<red>[Missing Tag]</red>")
|
|
|
|
bbcodeBold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`)
|
|
bbcodeItalic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`)
|
|
bbcodeUnderline = regexp.MustCompile(`(?s)\[u\](.*)\[/u\]`)
|
|
bbcodeStrike = regexp.MustCompile(`(?s)\[s\](.*)\[/s\]`)
|
|
bbcodeH1 = regexp.MustCompile(`(?s)\[h1\](.*)\[/h1\]`)
|
|
urlpattern := `(http|https|ftp|mailto*)(:??)\/\/([\.a-zA-Z\/]+)`
|
|
bbcodeURL = regexp.MustCompile(`\[url\]` + urlpattern + `\[/url\]`)
|
|
bbcodeURLLabel = regexp.MustCompile(`(?s)\[url=` + urlpattern + `\](.*)\[/url\]`)
|
|
bbcodeQuotes = regexp.MustCompile(`\[quote\](.*)\[/quote\]`)
|
|
bbcodeCode = regexp.MustCompile(`\[code\](.*)\[/code\]`)
|
|
bbcodeSpoiler = regexp.MustCompile(`\[spoiler\](.*)\[/spoiler\]`)
|
|
|
|
bbcodeRandom = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
pl.AddHook("parse_assign", BbcodeFullParse)
|
|
pl.AddHook("topic_ogdesc_assign", BbcodeStripTags)
|
|
return nil
|
|
}
|
|
|
|
func deactivateBbcode(pl *c.Plugin) {
|
|
pl.RemoveHook("parse_assign", BbcodeFullParse)
|
|
pl.RemoveHook("topic_ogdesc_assign", BbcodeStripTags)
|
|
}
|
|
|
|
func BbcodeStripTags(msg string) string {
|
|
msg = bbcodeBold.ReplaceAllString(msg, "$1")
|
|
msg = bbcodeItalic.ReplaceAllString(msg, "$1")
|
|
msg = bbcodeUnderline.ReplaceAllString(msg, "$1")
|
|
msg = bbcodeStrike.ReplaceAllString(msg, "$1")
|
|
return msg
|
|
}
|
|
|
|
func BbcodeRegexParse(msg string) string {
|
|
msg = bbcodeBold.ReplaceAllString(msg, "<b>$1</b>")
|
|
msg = bbcodeItalic.ReplaceAllString(msg, "<i>$1</i>")
|
|
msg = bbcodeUnderline.ReplaceAllString(msg, "<u>$1</u>")
|
|
msg = bbcodeStrike.ReplaceAllString(msg, "<s>$1</s>")
|
|
msg = bbcodeURL.ReplaceAllString(msg, "<a href=''$1$2//$3' rel='ugc'>$1$2//$3</i>")
|
|
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href=''$1$2//$3' rel='ugc'>$4</i>")
|
|
msg = bbcodeQuotes.ReplaceAllString(msg, "<blockquote>$1</blockquote>")
|
|
msg = bbcodeSpoiler.ReplaceAllString(msg, "<spoiler>$1</spoiler>")
|
|
msg = bbcodeH1.ReplaceAllString(msg, "<h2>$1</h2>")
|
|
//msg = bbcodeCode.ReplaceAllString(msg,"<span class='codequotes'>$1</span>")
|
|
return msg
|
|
}
|
|
|
|
// Only does the simple BBCode like [u], [b], [i] and [s]
|
|
func bbcodeSimpleParse(msg string) string {
|
|
var hasU, hasB, hasI, hasS bool
|
|
mbytes := []byte(msg)
|
|
for i := 0; (i + 2) < len(mbytes); i++ {
|
|
if mbytes[i] == '[' && mbytes[i+2] == ']' {
|
|
ch := mbytes[i+1]
|
|
if ch == 'b' && !hasB {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasB = true
|
|
} else if ch == 'i' && !hasI {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasI = true
|
|
} else if ch == 'u' && !hasU {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasU = true
|
|
} else if ch == 's' && !hasS {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasS = true
|
|
}
|
|
i += 2
|
|
}
|
|
}
|
|
|
|
// There's an unclosed tag in there x.x
|
|
if hasI || hasU || hasB || hasS {
|
|
closeUnder := []byte("</u>")
|
|
closeItalic := []byte("</i>")
|
|
closeBold := []byte("</b>")
|
|
closeStrike := []byte("</s>")
|
|
if hasI {
|
|
mbytes = append(mbytes, closeItalic...)
|
|
}
|
|
if hasU {
|
|
mbytes = append(mbytes, closeUnder...)
|
|
}
|
|
if hasB {
|
|
mbytes = append(mbytes, closeBold...)
|
|
}
|
|
if hasS {
|
|
mbytes = append(mbytes, closeStrike...)
|
|
}
|
|
}
|
|
return string(mbytes)
|
|
}
|
|
|
|
// Here for benchmarking purposes. Might add a plugin setting for disabling [code] as it has it's paws everywhere
|
|
func BbcodeParseWithoutCode(msg string) string {
|
|
var hasU, hasB, hasI, hasS bool
|
|
var complexBbc bool
|
|
mbytes := []byte(msg)
|
|
for i := 0; (i + 3) < len(mbytes); i++ {
|
|
if mbytes[i] == '[' {
|
|
if mbytes[i+2] != ']' {
|
|
if mbytes[i+1] == '/' {
|
|
if mbytes[i+3] == ']' {
|
|
switch mbytes[i+2] {
|
|
case 'b':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasB = false
|
|
case 'i':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasI = false
|
|
case 'u':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasU = false
|
|
case 's':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasS = false
|
|
}
|
|
i += 3
|
|
} else {
|
|
complexBbc = true
|
|
}
|
|
} else {
|
|
complexBbc = true
|
|
}
|
|
} else {
|
|
ch := mbytes[i+1]
|
|
if ch == 'b' && !hasB {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasB = true
|
|
} else if ch == 'i' && !hasI {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasI = true
|
|
} else if ch == 'u' && !hasU {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasU = true
|
|
} else if ch == 's' && !hasS {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasS = true
|
|
}
|
|
i += 2
|
|
}
|
|
}
|
|
}
|
|
|
|
// There's an unclosed tag in there x.x
|
|
if hasI || hasU || hasB || hasS {
|
|
closeUnder := []byte("</u>")
|
|
closeItalic := []byte("</i>")
|
|
closeBold := []byte("</b>")
|
|
closeStrike := []byte("</s>")
|
|
if hasI {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeItalic...)
|
|
}
|
|
if hasU {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeUnder...)
|
|
}
|
|
if hasB {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeBold...)
|
|
}
|
|
if hasS {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeStrike...)
|
|
}
|
|
}
|
|
|
|
// Copy the new complex parser over once the rough edges have been smoothed over
|
|
if complexBbc {
|
|
msg = string(mbytes)
|
|
msg = bbcodeURL.ReplaceAllString(msg, "<a href='$1$2//$3' rel='ugc'>$1$2//$3</i>")
|
|
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href='$1$2//$3' rel='ugc'>$4</i>")
|
|
msg = bbcodeSpoiler.ReplaceAllString(msg, "<spoiler>$1</spoiler>")
|
|
msg = bbcodeQuotes.ReplaceAllString(msg, "<blockquote>$1</blockquote>")
|
|
return bbcodeCode.ReplaceAllString(msg, "<span class='codequotes'>$1</span>")
|
|
}
|
|
return string(mbytes)
|
|
}
|
|
|
|
// Does every type of BBCode
|
|
func BbcodeFullParse(msg string) string {
|
|
var hasU, hasB, hasI, hasS, hasC bool
|
|
var complexBbc bool
|
|
|
|
mbytes := []byte(msg)
|
|
mbytes = append(mbytes, c.SpaceGap...)
|
|
for i := 0; i < len(mbytes); i++ {
|
|
if mbytes[i] == '[' {
|
|
if mbytes[i+2] != ']' {
|
|
if mbytes[i+1] == '/' {
|
|
if mbytes[i+3] == ']' {
|
|
if !hasC {
|
|
switch mbytes[i+2] {
|
|
case 'b':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasB = false
|
|
case 'i':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasI = false
|
|
case 'u':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasU = false
|
|
case 's':
|
|
mbytes[i] = '<'
|
|
mbytes[i+3] = '>'
|
|
hasS = false
|
|
}
|
|
i += 3
|
|
}
|
|
} else {
|
|
if mbytes[i+6] == ']' && mbytes[i+2] == 'c' && mbytes[i+3] == 'o' && mbytes[i+4] == 'd' && mbytes[i+5] == 'e' {
|
|
hasC = false
|
|
i += 7
|
|
}
|
|
complexBbc = true
|
|
}
|
|
} else {
|
|
// Put the biggest index first to avoid unnecessary bounds checks
|
|
if mbytes[i+5] == ']' && mbytes[i+1] == 'c' && mbytes[i+2] == 'o' && mbytes[i+3] == 'd' && mbytes[i+4] == 'e' {
|
|
hasC = true
|
|
i += 6
|
|
}
|
|
complexBbc = true
|
|
}
|
|
} else if !hasC {
|
|
ch := mbytes[i+1]
|
|
if ch == 'b' && !hasB {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasB = true
|
|
} else if ch == 'i' && !hasI {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasI = true
|
|
} else if ch == 'u' && !hasU {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasU = true
|
|
} else if ch == 's' && !hasS {
|
|
mbytes[i] = '<'
|
|
mbytes[i+2] = '>'
|
|
hasS = true
|
|
}
|
|
i += 2
|
|
}
|
|
}
|
|
}
|
|
|
|
// There's an unclosed tag in there somewhere x.x
|
|
if hasI || hasU || hasB || hasS {
|
|
closeUnder := []byte("</u>")
|
|
closeItalic := []byte("</i>")
|
|
closeBold := []byte("</b>")
|
|
closeStrike := []byte("</s>")
|
|
if hasI {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeItalic...)
|
|
}
|
|
if hasU {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeUnder...)
|
|
}
|
|
if hasB {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeBold...)
|
|
}
|
|
if hasS {
|
|
mbytes = append(bytes.TrimSpace(mbytes), closeStrike...)
|
|
}
|
|
mbytes = append(mbytes, c.SpaceGap...)
|
|
}
|
|
|
|
if complexBbc {
|
|
i := 0
|
|
var start, lastTag int
|
|
var outbytes []byte
|
|
for ; i < len(mbytes); i++ {
|
|
if mbytes[i] == '[' {
|
|
if mbytes[i+1] == 'u' {
|
|
if mbytes[i+4] == ']' && mbytes[i+2] == 'r' && mbytes[i+3] == 'l' {
|
|
i, start, lastTag, outbytes = bbcodeParseURL(i, start, lastTag, mbytes, outbytes)
|
|
continue
|
|
}
|
|
} else if mbytes[i+1] == 'r' {
|
|
if bytes.Equal(mbytes[i+2:i+6], []byte("and]")) {
|
|
i, start, lastTag, outbytes = bbcodeParseRand(i, start, lastTag, mbytes, outbytes)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if lastTag != i {
|
|
outbytes = append(outbytes, mbytes[lastTag:]...)
|
|
}
|
|
|
|
if len(outbytes) != 0 {
|
|
msg = string(outbytes[0 : len(outbytes)-10])
|
|
} else {
|
|
msg = string(mbytes[0 : len(mbytes)-10])
|
|
}
|
|
|
|
// TODO: Optimise these
|
|
//msg = bbcode_url.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"ugc\">$1$2//$3</i>")
|
|
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href='$1$2//$3' rel='ugc'>$4</i>")
|
|
msg = bbcodeQuotes.ReplaceAllString(msg, "<blockquote>$1</blockquote>")
|
|
msg = bbcodeCode.ReplaceAllString(msg, "<span class='codequotes'>$1</span>")
|
|
msg = bbcodeSpoiler.ReplaceAllString(msg, "<spoiler>$1</spoiler>")
|
|
msg = bbcodeH1.ReplaceAllString(msg, "<h2>$1</h2>")
|
|
} else {
|
|
msg = string(mbytes[0 : len(mbytes)-10])
|
|
}
|
|
|
|
return msg
|
|
}
|
|
|
|
// TODO: Strip the containing [url] so the media parser can work it's magic instead? Or do we want to allow something like [url=]label[/url] here?
|
|
func bbcodeParseURL(i int, start int, lastTag int, mbytes []byte, outbytes []byte) (int, int, int, []byte) {
|
|
start = i + 5
|
|
outbytes = append(outbytes, mbytes[lastTag:i]...)
|
|
i = start
|
|
i += c.PartialURLStringLen2(string(mbytes[start:]))
|
|
if !bytes.Equal(mbytes[i:i+6], []byte("[/url]")) {
|
|
outbytes = append(outbytes, c.InvalidURL...)
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
|
|
outbytes = append(outbytes, c.URLOpen...)
|
|
outbytes = append(outbytes, mbytes[start:i]...)
|
|
outbytes = append(outbytes, c.URLOpen2...)
|
|
outbytes = append(outbytes, mbytes[start:i]...)
|
|
outbytes = append(outbytes, c.URLClose...)
|
|
i += 6
|
|
lastTag = i
|
|
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
|
|
func bbcodeParseRand(i int, start int, lastTag int, msgbytes []byte, outbytes []byte) (int, int, int, []byte) {
|
|
outbytes = append(outbytes, msgbytes[lastTag:i]...)
|
|
start = i + 6
|
|
i = start
|
|
for ; ; i++ {
|
|
if msgbytes[i] == '[' {
|
|
if !bytes.Equal(msgbytes[i+1:i+7], []byte("/rand]")) {
|
|
outbytes = append(outbytes, bbcodeMissingTag...)
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
break
|
|
} else if (len(msgbytes) - 1) < (i + 10) {
|
|
outbytes = append(outbytes, bbcodeMissingTag...)
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
}
|
|
|
|
number, err := strconv.ParseInt(string(msgbytes[start:i]), 10, 64)
|
|
if err != nil {
|
|
outbytes = append(outbytes, bbcodeInvalidNumber...)
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
|
|
// TODO: Add support for negative numbers?
|
|
if number < 0 {
|
|
outbytes = append(outbytes, bbcodeNoNegative...)
|
|
return i, start, lastTag, outbytes
|
|
}
|
|
|
|
var dat []byte
|
|
if number == 0 {
|
|
dat = []byte("0")
|
|
} else {
|
|
dat = []byte(strconv.FormatInt((bbcodeRandom.Int63n(number)), 10))
|
|
}
|
|
|
|
outbytes = append(outbytes, dat...)
|
|
i += 7
|
|
lastTag = i
|
|
return i, start, lastTag, outbytes
|
|
}
|