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 12 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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/hex"
  11. "fmt"
  12. "hash"
  13. "html/template"
  14. "math"
  15. "regexp"
  16. "strings"
  17. "time"
  18. "github.com/Unknwon/com"
  19. "github.com/Unknwon/i18n"
  20. "github.com/gogits/gogs/modules/setting"
  21. )
  22. // Encode string to md5 hex value.
  23. func EncodeMd5(str string) string {
  24. m := md5.New()
  25. m.Write([]byte(str))
  26. return hex.EncodeToString(m.Sum(nil))
  27. }
  28. // GetRandomString generate random string by specify chars.
  29. func GetRandomString(n int, alphabets ...byte) string {
  30. const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  31. var bytes = make([]byte, n)
  32. rand.Read(bytes)
  33. for i, b := range bytes {
  34. if len(alphabets) == 0 {
  35. bytes[i] = alphanum[b%byte(len(alphanum))]
  36. } else {
  37. bytes[i] = alphabets[b%byte(len(alphabets))]
  38. }
  39. }
  40. return string(bytes)
  41. }
  42. // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
  43. func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
  44. prf := hmac.New(h, password)
  45. hashLen := prf.Size()
  46. numBlocks := (keyLen + hashLen - 1) / hashLen
  47. var buf [4]byte
  48. dk := make([]byte, 0, numBlocks*hashLen)
  49. U := make([]byte, hashLen)
  50. for block := 1; block <= numBlocks; block++ {
  51. // N.B.: || means concatenation, ^ means XOR
  52. // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
  53. // U_1 = PRF(password, salt || uint(i))
  54. prf.Reset()
  55. prf.Write(salt)
  56. buf[0] = byte(block >> 24)
  57. buf[1] = byte(block >> 16)
  58. buf[2] = byte(block >> 8)
  59. buf[3] = byte(block)
  60. prf.Write(buf[:4])
  61. dk = prf.Sum(dk)
  62. T := dk[len(dk)-hashLen:]
  63. copy(U, T)
  64. // U_n = PRF(password, U_(n-1))
  65. for n := 2; n <= iter; n++ {
  66. prf.Reset()
  67. prf.Write(U)
  68. U = U[:0]
  69. U = prf.Sum(U)
  70. for x := range U {
  71. T[x] ^= U[x]
  72. }
  73. }
  74. }
  75. return dk[:keyLen]
  76. }
  77. // verify time limit code
  78. func VerifyTimeLimitCode(data string, minutes int, code string) bool {
  79. if len(code) <= 18 {
  80. return false
  81. }
  82. // split code
  83. start := code[:12]
  84. lives := code[12:18]
  85. if d, err := com.StrTo(lives).Int(); err == nil {
  86. minutes = d
  87. }
  88. // right active code
  89. retCode := CreateTimeLimitCode(data, minutes, start)
  90. if retCode == code && minutes > 0 {
  91. // check time is expired or not
  92. before, _ := DateParse(start, "YmdHi")
  93. now := time.Now()
  94. if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
  95. return true
  96. }
  97. }
  98. return false
  99. }
  100. const TimeLimitCodeLength = 12 + 6 + 40
  101. // create a time limit code
  102. // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
  103. func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
  104. format := "YmdHi"
  105. var start, end time.Time
  106. var startStr, endStr string
  107. if startInf == nil {
  108. // Use now time create code
  109. start = time.Now()
  110. startStr = DateFormat(start, format)
  111. } else {
  112. // use start string create code
  113. startStr = startInf.(string)
  114. start, _ = DateParse(startStr, format)
  115. startStr = DateFormat(start, format)
  116. }
  117. end = start.Add(time.Minute * time.Duration(minutes))
  118. endStr = DateFormat(end, format)
  119. // create sha1 encode string
  120. sh := sha1.New()
  121. sh.Write([]byte(data + setting.SecretKey + startStr + endStr + com.ToStr(minutes)))
  122. encoded := hex.EncodeToString(sh.Sum(nil))
  123. code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
  124. return code
  125. }
  126. // AvatarLink returns avatar link by given e-mail.
  127. func AvatarLink(email string) string {
  128. if setting.DisableGravatar {
  129. return setting.AppSubUrl + "/img/avatar_default.jpg"
  130. } else if setting.Service.EnableCacheAvatar {
  131. return setting.AppSubUrl + "/avatar/" + EncodeMd5(email)
  132. }
  133. return "//1.gravatar.com/avatar/" + EncodeMd5(email)
  134. }
  135. // Seconds-based time units
  136. const (
  137. Minute = 60
  138. Hour = 60 * Minute
  139. Day = 24 * Hour
  140. Week = 7 * Day
  141. Month = 30 * Day
  142. Year = 12 * Month
  143. )
  144. func computeTimeDiff(diff int64) (int64, string) {
  145. diffStr := ""
  146. switch {
  147. case diff <= 0:
  148. diff = 0
  149. diffStr = "now"
  150. case diff < 2:
  151. diff = 0
  152. diffStr = "1 second"
  153. case diff < 1*Minute:
  154. diffStr = fmt.Sprintf("%d seconds", diff)
  155. diff = 0
  156. case diff < 2*Minute:
  157. diff -= 1 * Minute
  158. diffStr = "1 minute"
  159. case diff < 1*Hour:
  160. diffStr = fmt.Sprintf("%d minutes", diff/Minute)
  161. diff -= diff / Minute * Minute
  162. case diff < 2*Hour:
  163. diff -= 1 * Hour
  164. diffStr = "1 hour"
  165. case diff < 1*Day:
  166. diffStr = fmt.Sprintf("%d hours", diff/Hour)
  167. diff -= diff / Hour * Hour
  168. case diff < 2*Day:
  169. diff -= 1 * Day
  170. diffStr = "1 day"
  171. case diff < 1*Week:
  172. diffStr = fmt.Sprintf("%d days", diff/Day)
  173. diff -= diff / Day * Day
  174. case diff < 2*Week:
  175. diff -= 1 * Week
  176. diffStr = "1 week"
  177. case diff < 1*Month:
  178. diffStr = fmt.Sprintf("%d weeks", diff/Week)
  179. diff -= diff / Week * Week
  180. case diff < 2*Month:
  181. diff -= 1 * Month
  182. diffStr = "1 month"
  183. case diff < 1*Year:
  184. diffStr = fmt.Sprintf("%d months", diff/Month)
  185. diff -= diff / Month * Month
  186. case diff < 2*Year:
  187. diff -= 1 * Year
  188. diffStr = "1 year"
  189. default:
  190. diffStr = fmt.Sprintf("%d years", diff/Year)
  191. diff = 0
  192. }
  193. return diff, diffStr
  194. }
  195. // TimeSincePro calculates the time interval and generate full user-friendly string.
  196. func TimeSincePro(then time.Time) string {
  197. now := time.Now()
  198. diff := now.Unix() - then.Unix()
  199. if then.After(now) {
  200. return "future"
  201. }
  202. var timeStr, diffStr string
  203. for {
  204. if diff == 0 {
  205. break
  206. }
  207. diff, diffStr = computeTimeDiff(diff)
  208. timeStr += ", " + diffStr
  209. }
  210. return strings.TrimPrefix(timeStr, ", ")
  211. }
  212. func timeSince(then time.Time, lang string) string {
  213. now := time.Now()
  214. lbl := i18n.Tr(lang, "tool.ago")
  215. diff := now.Unix() - then.Unix()
  216. if then.After(now) {
  217. lbl = i18n.Tr(lang, "tool.from_now")
  218. diff = then.Unix() - now.Unix()
  219. }
  220. switch {
  221. case diff <= 0:
  222. return i18n.Tr(lang, "tool.now")
  223. case diff <= 2:
  224. return i18n.Tr(lang, "tool.1s", lbl)
  225. case diff < 1*Minute:
  226. return i18n.Tr(lang, "tool.seconds", diff, lbl)
  227. case diff < 2*Minute:
  228. return i18n.Tr(lang, "tool.1m", lbl)
  229. case diff < 1*Hour:
  230. return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl)
  231. case diff < 2*Hour:
  232. return i18n.Tr(lang, "tool.1h", lbl)
  233. case diff < 1*Day:
  234. return i18n.Tr(lang, "tool.hours", diff/Hour, lbl)
  235. case diff < 2*Day:
  236. return i18n.Tr(lang, "tool.1d", lbl)
  237. case diff < 1*Week:
  238. return i18n.Tr(lang, "tool.days", diff/Day, lbl)
  239. case diff < 2*Week:
  240. return i18n.Tr(lang, "tool.1w", lbl)
  241. case diff < 1*Month:
  242. return i18n.Tr(lang, "tool.weeks", diff/Week, lbl)
  243. case diff < 2*Month:
  244. return i18n.Tr(lang, "tool.1mon", lbl)
  245. case diff < 1*Year:
  246. return i18n.Tr(lang, "tool.months", diff/Month, lbl)
  247. case diff < 2*Year:
  248. return i18n.Tr(lang, "tool.1y", lbl)
  249. default:
  250. return i18n.Tr(lang, "tool.years", diff/Year, lbl)
  251. }
  252. }
  253. // TimeSince calculates the time interval and generate user-friendly string.
  254. func TimeSince(t time.Time, lang string) template.HTML {
  255. return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang)))
  256. }
  257. const (
  258. Byte = 1
  259. KByte = Byte * 1024
  260. MByte = KByte * 1024
  261. GByte = MByte * 1024
  262. TByte = GByte * 1024
  263. PByte = TByte * 1024
  264. EByte = PByte * 1024
  265. )
  266. var bytesSizeTable = map[string]uint64{
  267. "b": Byte,
  268. "kb": KByte,
  269. "mb": MByte,
  270. "gb": GByte,
  271. "tb": TByte,
  272. "pb": PByte,
  273. "eb": EByte,
  274. }
  275. func logn(n, b float64) float64 {
  276. return math.Log(n) / math.Log(b)
  277. }
  278. func humanateBytes(s uint64, base float64, sizes []string) string {
  279. if s < 10 {
  280. return fmt.Sprintf("%dB", s)
  281. }
  282. e := math.Floor(logn(float64(s), base))
  283. suffix := sizes[int(e)]
  284. val := float64(s) / math.Pow(base, math.Floor(e))
  285. f := "%.0f"
  286. if val < 10 {
  287. f = "%.1f"
  288. }
  289. return fmt.Sprintf(f+"%s", val, suffix)
  290. }
  291. // FileSize calculates the file size and generate user-friendly string.
  292. func FileSize(s int64) string {
  293. sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
  294. return humanateBytes(uint64(s), 1024, sizes)
  295. }
  296. // Subtract deals with subtraction of all types of number.
  297. func Subtract(left interface{}, right interface{}) interface{} {
  298. var rleft, rright int64
  299. var fleft, fright float64
  300. var isInt bool = true
  301. switch left.(type) {
  302. case int:
  303. rleft = int64(left.(int))
  304. case int8:
  305. rleft = int64(left.(int8))
  306. case int16:
  307. rleft = int64(left.(int16))
  308. case int32:
  309. rleft = int64(left.(int32))
  310. case int64:
  311. rleft = left.(int64)
  312. case float32:
  313. fleft = float64(left.(float32))
  314. isInt = false
  315. case float64:
  316. fleft = left.(float64)
  317. isInt = false
  318. }
  319. switch right.(type) {
  320. case int:
  321. rright = int64(right.(int))
  322. case int8:
  323. rright = int64(right.(int8))
  324. case int16:
  325. rright = int64(right.(int16))
  326. case int32:
  327. rright = int64(right.(int32))
  328. case int64:
  329. rright = right.(int64)
  330. case float32:
  331. fright = float64(left.(float32))
  332. isInt = false
  333. case float64:
  334. fleft = left.(float64)
  335. isInt = false
  336. }
  337. if isInt {
  338. return rleft - rright
  339. } else {
  340. return fleft + float64(rleft) - (fright + float64(rright))
  341. }
  342. }
  343. // DateFormat pattern rules.
  344. var datePatterns = []string{
  345. // year
  346. "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  347. "y", "06", //A two digit representation of a year Examples: 99 or 03
  348. // month
  349. "m", "01", // Numeric representation of a month, with leading zeros 01 through 12
  350. "n", "1", // Numeric representation of a month, without leading zeros 1 through 12
  351. "M", "Jan", // A short textual representation of a month, three letters Jan through Dec
  352. "F", "January", // A full textual representation of a month, such as January or March January through December
  353. // day
  354. "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
  355. "j", "2", // Day of the month without leading zeros 1 to 31
  356. // week
  357. "D", "Mon", // A textual representation of a day, three letters Mon through Sun
  358. "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
  359. // time
  360. "g", "3", // 12-hour format of an hour without leading zeros 1 through 12
  361. "G", "15", // 24-hour format of an hour without leading zeros 0 through 23
  362. "h", "03", // 12-hour format of an hour with leading zeros 01 through 12
  363. "H", "15", // 24-hour format of an hour with leading zeros 00 through 23
  364. "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
  365. "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
  366. "i", "04", // Minutes with leading zeros 00 to 59
  367. "s", "05", // Seconds, with leading zeros 00 through 59
  368. // time zone
  369. "T", "MST",
  370. "P", "-07:00",
  371. "O", "-0700",
  372. // RFC 2822
  373. "r", time.RFC1123Z,
  374. }
  375. // Parse Date use PHP time format.
  376. func DateParse(dateString, format string) (time.Time, error) {
  377. replacer := strings.NewReplacer(datePatterns...)
  378. format = replacer.Replace(format)
  379. return time.ParseInLocation(format, dateString, time.Local)
  380. }
  381. // Date takes a PHP like date func to Go's time format.
  382. func DateFormat(t time.Time, format string) string {
  383. replacer := strings.NewReplacer(datePatterns...)
  384. format = replacer.Replace(format)
  385. return t.Format(format)
  386. }
  387. type xssFilter struct {
  388. reg *regexp.Regexp
  389. repl []byte
  390. }
  391. var (
  392. whiteSpace = []byte(" ")
  393. xssFilters = []xssFilter{
  394. {regexp.MustCompile(`\ [ONon]\w*=["]*`), whiteSpace},
  395. {regexp.MustCompile(`<[SCRIPTscript]{6}`), whiteSpace},
  396. {regexp.MustCompile(`=[` + "`" + `'"]*[JAVASCRIPTjavascript \t\0&#x0D;]*:`), whiteSpace},
  397. }
  398. )
  399. // XSS goes through all the XSS filters to make user input content as safe as possible.
  400. func XSS(in []byte) []byte {
  401. for _, filter := range xssFilters {
  402. in = filter.reg.ReplaceAll(in, filter.repl)
  403. }
  404. return in
  405. }
  406. func XSSString(in string) string {
  407. return string(XSS([]byte(in)))
  408. }