You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

tool.go 11 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package base
  5. import (
  6. "crypto/hmac"
  7. "crypto/md5"
  8. "crypto/rand"
  9. "crypto/sha1"
  10. "encoding/base64"
  11. "encoding/hex"
  12. "fmt"
  13. "hash"
  14. "html/template"
  15. "math"
  16. "strings"
  17. "time"
  18. "github.com/Unknwon/com"
  19. "github.com/Unknwon/i18n"
  20. "github.com/microcosm-cc/bluemonday"
  21. "github.com/gogits/gogs/modules/avatar"
  22. "github.com/gogits/gogs/modules/setting"
  23. )
  24. var Sanitizer = bluemonday.UGCPolicy()
  25. func init() {
  26. Sanitizer.AllowAttrs("class").OnElements("code")
  27. }
  28. // Encode string to md5 hex value.
  29. func EncodeMd5(str string) string {
  30. m := md5.New()
  31. m.Write([]byte(str))
  32. return hex.EncodeToString(m.Sum(nil))
  33. }
  34. // Encode string to sha1 hex value.
  35. func EncodeSha1(str string) string {
  36. h := sha1.New()
  37. h.Write([]byte(str))
  38. return hex.EncodeToString(h.Sum(nil))
  39. }
  40. func BasicAuthDecode(encoded string) (string, string, error) {
  41. s, err := base64.StdEncoding.DecodeString(encoded)
  42. if err != nil {
  43. return "", "", err
  44. }
  45. auth := strings.SplitN(string(s), ":", 2)
  46. return auth[0], auth[1], nil
  47. }
  48. func BasicAuthEncode(username, password string) string {
  49. return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
  50. }
  51. // GetRandomString generate random string by specify chars.
  52. func GetRandomString(n int, alphabets ...byte) string {
  53. const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  54. var bytes = make([]byte, n)
  55. rand.Read(bytes)
  56. for i, b := range bytes {
  57. if len(alphabets) == 0 {
  58. bytes[i] = alphanum[b%byte(len(alphanum))]
  59. } else {
  60. bytes[i] = alphabets[b%byte(len(alphabets))]
  61. }
  62. }
  63. return string(bytes)
  64. }
  65. // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
  66. func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
  67. prf := hmac.New(h, password)
  68. hashLen := prf.Size()
  69. numBlocks := (keyLen + hashLen - 1) / hashLen
  70. var buf [4]byte
  71. dk := make([]byte, 0, numBlocks*hashLen)
  72. U := make([]byte, hashLen)
  73. for block := 1; block <= numBlocks; block++ {
  74. // N.B.: || means concatenation, ^ means XOR
  75. // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
  76. // U_1 = PRF(password, salt || uint(i))
  77. prf.Reset()
  78. prf.Write(salt)
  79. buf[0] = byte(block >> 24)
  80. buf[1] = byte(block >> 16)
  81. buf[2] = byte(block >> 8)
  82. buf[3] = byte(block)
  83. prf.Write(buf[:4])
  84. dk = prf.Sum(dk)
  85. T := dk[len(dk)-hashLen:]
  86. copy(U, T)
  87. // U_n = PRF(password, U_(n-1))
  88. for n := 2; n <= iter; n++ {
  89. prf.Reset()
  90. prf.Write(U)
  91. U = U[:0]
  92. U = prf.Sum(U)
  93. for x := range U {
  94. T[x] ^= U[x]
  95. }
  96. }
  97. }
  98. return dk[:keyLen]
  99. }
  100. // verify time limit code
  101. func VerifyTimeLimitCode(data string, minutes int, code string) bool {
  102. if len(code) <= 18 {
  103. return false
  104. }
  105. // split code
  106. start := code[:12]
  107. lives := code[12:18]
  108. if d, err := com.StrTo(lives).Int(); err == nil {
  109. minutes = d
  110. }
  111. // right active code
  112. retCode := CreateTimeLimitCode(data, minutes, start)
  113. if retCode == code && minutes > 0 {
  114. // check time is expired or not
  115. before, _ := time.ParseInLocation("200601021504", start, time.Local)
  116. now := time.Now()
  117. if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
  118. return true
  119. }
  120. }
  121. return false
  122. }
  123. const TimeLimitCodeLength = 12 + 6 + 40
  124. // create a time limit code
  125. // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
  126. func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
  127. format := "200601021504"
  128. var start, end time.Time
  129. var startStr, endStr string
  130. if startInf == nil {
  131. // Use now time create code
  132. start = time.Now()
  133. startStr = start.Format(format)
  134. } else {
  135. // use start string create code
  136. startStr = startInf.(string)
  137. start, _ = time.ParseInLocation(format, startStr, time.Local)
  138. startStr = start.Format(format)
  139. }
  140. end = start.Add(time.Minute * time.Duration(minutes))
  141. endStr = end.Format(format)
  142. // create sha1 encode string
  143. sh := sha1.New()
  144. sh.Write([]byte(data + setting.SecretKey + startStr + endStr + com.ToStr(minutes)))
  145. encoded := hex.EncodeToString(sh.Sum(nil))
  146. code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
  147. return code
  148. }
  149. // AvatarLink returns avatar link by given e-mail.
  150. func AvatarLink(email string) string {
  151. if setting.DisableGravatar || setting.OfflineMode {
  152. return setting.AppSubUrl + "/img/avatar_default.jpg"
  153. }
  154. gravatarHash := avatar.HashEmail(email)
  155. if setting.Service.EnableCacheAvatar {
  156. return setting.AppSubUrl + "/avatar/" + gravatarHash
  157. }
  158. return setting.GravatarSource + gravatarHash
  159. }
  160. // Seconds-based time units
  161. const (
  162. Minute = 60
  163. Hour = 60 * Minute
  164. Day = 24 * Hour
  165. Week = 7 * Day
  166. Month = 30 * Day
  167. Year = 12 * Month
  168. )
  169. func computeTimeDiff(diff int64) (int64, string) {
  170. diffStr := ""
  171. switch {
  172. case diff <= 0:
  173. diff = 0
  174. diffStr = "now"
  175. case diff < 2:
  176. diff = 0
  177. diffStr = "1 second"
  178. case diff < 1*Minute:
  179. diffStr = fmt.Sprintf("%d seconds", diff)
  180. diff = 0
  181. case diff < 2*Minute:
  182. diff -= 1 * Minute
  183. diffStr = "1 minute"
  184. case diff < 1*Hour:
  185. diffStr = fmt.Sprintf("%d minutes", diff/Minute)
  186. diff -= diff / Minute * Minute
  187. case diff < 2*Hour:
  188. diff -= 1 * Hour
  189. diffStr = "1 hour"
  190. case diff < 1*Day:
  191. diffStr = fmt.Sprintf("%d hours", diff/Hour)
  192. diff -= diff / Hour * Hour
  193. case diff < 2*Day:
  194. diff -= 1 * Day
  195. diffStr = "1 day"
  196. case diff < 1*Week:
  197. diffStr = fmt.Sprintf("%d days", diff/Day)
  198. diff -= diff / Day * Day
  199. case diff < 2*Week:
  200. diff -= 1 * Week
  201. diffStr = "1 week"
  202. case diff < 1*Month:
  203. diffStr = fmt.Sprintf("%d weeks", diff/Week)
  204. diff -= diff / Week * Week
  205. case diff < 2*Month:
  206. diff -= 1 * Month
  207. diffStr = "1 month"
  208. case diff < 1*Year:
  209. diffStr = fmt.Sprintf("%d months", diff/Month)
  210. diff -= diff / Month * Month
  211. case diff < 2*Year:
  212. diff -= 1 * Year
  213. diffStr = "1 year"
  214. default:
  215. diffStr = fmt.Sprintf("%d years", diff/Year)
  216. diff = 0
  217. }
  218. return diff, diffStr
  219. }
  220. // TimeSincePro calculates the time interval and generate full user-friendly string.
  221. func TimeSincePro(then time.Time) string {
  222. now := time.Now()
  223. diff := now.Unix() - then.Unix()
  224. if then.After(now) {
  225. return "future"
  226. }
  227. var timeStr, diffStr string
  228. for {
  229. if diff == 0 {
  230. break
  231. }
  232. diff, diffStr = computeTimeDiff(diff)
  233. timeStr += ", " + diffStr
  234. }
  235. return strings.TrimPrefix(timeStr, ", ")
  236. }
  237. func timeSince(then time.Time, lang string) string {
  238. now := time.Now()
  239. lbl := i18n.Tr(lang, "tool.ago")
  240. diff := now.Unix() - then.Unix()
  241. if then.After(now) {
  242. lbl = i18n.Tr(lang, "tool.from_now")
  243. diff = then.Unix() - now.Unix()
  244. }
  245. switch {
  246. case diff <= 0:
  247. return i18n.Tr(lang, "tool.now")
  248. case diff <= 2:
  249. return i18n.Tr(lang, "tool.1s", lbl)
  250. case diff < 1*Minute:
  251. return i18n.Tr(lang, "tool.seconds", diff, lbl)
  252. case diff < 2*Minute:
  253. return i18n.Tr(lang, "tool.1m", lbl)
  254. case diff < 1*Hour:
  255. return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl)
  256. case diff < 2*Hour:
  257. return i18n.Tr(lang, "tool.1h", lbl)
  258. case diff < 1*Day:
  259. return i18n.Tr(lang, "tool.hours", diff/Hour, lbl)
  260. case diff < 2*Day:
  261. return i18n.Tr(lang, "tool.1d", lbl)
  262. case diff < 1*Week:
  263. return i18n.Tr(lang, "tool.days", diff/Day, lbl)
  264. case diff < 2*Week:
  265. return i18n.Tr(lang, "tool.1w", lbl)
  266. case diff < 1*Month:
  267. return i18n.Tr(lang, "tool.weeks", diff/Week, lbl)
  268. case diff < 2*Month:
  269. return i18n.Tr(lang, "tool.1mon", lbl)
  270. case diff < 1*Year:
  271. return i18n.Tr(lang, "tool.months", diff/Month, lbl)
  272. case diff < 2*Year:
  273. return i18n.Tr(lang, "tool.1y", lbl)
  274. default:
  275. return i18n.Tr(lang, "tool.years", diff/Year, lbl)
  276. }
  277. }
  278. func RawTimeSince(t time.Time, lang string) string {
  279. return timeSince(t, lang)
  280. }
  281. // TimeSince calculates the time interval and generate user-friendly string.
  282. func TimeSince(t time.Time, lang string) template.HTML {
  283. return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang)))
  284. }
  285. const (
  286. Byte = 1
  287. KByte = Byte * 1024
  288. MByte = KByte * 1024
  289. GByte = MByte * 1024
  290. TByte = GByte * 1024
  291. PByte = TByte * 1024
  292. EByte = PByte * 1024
  293. )
  294. var bytesSizeTable = map[string]uint64{
  295. "b": Byte,
  296. "kb": KByte,
  297. "mb": MByte,
  298. "gb": GByte,
  299. "tb": TByte,
  300. "pb": PByte,
  301. "eb": EByte,
  302. }
  303. func logn(n, b float64) float64 {
  304. return math.Log(n) / math.Log(b)
  305. }
  306. func humanateBytes(s uint64, base float64, sizes []string) string {
  307. if s < 10 {
  308. return fmt.Sprintf("%dB", s)
  309. }
  310. e := math.Floor(logn(float64(s), base))
  311. suffix := sizes[int(e)]
  312. val := float64(s) / math.Pow(base, math.Floor(e))
  313. f := "%.0f"
  314. if val < 10 {
  315. f = "%.1f"
  316. }
  317. return fmt.Sprintf(f+"%s", val, suffix)
  318. }
  319. // FileSize calculates the file size and generate user-friendly string.
  320. func FileSize(s int64) string {
  321. sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
  322. return humanateBytes(uint64(s), 1024, sizes)
  323. }
  324. // Subtract deals with subtraction of all types of number.
  325. func Subtract(left interface{}, right interface{}) interface{} {
  326. var rleft, rright int64
  327. var fleft, fright float64
  328. var isInt bool = true
  329. switch left.(type) {
  330. case int:
  331. rleft = int64(left.(int))
  332. case int8:
  333. rleft = int64(left.(int8))
  334. case int16:
  335. rleft = int64(left.(int16))
  336. case int32:
  337. rleft = int64(left.(int32))
  338. case int64:
  339. rleft = left.(int64)
  340. case float32:
  341. fleft = float64(left.(float32))
  342. isInt = false
  343. case float64:
  344. fleft = left.(float64)
  345. isInt = false
  346. }
  347. switch right.(type) {
  348. case int:
  349. rright = int64(right.(int))
  350. case int8:
  351. rright = int64(right.(int8))
  352. case int16:
  353. rright = int64(right.(int16))
  354. case int32:
  355. rright = int64(right.(int32))
  356. case int64:
  357. rright = right.(int64)
  358. case float32:
  359. fright = float64(left.(float32))
  360. isInt = false
  361. case float64:
  362. fleft = left.(float64)
  363. isInt = false
  364. }
  365. if isInt {
  366. return rleft - rright
  367. } else {
  368. return fleft + float64(rleft) - (fright + float64(rright))
  369. }
  370. }
  371. // StringsToInt64s converts a slice of string to a slice of int64.
  372. func StringsToInt64s(strs []string) []int64 {
  373. ints := make([]int64, len(strs))
  374. for i := range strs {
  375. ints[i] = com.StrTo(strs[i]).MustInt64()
  376. }
  377. return ints
  378. }
  379. // Int64sToStrings converts a slice of int64 to a slice of string.
  380. func Int64sToStrings(ints []int64) []string {
  381. strs := make([]string, len(ints))
  382. for i := range ints {
  383. strs[i] = com.ToStr(ints[i])
  384. }
  385. return strs
  386. }
  387. // Int64sToMap converts a slice of int64 to a int64 map.
  388. func Int64sToMap(ints []int64) map[int64]bool {
  389. m := make(map[int64]bool)
  390. for _, i := range ints {
  391. m[i] = true
  392. }
  393. return m
  394. }