PageRenderTime 1604ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/model/utils.go

https://gitlab.com/auchalet/mattermost
Go | 422 lines | 337 code | 74 blank | 11 comment | 80 complexity | a9385eb96e54fe7a67517889caee1f29 MD5 | raw file
  1. // Copyright (c) 2015 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. "net/mail"
  12. "net/url"
  13. "regexp"
  14. "strings"
  15. "time"
  16. goi18n "github.com/nicksnyder/go-i18n/i18n"
  17. "github.com/pborman/uuid"
  18. )
  19. type StringInterface map[string]interface{}
  20. type StringMap map[string]string
  21. type StringArray []string
  22. type EncryptStringMap map[string]string
  23. type AppError struct {
  24. Id string `json:"id"`
  25. Message string `json:"message"` // Message to be display to the end user without debugging information
  26. DetailedError string `json:"detailed_error"` // Internal error string to help the developer
  27. RequestId string `json:"request_id"` // The RequestId that's also set in the header
  28. StatusCode int `json:"status_code"` // The http status code
  29. Where string `json:"-"` // The function where it happened in the form of Struct.Func
  30. IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific
  31. params map[string]interface{} `json:"-"`
  32. }
  33. func (er *AppError) Error() string {
  34. return er.Where + ": " + er.Message + ", " + er.DetailedError
  35. }
  36. func (er *AppError) Translate(T goi18n.TranslateFunc) {
  37. if er.params == nil {
  38. er.Message = T(er.Id)
  39. } else {
  40. er.Message = T(er.Id, er.params)
  41. }
  42. }
  43. func (er *AppError) SystemMessage(T goi18n.TranslateFunc) string {
  44. if er.params == nil {
  45. return T(er.Id)
  46. } else {
  47. return T(er.Id, er.params)
  48. }
  49. }
  50. func (er *AppError) ToJson() string {
  51. b, err := json.Marshal(er)
  52. if err != nil {
  53. return ""
  54. } else {
  55. return string(b)
  56. }
  57. }
  58. // AppErrorFromJson will decode the input and return an AppError
  59. func AppErrorFromJson(data io.Reader) *AppError {
  60. decoder := json.NewDecoder(data)
  61. var er AppError
  62. err := decoder.Decode(&er)
  63. if err == nil {
  64. return &er
  65. } else {
  66. return NewLocAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, err.Error())
  67. }
  68. }
  69. func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
  70. ap := &AppError{}
  71. ap.Id = id
  72. ap.params = params
  73. ap.Message = id
  74. ap.Where = where
  75. ap.DetailedError = details
  76. ap.StatusCode = 500
  77. ap.IsOAuth = false
  78. return ap
  79. }
  80. var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
  81. // NewId is a globally unique identifier. It is a [A-Z0-9] string 26
  82. // characters long. It is a UUID version 4 Guid that is zbased32 encoded
  83. // with the padding stripped off.
  84. func NewId() string {
  85. var b bytes.Buffer
  86. encoder := base32.NewEncoder(encoding, &b)
  87. encoder.Write(uuid.NewRandom())
  88. encoder.Close()
  89. b.Truncate(26) // removes the '==' padding
  90. return b.String()
  91. }
  92. func NewRandomString(length int) string {
  93. var b bytes.Buffer
  94. str := make([]byte, length+8)
  95. rand.Read(str)
  96. encoder := base32.NewEncoder(encoding, &b)
  97. encoder.Write(str)
  98. encoder.Close()
  99. b.Truncate(length) // removes the '==' padding
  100. return b.String()
  101. }
  102. // GetMillis is a convience method to get milliseconds since epoch.
  103. func GetMillis() int64 {
  104. return time.Now().UnixNano() / int64(time.Millisecond)
  105. }
  106. // MapToJson converts a map to a json string
  107. func MapToJson(objmap map[string]string) string {
  108. if b, err := json.Marshal(objmap); err != nil {
  109. return ""
  110. } else {
  111. return string(b)
  112. }
  113. }
  114. // MapFromJson will decode the key/value pair map
  115. func MapFromJson(data io.Reader) map[string]string {
  116. decoder := json.NewDecoder(data)
  117. var objmap map[string]string
  118. if err := decoder.Decode(&objmap); err != nil {
  119. return make(map[string]string)
  120. } else {
  121. return objmap
  122. }
  123. }
  124. func ArrayToJson(objmap []string) string {
  125. if b, err := json.Marshal(objmap); err != nil {
  126. return ""
  127. } else {
  128. return string(b)
  129. }
  130. }
  131. func ArrayFromJson(data io.Reader) []string {
  132. decoder := json.NewDecoder(data)
  133. var objmap []string
  134. if err := decoder.Decode(&objmap); err != nil {
  135. return make([]string, 0)
  136. } else {
  137. return objmap
  138. }
  139. }
  140. func StringInterfaceToJson(objmap map[string]interface{}) string {
  141. if b, err := json.Marshal(objmap); err != nil {
  142. return ""
  143. } else {
  144. return string(b)
  145. }
  146. }
  147. func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
  148. decoder := json.NewDecoder(data)
  149. var objmap map[string]interface{}
  150. if err := decoder.Decode(&objmap); err != nil {
  151. return make(map[string]interface{})
  152. } else {
  153. return objmap
  154. }
  155. }
  156. func StringToJson(s string) string {
  157. b, err := json.Marshal(s)
  158. if err != nil {
  159. return ""
  160. } else {
  161. return string(b)
  162. }
  163. }
  164. func StringFromJson(data io.Reader) string {
  165. decoder := json.NewDecoder(data)
  166. var s string
  167. if err := decoder.Decode(&s); err != nil {
  168. return ""
  169. } else {
  170. return s
  171. }
  172. }
  173. func IsLower(s string) bool {
  174. if strings.ToLower(s) == s {
  175. return true
  176. }
  177. return false
  178. }
  179. func IsValidEmail(email string) bool {
  180. if !IsLower(email) {
  181. return false
  182. }
  183. if _, err := mail.ParseAddress(email); err == nil {
  184. return true
  185. }
  186. return false
  187. }
  188. var reservedName = []string{
  189. "www",
  190. "web",
  191. "admin",
  192. "support",
  193. "notify",
  194. "test",
  195. "demo",
  196. "mail",
  197. "team",
  198. "channel",
  199. "internal",
  200. "localhost",
  201. "dockerhost",
  202. "stag",
  203. "post",
  204. "cluster",
  205. "api",
  206. "oauth",
  207. }
  208. var wwwStart = regexp.MustCompile(`^www`)
  209. var betaStart = regexp.MustCompile(`^beta`)
  210. var ciStart = regexp.MustCompile(`^ci`)
  211. func GetSubDomain(s string) (string, string) {
  212. s = strings.Replace(s, "http://", "", 1)
  213. s = strings.Replace(s, "https://", "", 1)
  214. match := wwwStart.MatchString(s)
  215. if match {
  216. return "", ""
  217. }
  218. match = betaStart.MatchString(s)
  219. if match {
  220. return "", ""
  221. }
  222. match = ciStart.MatchString(s)
  223. if match {
  224. return "", ""
  225. }
  226. parts := strings.Split(s, ".")
  227. if len(parts) != 3 {
  228. return "", ""
  229. }
  230. return parts[0], parts[1]
  231. }
  232. func IsValidChannelIdentifier(s string) bool {
  233. if !IsValidAlphaNum(s, true) {
  234. return false
  235. }
  236. if len(s) < 2 {
  237. return false
  238. }
  239. return true
  240. }
  241. var validAlphaNumUnderscore = regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`)
  242. var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`)
  243. func IsValidAlphaNum(s string, allowUnderscores bool) bool {
  244. var match bool
  245. if allowUnderscores {
  246. match = validAlphaNumUnderscore.MatchString(s)
  247. } else {
  248. match = validAlphaNum.MatchString(s)
  249. }
  250. if !match {
  251. return false
  252. }
  253. return true
  254. }
  255. func Etag(parts ...interface{}) string {
  256. etag := CurrentVersion
  257. for _, part := range parts {
  258. etag += fmt.Sprintf(".%v", part)
  259. }
  260. return etag
  261. }
  262. var validHashtag = regexp.MustCompile(`^(#[A-Za-zäöüÄÖÜß]+[A-Za-z0-9äöüÄÖÜß_\-]*[A-Za-z0-9äöüÄÖÜß])$`)
  263. var puncStart = regexp.MustCompile(`^[.,()&$!\?\[\]{}':;\\<>\-+=%^*|]+`)
  264. var hashtagStart = regexp.MustCompile(`^#{2,}`)
  265. var puncEnd = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^*|]+$`)
  266. var puncEndWildcard = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^|]+$`)
  267. func ParseHashtags(text string) (string, string) {
  268. words := strings.Fields(text)
  269. hashtagString := ""
  270. plainString := ""
  271. for _, word := range words {
  272. // trim off surrounding punctuation
  273. word = puncStart.ReplaceAllString(word, "")
  274. word = puncEnd.ReplaceAllString(word, "")
  275. // and remove extra pound #s
  276. word = hashtagStart.ReplaceAllString(word, "#")
  277. if validHashtag.MatchString(word) {
  278. hashtagString += " " + word
  279. } else {
  280. plainString += " " + word
  281. }
  282. }
  283. if len(hashtagString) > 1000 {
  284. hashtagString = hashtagString[:999]
  285. lastSpace := strings.LastIndex(hashtagString, " ")
  286. if lastSpace > -1 {
  287. hashtagString = hashtagString[:lastSpace]
  288. } else {
  289. hashtagString = ""
  290. }
  291. }
  292. return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
  293. }
  294. func IsFileExtImage(ext string) bool {
  295. ext = strings.ToLower(ext)
  296. for _, imgExt := range IMAGE_EXTENSIONS {
  297. if ext == imgExt {
  298. return true
  299. }
  300. }
  301. return false
  302. }
  303. func GetImageMimeType(ext string) string {
  304. ext = strings.ToLower(ext)
  305. if len(IMAGE_MIME_TYPES[ext]) == 0 {
  306. return "image"
  307. } else {
  308. return IMAGE_MIME_TYPES[ext]
  309. }
  310. }
  311. func ClearMentionTags(post string) string {
  312. post = strings.Replace(post, "<mention>", "", -1)
  313. post = strings.Replace(post, "</mention>", "", -1)
  314. return post
  315. }
  316. 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+_~\-\.%=&amp;]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
  317. var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
  318. var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
  319. func IsValidHttpUrl(rawUrl string) bool {
  320. if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
  321. return false
  322. }
  323. if _, err := url.ParseRequestURI(rawUrl); err != nil {
  324. return false
  325. }
  326. return true
  327. }
  328. func IsValidHttpsUrl(rawUrl string) bool {
  329. if strings.Index(rawUrl, "https://") != 0 {
  330. return false
  331. }
  332. if _, err := url.ParseRequestURI(rawUrl); err != nil {
  333. return false
  334. }
  335. return true
  336. }
  337. func IsSafeLink(link *string) bool {
  338. if link != nil {
  339. if IsValidHttpUrl(*link) {
  340. return true
  341. } else if strings.HasPrefix(*link, "/") {
  342. return true
  343. } else {
  344. return false
  345. }
  346. }
  347. return true
  348. }