package main //import "fmt" import "sync" import "bytes" import "strings" import "strconv" import "regexp" import "html/template" 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 } /*type UnsafeExtData struct { items map[string]interface{} // Key: pluginname }*/ // TODO: Add a ExtDataHolder interface with methods for manipulating the contents? 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 Log struct { Action template.HTML IPAddress string DoneAt string } type PanelLogsPage struct { Title string CurrentUser User Header *HeaderVars Stats PanelStats Logs []Log 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) } 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) } 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.CascadeGet(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.CascadeGet(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 } 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) 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 } 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 } 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 } 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 } 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:] } 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 }