package main
import (
//"fmt"
"bytes"
"html/template"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
type HeaderVars struct {
NoticeList []string
Scripts []string
Stylesheets []string
Widgets PageWidgets
Site *Site
Settings map[string]interface{}
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
ThemeName string
ExtData ExtData
}
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
type HeaderLite struct {
Site *Site
Settings SettingBox
ExtData ExtData
}
type PageWidgets struct {
LeftSidebar template.HTML
RightSidebar template.HTML
}
// TODO: Add a ExtDataHolder interface with methods for manipulating the contents?
// ? - Could we use a sync.Map instead?
type ExtData struct {
items map[string]interface{} // Key: pluginname
sync.RWMutex
}
type Page struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []interface{}
Something interface{}
}
type TopicPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []Reply
Topic TopicUser
Page int
LastPage int
}
type TopicsPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []*TopicsRow
}
type ForumPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []*TopicsRow
Forum Forum
Page int
LastPage int
}
type ForumsPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []Forum
}
type ProfilePage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []Reply
ProfileOwner User
}
type CreateTopicPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList []Forum
FID int
}
type IPSearchPage struct {
Title string
CurrentUser User
Header *HeaderVars
ItemList map[int]*User
IP string
}
type PanelStats struct {
Users int
Groups int
Forums int
Settings int
WordFilters int
Themes int
Reports int
}
type PanelPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ItemList []interface{}
Something interface{}
}
type GridElement struct {
ID string
Body string
Order int // For future use
Class string
Background string
TextColour string
Note string
}
type PanelDashboardPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
GridItems []GridElement
}
type PanelThemesPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
PrimaryThemes []Theme
VariantThemes []Theme
}
type PanelUserPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ItemList []User
PageList []int
Page int
LastPage int
}
type PanelGroupPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ItemList []GroupAdmin
PageList []int
Page int
LastPage int
}
type PanelEditGroupPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ID int
Name string
Tag string
Rank string
DisableRank bool
}
type GroupForumPermPreset struct {
Group *Group
Preset string
}
type PanelEditForumPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ID int
Name string
Desc string
Active bool
Preset string
Groups []GroupForumPermPreset
}
/*type NameLangPair struct {
Name string
LangStr string
}*/
type NameLangToggle struct {
Name string
LangStr string
Toggle bool
}
type PanelEditGroupPermsPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
ID int
Name string
LocalPerms []NameLangToggle
GlobalPerms []NameLangToggle
}
type backupItem struct {
SQLURL string
// TODO: Add an easier to parse format here for Gosora to be able to more easily reimport portions of the dump and to strip unneccesary data (e.g. table defs and parsed post data)
Timestamp time.Time
}
type PanelBackupPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
Backups []backupItem
}
type logItem struct {
Action template.HTML
IPAddress string
DoneAt string
}
type PanelLogsPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
Logs []logItem
PageList []int
Page int
LastPage int
}
type PanelDebugPage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
Uptime string
OpenConns int
DBAdapter string
}
type PageSimple struct {
Title string
Something interface{}
}
type AreYouSure struct {
URL string
Message string
}
var spaceGap = []byte(" ")
var httpProtBytes = []byte("http://")
var invalidURL = []byte("[Invalid URL]")
var invalidTopic = []byte("[Invalid Topic]")
var invalidProfile = []byte("[Invalid Profile]")
var invalidForum = []byte("[Invalid Forum]")
var urlOpen = []byte("")
var bytesSinglequote = []byte("'")
var bytesGreaterthan = []byte(">")
var urlMention = []byte(" class='mention'")
var urlClose = []byte("")
var urlpattern = `(?s)([ {1}])((http|https|ftp|mailto)*)(:{??)\/\/([\.a-zA-Z\/]+)([ {1}])`
var urlReg *regexp.Regexp
func init() {
urlReg = regexp.MustCompile(urlpattern)
}
// TODO: Write a test for this
func shortcodeToUnicode(msg string) string {
//re := regexp.MustCompile(":(.):")
msg = strings.Replace(msg, ":grinning:", "đ", -1)
msg = strings.Replace(msg, ":grin:", "đ", -1)
msg = strings.Replace(msg, ":joy:", "đ", -1)
msg = strings.Replace(msg, ":rofl:", "đ¤Ŗ", -1)
msg = strings.Replace(msg, ":smiley:", "đ", -1)
msg = strings.Replace(msg, ":smile:", "đ", -1)
msg = strings.Replace(msg, ":sweat_smile:", "đ
", -1)
msg = strings.Replace(msg, ":laughing:", "đ", -1)
msg = strings.Replace(msg, ":satisfied:", "đ", -1)
msg = strings.Replace(msg, ":wink:", "đ", -1)
msg = strings.Replace(msg, ":blush:", "đ", -1)
msg = strings.Replace(msg, ":yum:", "đ", -1)
msg = strings.Replace(msg, ":sunglasses:", "đ", -1)
msg = strings.Replace(msg, ":heart_eyes:", "đ", -1)
msg = strings.Replace(msg, ":kissing_heart:", "đ", -1)
msg = strings.Replace(msg, ":kissing:", "đ", -1)
msg = strings.Replace(msg, ":kissing_smiling_eyes:", "đ", -1)
msg = strings.Replace(msg, ":kissing_closed_eyes:", "đ", -1)
msg = strings.Replace(msg, ":relaxed:", "âēī¸", -1)
msg = strings.Replace(msg, ":slight_smile:", "đ", -1)
msg = strings.Replace(msg, ":hugging:", "đ¤", -1)
msg = strings.Replace(msg, ":thinking:", "đ¤", -1)
msg = strings.Replace(msg, ":neutral_face:", "đ", -1)
msg = strings.Replace(msg, ":expressionless:", "đ", -1)
msg = strings.Replace(msg, ":no_mouth:", "đļ", -1)
msg = strings.Replace(msg, ":rolling_eyes:", "đ", -1)
msg = strings.Replace(msg, ":smirk:", "đ", -1)
msg = strings.Replace(msg, ":persevere:", "đŖ", -1)
msg = strings.Replace(msg, ":disappointed_relieved:", "đĨ", -1)
msg = strings.Replace(msg, ":open_mouth:", "đŽ", -1)
msg = strings.Replace(msg, ":zipper_mouth:", "đ¤", -1)
msg = strings.Replace(msg, ":hushed:", "đ¯", -1)
msg = strings.Replace(msg, ":sleepy:", "đĒ", -1)
msg = strings.Replace(msg, ":tired_face:", "đĢ", -1)
msg = strings.Replace(msg, ":sleeping:", "đ´", -1)
msg = strings.Replace(msg, ":relieved:", "đ", -1)
msg = strings.Replace(msg, ":nerd:", "đ¤", -1)
msg = strings.Replace(msg, ":stuck_out_tongue:", "đ", -1)
msg = strings.Replace(msg, ":worried:", "đ", -1)
msg = strings.Replace(msg, ":drooling_face:", "đ¤¤", -1)
msg = strings.Replace(msg, ":disappointed:", "đ", -1)
msg = strings.Replace(msg, ":astonished:", "đ˛", -1)
msg = strings.Replace(msg, ":slight_frown:", "đ", -1)
msg = strings.Replace(msg, ":skull_crossbones:", "â ī¸", -1)
msg = strings.Replace(msg, ":skull:", "đ", -1)
msg = strings.Replace(msg, ":point_up:", "âī¸", -1)
msg = strings.Replace(msg, ":v:", "âī¸ī¸", -1)
msg = strings.Replace(msg, ":writing_hand:", "âī¸", -1)
msg = strings.Replace(msg, ":heart:", "â¤ī¸ī¸", -1)
msg = strings.Replace(msg, ":heart_exclamation:", "âŖī¸", -1)
msg = strings.Replace(msg, ":hotsprings:", "â¨ī¸", -1)
msg = strings.Replace(msg, ":airplane:", "âī¸ī¸", -1)
msg = strings.Replace(msg, ":hourglass:", "â", -1)
msg = strings.Replace(msg, ":watch:", "â", -1)
msg = strings.Replace(msg, ":comet:", "âī¸", -1)
msg = strings.Replace(msg, ":snowflake:", "âī¸", -1)
msg = strings.Replace(msg, ":cloud:", "âī¸", -1)
msg = strings.Replace(msg, ":sunny:", "âī¸", -1)
msg = strings.Replace(msg, ":spades:", "â ī¸", -1)
msg = strings.Replace(msg, ":hearts:", "âĨī¸ī¸", -1)
msg = strings.Replace(msg, ":diamonds:", "âĻī¸", -1)
msg = strings.Replace(msg, ":clubs:", "âŖī¸", -1)
msg = strings.Replace(msg, ":phone:", "âī¸", -1)
msg = strings.Replace(msg, ":telephone:", "âī¸", -1)
msg = strings.Replace(msg, ":biohazard:", "âŖī¸", -1)
msg = strings.Replace(msg, ":radioactive:", "âĸī¸", -1)
msg = strings.Replace(msg, ":scissors:", "âī¸", -1)
msg = strings.Replace(msg, ":arrow_upper_right:", "âī¸", -1)
msg = strings.Replace(msg, ":arrow_right:", "âĄī¸", -1)
msg = strings.Replace(msg, ":arrow_lower_right:", "âī¸", -1)
msg = strings.Replace(msg, ":arrow_lower_left:", "âī¸", -1)
msg = strings.Replace(msg, ":arrow_upper_left:", "âī¸", -1)
msg = strings.Replace(msg, ":arrow_up_down:", "âī¸", -1)
msg = strings.Replace(msg, ":left_right_arrow:", "âī¸", -1)
msg = strings.Replace(msg, ":leftwards_arrow_with_hook:", "âŠī¸", -1)
msg = strings.Replace(msg, ":arrow_right_hook:", "âĒī¸", -1)
msg = strings.Replace(msg, ":arrow_forward:", "âļī¸", -1)
msg = strings.Replace(msg, ":arrow_backward:", "âī¸", -1)
msg = strings.Replace(msg, ":female:", "âī¸", -1)
msg = strings.Replace(msg, ":male:", "âī¸", -1)
msg = strings.Replace(msg, ":ballot_box_with_check:", "âī¸", -1)
msg = strings.Replace(msg, ":heavy_check_mark:", "âī¸ī¸", -1)
msg = strings.Replace(msg, ":heavy_multiplication_x:", "âī¸", -1)
msg = strings.Replace(msg, ":pisces:", "â", -1)
msg = strings.Replace(msg, ":aquarius:", "â", -1)
msg = strings.Replace(msg, ":capricorn:", "â", -1)
msg = strings.Replace(msg, ":sagittarius:", "â", -1)
msg = strings.Replace(msg, ":scorpius:", "â", -1)
msg = strings.Replace(msg, ":libra:", "â", -1)
msg = strings.Replace(msg, ":virgo:", "â", -1)
msg = strings.Replace(msg, ":leo:", "â", -1)
msg = strings.Replace(msg, ":cancer:", "â", -1)
msg = strings.Replace(msg, ":gemini:", "â", -1)
msg = strings.Replace(msg, ":taurus:", "â", -1)
msg = strings.Replace(msg, ":aries:", "â", -1)
msg = strings.Replace(msg, ":peace:", "âŽī¸", -1)
msg = strings.Replace(msg, ":eight_spoked_asterisk:", "âŗī¸", -1)
msg = strings.Replace(msg, ":eight_pointed_black_star:", "â´ī¸", -1)
msg = strings.Replace(msg, ":snowman2:", "âī¸", -1)
msg = strings.Replace(msg, ":umbrella2:", "âī¸", -1)
msg = strings.Replace(msg, ":pencil2:", "âī¸", -1)
msg = strings.Replace(msg, ":black_nib:", "âī¸", -1)
msg = strings.Replace(msg, ":email:", "âī¸", -1)
msg = strings.Replace(msg, ":envelope:", "âī¸", -1)
msg = strings.Replace(msg, ":keyboard:", "â¨ī¸", -1)
msg = strings.Replace(msg, ":white_small_square:", "âĢī¸", -1)
msg = strings.Replace(msg, ":black_small_square:", "âĒī¸", -1)
msg = strings.Replace(msg, ":secret:", "ãī¸", -1)
msg = strings.Replace(msg, ":congratulations:", "ãī¸", -1)
msg = strings.Replace(msg, ":m:", "âī¸", -1)
msg = strings.Replace(msg, ":tm:", "âĸī¸ī¸", -1)
msg = strings.Replace(msg, ":registered:", "ÂŽī¸", -1)
msg = strings.Replace(msg, ":copyright:", "Šī¸", -1)
msg = strings.Replace(msg, ":wavy_dash:", "ã°ī¸", -1)
msg = strings.Replace(msg, ":bangbang:", "âŧī¸", -1)
msg = strings.Replace(msg, ":sparkle:", "âī¸", -1)
msg = strings.Replace(msg, ":star_of_david:", "âĄī¸", -1)
msg = strings.Replace(msg, ":wheel_of_dharma:", "â¸ī¸", -1)
msg = strings.Replace(msg, ":yin_yang:", "â¯ī¸", -1)
msg = strings.Replace(msg, ":cross:", "âī¸", -1)
msg = strings.Replace(msg, ":orthodox_cross:", "âĻī¸", -1)
msg = strings.Replace(msg, ":star_and_crescent:", "âĒī¸", -1)
msg = strings.Replace(msg, ":frowning2:", "âšī¸", -1)
msg = strings.Replace(msg, ":information_source:", "âšī¸", -1)
msg = strings.Replace(msg, ":interrobang:", "âī¸", -1)
return msg
}
func preparseMessage(msg string) string {
if sshooks["preparse_preassign"] != nil {
msg = runSshook("preparse_preassign", msg)
}
return shortcodeToUnicode(msg)
}
// TODO: Write a test for this
func parseMessage(msg string /*, user User*/) string {
msg = strings.Replace(msg, ":)", "đ", -1)
msg = strings.Replace(msg, ":(", "đ", -1)
msg = strings.Replace(msg, ":D", "đ", -1)
msg = strings.Replace(msg, ":P", "đ", -1)
msg = strings.Replace(msg, ":O", "đ˛", -1)
msg = strings.Replace(msg, ":p", "đ", -1)
msg = strings.Replace(msg, ":o", "đ˛", -1)
//msg = url_reg.ReplaceAllString(msg,"$2$3//$4")
// Word filter list. E.g. Swear words and other things the admins don't like
wordFilters := wordFilterBox.Load().(WordFilterBox)
for _, filter := range wordFilters {
msg = strings.Replace(msg, filter.Find, filter.Replacement, -1)
}
// Search for URLs, mentions and hashlinks in the messages...
//log.Print("Parser Loop!")
var msgbytes = []byte(msg)
var outbytes []byte
msgbytes = append(msgbytes, spaceGap...)
//log.Print(`"`+string(msgbytes)+`"`)
lastItem := 0
i := 0
for ; len(msgbytes) > (i + 1); i++ {
//log.Print("Index:",i)
//log.Print("Index Item:",msgbytes[i])
//if msgbytes[i] == 10 {
// log.Print("NEWLINE")
//} else if msgbytes[i] == 32 {
// log.Print("SPACE")
//} else {
// log.Print("string(msgbytes[i])",string(msgbytes[i]))
//}
//log.Print("End Index")
if (i == 0 && (msgbytes[0] > 32)) || ((msgbytes[i] < 33) && (msgbytes[i+1] > 32)) {
//log.Print("IN")
//log.Print(msgbytes[i])
if (i != 0) || msgbytes[i] < 33 {
i++
}
if msgbytes[i] == '#' {
//log.Print("IN #")
if bytes.Equal(msgbytes[i+1:i+5], []byte("tid-")) {
outbytes = append(outbytes, msgbytes[lastItem:i]...)
i += 5
start := i
tid, intLen := coerceIntBytes(msgbytes[start:])
i += intLen
topic, err := topics.Get(tid)
if err != nil || !fstore.Exists(topic.ParentID) {
outbytes = append(outbytes, invalidTopic...)
lastItem = i
continue
}
outbytes = append(outbytes, urlOpen...)
var urlBit = []byte(buildTopicURL("", tid))
outbytes = append(outbytes, urlBit...)
outbytes = append(outbytes, urlOpen2...)
var tidBit = []byte("#tid-" + strconv.Itoa(tid))
outbytes = append(outbytes, tidBit...)
outbytes = append(outbytes, urlClose...)
lastItem = i
//log.Print("string(msgbytes)",string(msgbytes))
//log.Print(msgbytes)
//log.Print(msgbytes[lastItem - 1])
//log.Print(lastItem - 1)
//log.Print(msgbytes[lastItem])
//log.Print(lastItem)
} else if bytes.Equal(msgbytes[i+1:i+5], []byte("rid-")) {
outbytes = append(outbytes, msgbytes[lastItem:i]...)
i += 5
start := i
rid, intLen := coerceIntBytes(msgbytes[start:])
i += intLen
topic, err := getTopicByReply(rid)
if err != nil || !fstore.Exists(topic.ParentID) {
outbytes = append(outbytes, invalidTopic...)
lastItem = i
continue
}
outbytes = append(outbytes, urlOpen...)
var urlBit = []byte(buildTopicURL("", topic.ID))
outbytes = append(outbytes, urlBit...)
outbytes = append(outbytes, urlOpen2...)
var ridBit = []byte("#rid-" + strconv.Itoa(rid))
outbytes = append(outbytes, ridBit...)
outbytes = append(outbytes, urlClose...)
lastItem = i
} else if bytes.Equal(msgbytes[i+1:i+5], []byte("fid-")) {
outbytes = append(outbytes, msgbytes[lastItem:i]...)
i += 5
start := i
fid, intLen := coerceIntBytes(msgbytes[start:])
i += intLen
if !fstore.Exists(fid) {
outbytes = append(outbytes, invalidForum...)
lastItem = i
continue
}
outbytes = append(outbytes, urlOpen...)
var urlBit = []byte(buildForumURL("", fid))
outbytes = append(outbytes, urlBit...)
outbytes = append(outbytes, urlOpen2...)
var fidBit = []byte("#fid-" + strconv.Itoa(fid))
outbytes = append(outbytes, fidBit...)
outbytes = append(outbytes, urlClose...)
lastItem = i
} else {
// TODO: Forum Shortcode Link
}
} else if msgbytes[i] == '@' {
//log.Print("IN @")
outbytes = append(outbytes, msgbytes[lastItem:i]...)
i++
start := i
uid, intLen := coerceIntBytes(msgbytes[start:])
i += intLen
menUser, err := users.Get(uid)
if err != nil {
outbytes = append(outbytes, invalidProfile...)
lastItem = i
continue
}
outbytes = append(outbytes, urlOpen...)
var urlBit = []byte(menUser.Link)
outbytes = append(outbytes, urlBit...)
outbytes = append(outbytes, bytesSinglequote...)
outbytes = append(outbytes, urlMention...)
outbytes = append(outbytes, bytesGreaterthan...)
var uidBit = []byte("@" + menUser.Name)
outbytes = append(outbytes, uidBit...)
outbytes = append(outbytes, urlClose...)
lastItem = i
//log.Print(string(msgbytes))
//log.Print(msgbytes)
//log.Print(msgbytes[lastItem - 1])
//log.Print("lastItem - 1",lastItem - 1)
//log.Print("msgbytes[lastItem]",msgbytes[lastItem])
//log.Print("lastItem",lastItem)
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
//log.Print("IN hfg")
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
if msgbytes[i+4] == 's' && msgbytes[i+5] == ':' && msgbytes[i+6] == '/' && msgbytes[i+7] == '/' {
// Do nothing
} else if msgbytes[i+4] == ':' && msgbytes[i+5] == '/' && msgbytes[i+6] == '/' {
// Do nothing
} else {
continue
}
} else if msgbytes[i+1] == 't' && msgbytes[i+2] == 'p' && msgbytes[i+3] == ':' && msgbytes[i+4] == '/' && msgbytes[i+5] == '/' {
// Do nothing
} else if msgbytes[i+1] == 'i' && msgbytes[i+2] == 't' && msgbytes[i+3] == ':' && msgbytes[i+4] == '/' && msgbytes[i+5] == '/' {
// Do nothing
} else {
continue
}
outbytes = append(outbytes, msgbytes[lastItem:i]...)
urlLen := partialURLBytesLen(msgbytes[i:])
if msgbytes[i+urlLen] != ' ' && msgbytes[i+urlLen] != 10 {
outbytes = append(outbytes, invalidURL...)
i += urlLen
continue
}
outbytes = append(outbytes, urlOpen...)
outbytes = append(outbytes, msgbytes[i:i+urlLen]...)
outbytes = append(outbytes, urlOpen2...)
outbytes = append(outbytes, msgbytes[i:i+urlLen]...)
outbytes = append(outbytes, urlClose...)
i += urlLen
lastItem = i
}
}
}
if lastItem != i && len(outbytes) != 0 {
//log.Print("lastItem:",msgbytes[lastItem])
//log.Print("lastItem index:")
//log.Print(lastItem)
//log.Print("i:")
//log.Print(i)
//log.Print("lastItem to end:")
//log.Print(msgbytes[lastItem:])
//log.Print("-----")
calclen := len(msgbytes) - 10
if calclen <= lastItem {
calclen = lastItem
}
outbytes = append(outbytes, msgbytes[lastItem:calclen]...)
msg = string(outbytes)
}
//log.Print(`"`+string(outbytes)+`"`)
//log.Print("msg",`"`+msg+`"`)
msg = strings.Replace(msg, "\n", "
", -1)
if sshooks["parse_assign"] != nil {
msg = runSshook("parse_assign", msg)
}
return msg
}
// TODO: Write a test for this
func regexParseMessage(msg string) string {
msg = strings.Replace(msg, ":)", "đ", -1)
msg = strings.Replace(msg, ":D", "đ", -1)
msg = strings.Replace(msg, ":P", "đ", -1)
msg = urlReg.ReplaceAllString(msg, "$2$3//$4")
msg = strings.Replace(msg, "\n", "
", -1)
if sshooks["parse_assign"] != nil {
msg = runSshook("parse_assign", msg)
}
return msg
}
// 6, 7, 8, 6, 7
// ftp://, http://, https:// git://, mailto: (not a URL, just here for length comparison purposes)
// TODO: Write a test for this
func validateURLBytes(data []byte) bool {
datalen := len(data)
i := 0
if datalen >= 6 {
if bytes.Equal(data[0:6], []byte("ftp://")) || bytes.Equal(data[0:6], []byte("git://")) {
i = 6
} else if datalen >= 7 && bytes.Equal(data[0:7], httpProtBytes) {
i = 7
} else if datalen >= 8 && bytes.Equal(data[0:8], []byte("https://")) {
i = 8
}
}
for ; datalen > i; i++ {
if data[i] != '\\' && data[i] != '_' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
return false
}
}
return true
}
// TODO: Write a test for this
func validatedURLBytes(data []byte) (url []byte) {
datalen := len(data)
i := 0
if datalen >= 6 {
if bytes.Equal(data[0:6], []byte("ftp://")) || bytes.Equal(data[0:6], []byte("git://")) {
i = 6
} else if datalen >= 7 && bytes.Equal(data[0:7], httpProtBytes) {
i = 7
} else if datalen >= 8 && bytes.Equal(data[0:8], []byte("https://")) {
i = 8
}
}
for ; datalen > i; i++ {
if data[i] != '\\' && data[i] != '_' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
return invalidURL
}
}
url = append(url, data...)
return url
}
// TODO: Write a test for this
func partialURLBytes(data []byte) (url []byte) {
datalen := len(data)
i := 0
end := datalen - 1
if datalen >= 6 {
if bytes.Equal(data[0:6], []byte("ftp://")) || bytes.Equal(data[0:6], []byte("git://")) {
i = 6
} else if datalen >= 7 && bytes.Equal(data[0:7], httpProtBytes) {
i = 7
} else if datalen >= 8 && bytes.Equal(data[0:8], []byte("https://")) {
i = 8
}
}
for ; end >= i; i++ {
if data[i] != '\\' && data[i] != '_' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
end = i
}
}
url = append(url, data[0:end]...)
return url
}
// TODO: Write a test for this
func partialURLBytesLen(data []byte) int {
datalen := len(data)
i := 0
if datalen >= 6 {
//log.Print(string(data[0:5]))
if bytes.Equal(data[0:6], []byte("ftp://")) || bytes.Equal(data[0:6], []byte("git://")) {
i = 6
} else if datalen >= 7 && bytes.Equal(data[0:7], httpProtBytes) {
i = 7
} else if datalen >= 8 && bytes.Equal(data[0:8], []byte("https://")) {
i = 8
}
}
for ; datalen > i; i++ {
if data[i] != '\\' && data[i] != '_' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
//log.Print("Bad Character:",data[i])
return i
}
}
//log.Print("Data Length:",datalen)
return datalen
}
// TODO: Write a test for this
func parseMediaBytes(data []byte) (protocol []byte, url []byte) {
datalen := len(data)
i := 0
if datalen >= 6 {
if bytes.Equal(data[0:6], []byte("ftp://")) || bytes.Equal(data[0:6], []byte("git://")) {
i = 6
protocol = data[0:2]
} else if datalen >= 7 && bytes.Equal(data[0:7], httpProtBytes) {
i = 7
protocol = []byte("http")
} else if datalen >= 8 && bytes.Equal(data[0:8], []byte("https://")) {
i = 8
protocol = []byte("https")
}
}
for ; datalen > i; i++ {
if data[i] != '\\' && data[i] != '_' && !(data[i] > 44 && data[i] < 58) && !(data[i] > 64 && data[i] < 91) && !(data[i] > 96 && data[i] < 123) {
return []byte(""), invalidURL
}
}
if len(protocol) == 0 {
protocol = []byte("http")
}
return protocol, data[i:]
}
// TODO: Write a test for this
func coerceIntBytes(data []byte) (res int, length int) {
if !(data[0] > 47 && data[0] < 58) {
return 0, 1
}
i := 0
for ; len(data) > i; i++ {
if !(data[i] > 47 && data[i] < 58) {
conv, err := strconv.Atoi(string(data[0:i]))
if err != nil {
return 0, i
}
return conv, i
}
}
conv, err := strconv.Atoi(string(data))
if err != nil {
return 0, i
}
return conv, i
}
// TODO: Write tests for this
func paginate(count int, perPage int, maxPages int) []int {
if count < perPage {
return []int{1}
}
var page int
var out []int
for current := 0; current < count; current += perPage {
page++
out = append(out, page)
if len(out) >= maxPages {
break
}
}
return out
}
// TODO: Write tests for this
func pageOffset(count int, page int, perPage int) (int, int, int) {
var offset int
lastPage := (count / perPage) + 1
if page > 1 {
offset = (perPage * page) - perPage
} else if page == -1 {
page = lastPage
offset = (perPage * page) - perPage
} else {
page = 1
}
// We don't want the offset to overflow the slices, if everything's in memory
if offset >= (count - 1) {
offset = 0
}
return offset, page, lastPage
}