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("[Invalid Number]") bbcodeNoNegative = []byte("[No Negative Numbers]") bbcodeMissingTag = []byte("[Missing Tag]") 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, "$1") msg = bbcodeItalic.ReplaceAllString(msg, "$1") msg = bbcodeUnderline.ReplaceAllString(msg, "$1") msg = bbcodeStrike.ReplaceAllString(msg, "$1") msg = bbcodeURL.ReplaceAllString(msg, "$1$2//$3") msg = bbcodeURLLabel.ReplaceAllString(msg, "$4") msg = bbcodeQuotes.ReplaceAllString(msg, "
$1
") msg = bbcodeSpoiler.ReplaceAllString(msg, "$1") msg = bbcodeH1.ReplaceAllString(msg, "

$1

") //msg = bbcodeCode.ReplaceAllString(msg,"$1") 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("") closeItalic := []byte("") closeBold := []byte("") closeStrike := []byte("") 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("") closeItalic := []byte("") closeBold := []byte("") closeStrike := []byte("") 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, "
$1$2//$3") msg = bbcodeURLLabel.ReplaceAllString(msg, "$4") msg = bbcodeSpoiler.ReplaceAllString(msg, "$1") msg = bbcodeQuotes.ReplaceAllString(msg, "
$1
") return bbcodeCode.ReplaceAllString(msg, "$1") } 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("") closeItalic := []byte("") closeBold := []byte("") closeStrike := []byte("") 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,"
$1$2//$3") msg = bbcodeURLLabel.ReplaceAllString(msg, "$4") msg = bbcodeQuotes.ReplaceAllString(msg, "
$1
") msg = bbcodeCode.ReplaceAllString(msg, "$1") msg = bbcodeSpoiler.ReplaceAllString(msg, "$1") msg = bbcodeH1.ReplaceAllString(msg, "

$1

") } 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 }