PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/model/utils.go

https://gitlab.com/unofficial-mirrors/mattermost-platform
Go | 538 lines | 423 code | 98 blank | 17 comment | 104 complexity | be9b3e02bece094e566d5a9b8f256019 MD5 | raw file
  1. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  2. // See License.txt for license information.
  3. package model
  4. import (
  5. "bytes"
  6. "crypto/rand"
  7. "encoding/base32"
  8. "encoding/json"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "net"
  13. "net/http"
  14. "net/mail"
  15. "net/url"
  16. "reflect"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "testing"
  21. "time"
  22. "unicode"
  23. goi18n "github.com/nicksnyder/go-i18n/i18n"
  24. "github.com/pborman/uuid"
  25. )
  26. const (
  27. LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"
  28. UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  29. NUMBERS = "0123456789"
  30. SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
  31. )
  32. type StringInterface map[string]interface{}
  33. type StringMap map[string]string
  34. type StringArray []string
  35. var translateFunc goi18n.TranslateFunc = nil
  36. func AppErrorInit(t goi18n.TranslateFunc) {
  37. translateFunc = t
  38. }
  39. type AppError struct {
  40. Id string `json:"id"`
  41. Message string `json:"message"` // Message to be display to the end user without debugging information
  42. DetailedError string `json:"detailed_error"` // Internal error string to help the developer
  43. RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
  44. StatusCode int `json:"status_code,omitempty"` // The http status code
  45. Where string `json:"-"` // The function where it happened in the form of Struct.Func
  46. IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
  47. params map[string]interface{}
  48. }
  49. func (er *AppError) Error() string {
  50. return er.Where + ": " + er.Message + ", " + er.DetailedError
  51. }
  52. func (er *AppError) Translate(T goi18n.TranslateFunc) {
  53. if T == nil {
  54. er.Message = er.Id
  55. return
  56. }
  57. if er.params == nil {
  58. er.Message = T(er.Id)
  59. } else {
  60. er.Message = T(er.Id, er.params)
  61. }
  62. }
  63. func (er *AppError) SystemMessage(T goi18n.TranslateFunc) string {
  64. if er.params == nil {
  65. return T(er.Id)
  66. } else {
  67. return T(er.Id, er.params)
  68. }
  69. }
  70. func (er *AppError) ToJson() string {
  71. b, _ := json.Marshal(er)
  72. return string(b)
  73. }
  74. // AppErrorFromJson will decode the input and return an AppError
  75. func AppErrorFromJson(data io.Reader) *AppError {
  76. str := ""
  77. bytes, rerr := ioutil.ReadAll(data)
  78. if rerr != nil {
  79. str = rerr.Error()
  80. } else {
  81. str = string(bytes)
  82. }
  83. decoder := json.NewDecoder(strings.NewReader(str))
  84. var er AppError
  85. err := decoder.Decode(&er)
  86. if err == nil {
  87. return &er
  88. } else {
  89. return NewAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, "body: "+str, http.StatusInternalServerError)
  90. }
  91. }
  92. func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
  93. ap := &AppError{}
  94. ap.Id = id
  95. ap.params = params
  96. ap.Message = id
  97. ap.Where = where
  98. ap.DetailedError = details
  99. ap.StatusCode = status
  100. ap.IsOAuth = false
  101. ap.Translate(translateFunc)
  102. return ap
  103. }
  104. var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
  105. // NewId is a globally unique identifier. It is a [A-Z0-9] string 26
  106. // characters long. It is a UUID version 4 Guid that is zbased32 encoded
  107. // with the padding stripped off.
  108. func NewId() string {
  109. var b bytes.Buffer
  110. encoder := base32.NewEncoder(encoding, &b)
  111. encoder.Write(uuid.NewRandom())
  112. encoder.Close()
  113. b.Truncate(26) // removes the '==' padding
  114. return b.String()
  115. }
  116. func NewRandomString(length int) string {
  117. var b bytes.Buffer
  118. str := make([]byte, length+8)
  119. rand.Read(str)
  120. encoder := base32.NewEncoder(encoding, &b)
  121. encoder.Write(str)
  122. encoder.Close()
  123. b.Truncate(length) // removes the '==' padding
  124. return b.String()
  125. }
  126. // GetMillis is a convience method to get milliseconds since epoch.
  127. func GetMillis() int64 {
  128. return time.Now().UnixNano() / int64(time.Millisecond)
  129. }
  130. func CopyStringMap(originalMap map[string]string) map[string]string {
  131. copyMap := make(map[string]string)
  132. for k, v := range originalMap {
  133. copyMap[k] = v
  134. }
  135. return copyMap
  136. }
  137. // MapToJson converts a map to a json string
  138. func MapToJson(objmap map[string]string) string {
  139. b, _ := json.Marshal(objmap)
  140. return string(b)
  141. }
  142. // MapToJson converts a map to a json string
  143. func MapBoolToJson(objmap map[string]bool) string {
  144. b, _ := json.Marshal(objmap)
  145. return string(b)
  146. }
  147. // MapFromJson will decode the key/value pair map
  148. func MapFromJson(data io.Reader) map[string]string {
  149. decoder := json.NewDecoder(data)
  150. var objmap map[string]string
  151. if err := decoder.Decode(&objmap); err != nil {
  152. return make(map[string]string)
  153. } else {
  154. return objmap
  155. }
  156. }
  157. // MapFromJson will decode the key/value pair map
  158. func MapBoolFromJson(data io.Reader) map[string]bool {
  159. decoder := json.NewDecoder(data)
  160. var objmap map[string]bool
  161. if err := decoder.Decode(&objmap); err != nil {
  162. return make(map[string]bool)
  163. } else {
  164. return objmap
  165. }
  166. }
  167. func ArrayToJson(objmap []string) string {
  168. b, _ := json.Marshal(objmap)
  169. return string(b)
  170. }
  171. func ArrayFromJson(data io.Reader) []string {
  172. decoder := json.NewDecoder(data)
  173. var objmap []string
  174. if err := decoder.Decode(&objmap); err != nil {
  175. return make([]string, 0)
  176. } else {
  177. return objmap
  178. }
  179. }
  180. func ArrayFromInterface(data interface{}) []string {
  181. stringArray := []string{}
  182. dataArray, ok := data.([]interface{})
  183. if !ok {
  184. return stringArray
  185. }
  186. for _, v := range dataArray {
  187. if str, ok := v.(string); ok {
  188. stringArray = append(stringArray, str)
  189. }
  190. }
  191. return stringArray
  192. }
  193. func StringInterfaceToJson(objmap map[string]interface{}) string {
  194. b, _ := json.Marshal(objmap)
  195. return string(b)
  196. }
  197. func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
  198. decoder := json.NewDecoder(data)
  199. var objmap map[string]interface{}
  200. if err := decoder.Decode(&objmap); err != nil {
  201. return make(map[string]interface{})
  202. } else {
  203. return objmap
  204. }
  205. }
  206. func StringToJson(s string) string {
  207. b, _ := json.Marshal(s)
  208. return string(b)
  209. }
  210. func StringFromJson(data io.Reader) string {
  211. decoder := json.NewDecoder(data)
  212. var s string
  213. if err := decoder.Decode(&s); err != nil {
  214. return ""
  215. } else {
  216. return s
  217. }
  218. }
  219. func GetServerIpAddress() string {
  220. if addrs, err := net.InterfaceAddrs(); err != nil {
  221. return ""
  222. } else {
  223. for _, addr := range addrs {
  224. if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
  225. if ip.IP.To4() != nil {
  226. return ip.IP.String()
  227. }
  228. }
  229. }
  230. }
  231. return ""
  232. }
  233. func IsLower(s string) bool {
  234. return strings.ToLower(s) == s
  235. }
  236. func IsValidEmail(email string) bool {
  237. if !IsLower(email) {
  238. return false
  239. }
  240. if _, err := mail.ParseAddress(email); err == nil {
  241. return true
  242. }
  243. return false
  244. }
  245. var reservedName = []string{
  246. "signup",
  247. "login",
  248. "admin",
  249. "channel",
  250. "post",
  251. "api",
  252. "oauth",
  253. }
  254. func IsValidChannelIdentifier(s string) bool {
  255. if !IsValidAlphaNumHyphenUnderscore(s, true) {
  256. return false
  257. }
  258. if len(s) < CHANNEL_NAME_MIN_LENGTH {
  259. return false
  260. }
  261. return true
  262. }
  263. func IsValidAlphaNum(s string) bool {
  264. validAlphaNum := regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`)
  265. return validAlphaNum.MatchString(s)
  266. }
  267. func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool {
  268. if withFormat {
  269. validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`)
  270. return validAlphaNumHyphenUnderscore.MatchString(s)
  271. }
  272. validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
  273. return validSimpleAlphaNumHyphenUnderscore.MatchString(s)
  274. }
  275. func Etag(parts ...interface{}) string {
  276. etag := CurrentVersion
  277. for _, part := range parts {
  278. etag += fmt.Sprintf(".%v", part)
  279. }
  280. return etag
  281. }
  282. var validHashtag = regexp.MustCompile(`^(#\pL[\pL\d\-_.]*[\pL\d])$`)
  283. var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`)
  284. var hashtagStart = regexp.MustCompile(`^#{2,}`)
  285. var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`)
  286. func ParseHashtags(text string) (string, string) {
  287. words := strings.Fields(text)
  288. hashtagString := ""
  289. plainString := ""
  290. for _, word := range words {
  291. // trim off surrounding punctuation
  292. word = puncStart.ReplaceAllString(word, "")
  293. word = puncEnd.ReplaceAllString(word, "")
  294. // and remove extra pound #s
  295. word = hashtagStart.ReplaceAllString(word, "#")
  296. if validHashtag.MatchString(word) {
  297. hashtagString += " " + word
  298. } else {
  299. plainString += " " + word
  300. }
  301. }
  302. if len(hashtagString) > 1000 {
  303. hashtagString = hashtagString[:999]
  304. lastSpace := strings.LastIndex(hashtagString, " ")
  305. if lastSpace > -1 {
  306. hashtagString = hashtagString[:lastSpace]
  307. } else {
  308. hashtagString = ""
  309. }
  310. }
  311. return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
  312. }
  313. func IsFileExtImage(ext string) bool {
  314. ext = strings.ToLower(ext)
  315. for _, imgExt := range IMAGE_EXTENSIONS {
  316. if ext == imgExt {
  317. return true
  318. }
  319. }
  320. return false
  321. }
  322. func GetImageMimeType(ext string) string {
  323. ext = strings.ToLower(ext)
  324. if len(IMAGE_MIME_TYPES[ext]) == 0 {
  325. return "image"
  326. } else {
  327. return IMAGE_MIME_TYPES[ext]
  328. }
  329. }
  330. func ClearMentionTags(post string) string {
  331. post = strings.Replace(post, "<mention>", "", -1)
  332. post = strings.Replace(post, "</mention>", "", -1)
  333. return post
  334. }
  335. func IsValidHttpUrl(rawUrl string) bool {
  336. if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
  337. return false
  338. }
  339. if _, err := url.ParseRequestURI(rawUrl); err != nil {
  340. return false
  341. }
  342. return true
  343. }
  344. func IsValidTurnOrStunServer(rawUri string) bool {
  345. if strings.Index(rawUri, "turn:") != 0 && strings.Index(rawUri, "stun:") != 0 {
  346. return false
  347. }
  348. if _, err := url.ParseRequestURI(rawUri); err != nil {
  349. return false
  350. }
  351. return true
  352. }
  353. func IsSafeLink(link *string) bool {
  354. if link != nil {
  355. if IsValidHttpUrl(*link) {
  356. return true
  357. } else if strings.HasPrefix(*link, "/") {
  358. return true
  359. } else {
  360. return false
  361. }
  362. }
  363. return true
  364. }
  365. func IsValidWebsocketUrl(rawUrl string) bool {
  366. if strings.Index(rawUrl, "ws://") != 0 && strings.Index(rawUrl, "wss://") != 0 {
  367. return false
  368. }
  369. if _, err := url.ParseRequestURI(rawUrl); err != nil {
  370. return false
  371. }
  372. return true
  373. }
  374. func IsValidTrueOrFalseString(value string) bool {
  375. return value == "true" || value == "false"
  376. }
  377. func IsValidNumberString(value string) bool {
  378. if _, err := strconv.Atoi(value); err != nil {
  379. return false
  380. }
  381. return true
  382. }
  383. func IsValidId(value string) bool {
  384. if len(value) != 26 {
  385. return false
  386. }
  387. for _, r := range value {
  388. if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
  389. return false
  390. }
  391. }
  392. return true
  393. }
  394. // checkNowhereNil checks that the given interface value is not nil, and if a struct, that all of
  395. // its public fields are also nowhere nil
  396. func checkNowhereNil(t *testing.T, name string, value interface{}) bool {
  397. if value == nil {
  398. return false
  399. }
  400. v := reflect.ValueOf(value)
  401. switch v.Type().Kind() {
  402. case reflect.Ptr:
  403. if v.IsNil() {
  404. t.Logf("%s was nil", name)
  405. return false
  406. }
  407. return checkNowhereNil(t, fmt.Sprintf("(*%s)", name), v.Elem().Interface())
  408. case reflect.Map:
  409. if v.IsNil() {
  410. t.Logf("%s was nil", name)
  411. return false
  412. }
  413. // Don't check map values
  414. return true
  415. case reflect.Struct:
  416. nowhereNil := true
  417. for i := 0; i < v.NumField(); i++ {
  418. f := v.Field(i)
  419. // Ignore unexported fields
  420. if v.Type().Field(i).PkgPath != "" {
  421. continue
  422. }
  423. nowhereNil = nowhereNil && checkNowhereNil(t, fmt.Sprintf("%s.%s", name, v.Type().Field(i).Name), f.Interface())
  424. }
  425. return nowhereNil
  426. case reflect.Array:
  427. fallthrough
  428. case reflect.Chan:
  429. fallthrough
  430. case reflect.Func:
  431. fallthrough
  432. case reflect.Interface:
  433. fallthrough
  434. case reflect.UnsafePointer:
  435. t.Logf("unhandled field %s, type: %s", name, v.Type().Kind())
  436. return false
  437. default:
  438. return true
  439. }
  440. }