mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
This cleans up the few NewLocAppError calls that crept in since the main bulk of them were removed, and finally removes the NewLocAppError function altogether.
499 lines
11 KiB
Go
499 lines
11 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"encoding/base32"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/mail"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
goi18n "github.com/nicksnyder/go-i18n/i18n"
|
|
"github.com/pborman/uuid"
|
|
)
|
|
|
|
const (
|
|
LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"
|
|
UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
NUMBERS = "0123456789"
|
|
SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
|
|
)
|
|
|
|
type StringInterface map[string]interface{}
|
|
type StringMap map[string]string
|
|
type StringArray []string
|
|
|
|
type AppError struct {
|
|
Id string `json:"id"`
|
|
Message string `json:"message"` // Message to be display to the end user without debugging information
|
|
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
|
RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
|
|
StatusCode int `json:"status_code,omitempty"` // The http status code
|
|
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
|
IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
|
|
params map[string]interface{}
|
|
}
|
|
|
|
func (er *AppError) Error() string {
|
|
return er.Where + ": " + er.Message + ", " + er.DetailedError
|
|
}
|
|
|
|
func (er *AppError) Translate(T goi18n.TranslateFunc) {
|
|
if er.params == nil {
|
|
er.Message = T(er.Id)
|
|
} else {
|
|
er.Message = T(er.Id, er.params)
|
|
}
|
|
}
|
|
|
|
func (er *AppError) SystemMessage(T goi18n.TranslateFunc) string {
|
|
if er.params == nil {
|
|
return T(er.Id)
|
|
} else {
|
|
return T(er.Id, er.params)
|
|
}
|
|
}
|
|
|
|
func (er *AppError) ToJson() string {
|
|
b, err := json.Marshal(er)
|
|
if err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
// AppErrorFromJson will decode the input and return an AppError
|
|
func AppErrorFromJson(data io.Reader) *AppError {
|
|
str := ""
|
|
bytes, rerr := ioutil.ReadAll(data)
|
|
if rerr != nil {
|
|
str = rerr.Error()
|
|
} else {
|
|
str = string(bytes)
|
|
}
|
|
|
|
decoder := json.NewDecoder(strings.NewReader(str))
|
|
var er AppError
|
|
err := decoder.Decode(&er)
|
|
if err == nil {
|
|
return &er
|
|
} else {
|
|
return NewAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, "body: "+str, http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
|
|
ap := &AppError{}
|
|
ap.Id = id
|
|
ap.params = params
|
|
ap.Message = id
|
|
ap.Where = where
|
|
ap.DetailedError = details
|
|
ap.StatusCode = status
|
|
ap.IsOAuth = false
|
|
return ap
|
|
}
|
|
|
|
var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
|
|
|
|
// NewId is a globally unique identifier. It is a [A-Z0-9] string 26
|
|
// characters long. It is a UUID version 4 Guid that is zbased32 encoded
|
|
// with the padding stripped off.
|
|
func NewId() string {
|
|
var b bytes.Buffer
|
|
encoder := base32.NewEncoder(encoding, &b)
|
|
encoder.Write(uuid.NewRandom())
|
|
encoder.Close()
|
|
b.Truncate(26) // removes the '==' padding
|
|
return b.String()
|
|
}
|
|
|
|
func NewRandomString(length int) string {
|
|
var b bytes.Buffer
|
|
str := make([]byte, length+8)
|
|
rand.Read(str)
|
|
encoder := base32.NewEncoder(encoding, &b)
|
|
encoder.Write(str)
|
|
encoder.Close()
|
|
b.Truncate(length) // removes the '==' padding
|
|
return b.String()
|
|
}
|
|
|
|
// GetMillis is a convience method to get milliseconds since epoch.
|
|
func GetMillis() int64 {
|
|
return time.Now().UnixNano() / int64(time.Millisecond)
|
|
}
|
|
|
|
// MapToJson converts a map to a json string
|
|
func MapToJson(objmap map[string]string) string {
|
|
if b, err := json.Marshal(objmap); err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
// MapToJson converts a map to a json string
|
|
func MapBoolToJson(objmap map[string]bool) string {
|
|
if b, err := json.Marshal(objmap); err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
// MapFromJson will decode the key/value pair map
|
|
func MapFromJson(data io.Reader) map[string]string {
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var objmap map[string]string
|
|
if err := decoder.Decode(&objmap); err != nil {
|
|
return make(map[string]string)
|
|
} else {
|
|
return objmap
|
|
}
|
|
}
|
|
|
|
// MapFromJson will decode the key/value pair map
|
|
func MapBoolFromJson(data io.Reader) map[string]bool {
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var objmap map[string]bool
|
|
if err := decoder.Decode(&objmap); err != nil {
|
|
return make(map[string]bool)
|
|
} else {
|
|
return objmap
|
|
}
|
|
}
|
|
|
|
func ArrayToJson(objmap []string) string {
|
|
if b, err := json.Marshal(objmap); err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
func ArrayFromJson(data io.Reader) []string {
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var objmap []string
|
|
if err := decoder.Decode(&objmap); err != nil {
|
|
return make([]string, 0)
|
|
} else {
|
|
return objmap
|
|
}
|
|
}
|
|
|
|
func ArrayFromInterface(data interface{}) []string {
|
|
stringArray := []string{}
|
|
|
|
dataArray, ok := data.([]interface{})
|
|
if !ok {
|
|
return stringArray
|
|
}
|
|
|
|
for _, v := range dataArray {
|
|
if str, ok := v.(string); ok {
|
|
stringArray = append(stringArray, str)
|
|
}
|
|
}
|
|
|
|
return stringArray
|
|
}
|
|
|
|
func StringInterfaceToJson(objmap map[string]interface{}) string {
|
|
if b, err := json.Marshal(objmap); err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var objmap map[string]interface{}
|
|
if err := decoder.Decode(&objmap); err != nil {
|
|
return make(map[string]interface{})
|
|
} else {
|
|
return objmap
|
|
}
|
|
}
|
|
|
|
func StringToJson(s string) string {
|
|
b, err := json.Marshal(s)
|
|
if err != nil {
|
|
return ""
|
|
} else {
|
|
return string(b)
|
|
}
|
|
}
|
|
|
|
func StringFromJson(data io.Reader) string {
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var s string
|
|
if err := decoder.Decode(&s); err != nil {
|
|
return ""
|
|
} else {
|
|
return s
|
|
}
|
|
}
|
|
|
|
func GetServerIpAddress() string {
|
|
if addrs, err := net.InterfaceAddrs(); err != nil {
|
|
return ""
|
|
} else {
|
|
for _, addr := range addrs {
|
|
|
|
if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
|
|
if ip.IP.To4() != nil {
|
|
return ip.IP.String()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func IsLower(s string) bool {
|
|
if strings.ToLower(s) == s {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func IsValidEmail(email string) bool {
|
|
|
|
if !IsLower(email) {
|
|
return false
|
|
}
|
|
|
|
if _, err := mail.ParseAddress(email); err == nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
var reservedName = []string{
|
|
"signup",
|
|
"login",
|
|
"admin",
|
|
"channel",
|
|
"post",
|
|
"api",
|
|
"oauth",
|
|
}
|
|
|
|
func IsValidChannelIdentifier(s string) bool {
|
|
|
|
if !IsValidAlphaNumHyphenUnderscore(s, true) {
|
|
return false
|
|
}
|
|
|
|
if len(s) < CHANNEL_NAME_MIN_LENGTH {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidAlphaNum(s string) bool {
|
|
validAlphaNum := regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`)
|
|
|
|
return validAlphaNum.MatchString(s)
|
|
}
|
|
|
|
func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool {
|
|
if withFormat {
|
|
validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`)
|
|
return validAlphaNumHyphenUnderscore.MatchString(s)
|
|
}
|
|
|
|
validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
|
|
return validSimpleAlphaNumHyphenUnderscore.MatchString(s)
|
|
}
|
|
|
|
func Etag(parts ...interface{}) string {
|
|
|
|
etag := CurrentVersion
|
|
|
|
for _, part := range parts {
|
|
etag += fmt.Sprintf(".%v", part)
|
|
}
|
|
|
|
return etag
|
|
}
|
|
|
|
var validHashtag = regexp.MustCompile(`^(#\pL[\pL\d\-_.]*[\pL\d])$`)
|
|
var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`)
|
|
var hashtagStart = regexp.MustCompile(`^#{2,}`)
|
|
var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`)
|
|
|
|
func ParseHashtags(text string) (string, string) {
|
|
words := strings.Fields(text)
|
|
|
|
hashtagString := ""
|
|
plainString := ""
|
|
for _, word := range words {
|
|
// trim off surrounding punctuation
|
|
word = puncStart.ReplaceAllString(word, "")
|
|
word = puncEnd.ReplaceAllString(word, "")
|
|
|
|
// and remove extra pound #s
|
|
word = hashtagStart.ReplaceAllString(word, "#")
|
|
|
|
if validHashtag.MatchString(word) {
|
|
hashtagString += " " + word
|
|
} else {
|
|
plainString += " " + word
|
|
}
|
|
}
|
|
|
|
if len(hashtagString) > 1000 {
|
|
hashtagString = hashtagString[:999]
|
|
lastSpace := strings.LastIndex(hashtagString, " ")
|
|
if lastSpace > -1 {
|
|
hashtagString = hashtagString[:lastSpace]
|
|
} else {
|
|
hashtagString = ""
|
|
}
|
|
}
|
|
|
|
return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
|
|
}
|
|
|
|
func IsFileExtImage(ext string) bool {
|
|
ext = strings.ToLower(ext)
|
|
for _, imgExt := range IMAGE_EXTENSIONS {
|
|
if ext == imgExt {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func GetImageMimeType(ext string) string {
|
|
ext = strings.ToLower(ext)
|
|
if len(IMAGE_MIME_TYPES[ext]) == 0 {
|
|
return "image"
|
|
} else {
|
|
return IMAGE_MIME_TYPES[ext]
|
|
}
|
|
}
|
|
|
|
func ClearMentionTags(post string) string {
|
|
post = strings.Replace(post, "<mention>", "", -1)
|
|
post = strings.Replace(post, "</mention>", "", -1)
|
|
return post
|
|
}
|
|
|
|
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
|
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
|
|
|
|
func IsValidHttpUrl(rawUrl string) bool {
|
|
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
|
|
return false
|
|
}
|
|
|
|
if _, err := url.ParseRequestURI(rawUrl); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidHttpsUrl(rawUrl string) bool {
|
|
if strings.Index(rawUrl, "https://") != 0 {
|
|
return false
|
|
}
|
|
|
|
if _, err := url.ParseRequestURI(rawUrl); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidTurnOrStunServer(rawUri string) bool {
|
|
if strings.Index(rawUri, "turn:") != 0 && strings.Index(rawUri, "stun:") != 0 {
|
|
return false
|
|
}
|
|
|
|
if _, err := url.ParseRequestURI(rawUri); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsSafeLink(link *string) bool {
|
|
if link != nil {
|
|
if IsValidHttpUrl(*link) {
|
|
return true
|
|
} else if strings.HasPrefix(*link, "/") {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidWebsocketUrl(rawUrl string) bool {
|
|
if strings.Index(rawUrl, "ws://") != 0 && strings.Index(rawUrl, "wss://") != 0 {
|
|
return false
|
|
}
|
|
|
|
if _, err := url.ParseRequestURI(rawUrl); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidTrueOrFalseString(value string) bool {
|
|
return value == "true" || value == "false"
|
|
}
|
|
|
|
func IsValidNumberString(value string) bool {
|
|
if _, err := strconv.Atoi(value); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func IsValidId(value string) bool {
|
|
if len(value) != 26 {
|
|
return false
|
|
}
|
|
|
|
for _, r := range value {
|
|
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|