// Copyright 2014 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package base import ( "bytes" "crypto/md5" "crypto/rand" "crypto/sha1" "encoding/hex" "encoding/json" "fmt" "math" "strconv" "strings" "time" ) // Encode string to md5 hex value func EncodeMd5(str string) string { m := md5.New() m.Write([]byte(str)) return hex.EncodeToString(m.Sum(nil)) } // GetRandomString generate random string by specify chars. func GetRandomString(n int, alphabets ...byte) string { const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) rand.Read(bytes) for i, b := range bytes { if len(alphabets) == 0 { bytes[i] = alphanum[b%byte(len(alphanum))] } else { bytes[i] = alphabets[b%byte(len(alphabets))] } } return string(bytes) } // verify time limit code func VerifyTimeLimitCode(data string, minutes int, code string) bool { if len(code) <= 18 { return false } // split code start := code[:12] lives := code[12:18] if d, err := StrTo(lives).Int(); err == nil { minutes = d } // right active code retCode := CreateTimeLimitCode(data, minutes, start) if retCode == code && minutes > 0 { // check time is expired or not before, _ := DateParse(start, "YmdHi") now := time.Now() if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { return true } } return false } const TimeLimitCodeLength = 12 + 6 + 40 // create a time limit code // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { format := "YmdHi" var start, end time.Time var startStr, endStr string if startInf == nil { // Use now time create code start = time.Now() startStr = DateFormat(start, format) } else { // use start string create code startStr = startInf.(string) start, _ = DateParse(startStr, format) startStr = DateFormat(start, format) } end = start.Add(time.Minute * time.Duration(minutes)) endStr = DateFormat(end, format) // create sha1 encode string sh := sha1.New() sh.Write([]byte(data + SecretKey + startStr + endStr + ToStr(minutes))) encoded := hex.EncodeToString(sh.Sum(nil)) code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) return code } // AvatarLink returns avatar link by given e-mail. func AvatarLink(email string) string { if Service.EnableCacheAvatar { return "/avatar/" + EncodeMd5(email) } return "http://1.gravatar.com/avatar/" + EncodeMd5(email) } // Seconds-based time units const ( Minute = 60 Hour = 60 * Minute Day = 24 * Hour Week = 7 * Day Month = 30 * Day Year = 12 * Month ) func computeTimeDiff(diff int64) (int64, string) { diffStr := "" switch { case diff <= 0: diff = 0 diffStr = "now" case diff < 2: diff = 0 diffStr = "1 second" case diff < 1*Minute: diffStr = fmt.Sprintf("%d seconds", diff) diff = 0 case diff < 2*Minute: diff -= 1 * Minute diffStr = "1 minute" case diff < 1*Hour: diffStr = fmt.Sprintf("%d minutes", diff/Minute) diff -= diff / Minute * Minute case diff < 2*Hour: diff -= 1 * Hour diffStr = "1 hour" case diff < 1*Day: diffStr = fmt.Sprintf("%d hours", diff/Hour) diff -= diff / Hour * Hour case diff < 2*Day: diff -= 1 * Day diffStr = "1 day" case diff < 1*Week: diffStr = fmt.Sprintf("%d days", diff/Day) diff -= diff / Day * Day case diff < 2*Week: diff -= 1 * Week diffStr = "1 week" case diff < 1*Month: diffStr = fmt.Sprintf("%d weeks", diff/Week) diff -= diff / Week * Week case diff < 2*Month: diff -= 1 * Month diffStr = "1 month" case diff < 1*Year: diffStr = fmt.Sprintf("%d months", diff/Month) diff -= diff / Month * Month case diff < 2*Year: diff -= 1 * Year diffStr = "1 year" default: diffStr = fmt.Sprintf("%d years", diff/Year) diff = 0 } return diff, diffStr } // TimeSincePro calculates the time interval and generate full user-friendly string. func TimeSincePro(then time.Time) string { now := time.Now() diff := now.Unix() - then.Unix() if then.After(now) { return "future" } var timeStr, diffStr string for { if diff == 0 { break } diff, diffStr = computeTimeDiff(diff) timeStr += ", " + diffStr } return strings.TrimPrefix(timeStr, ", ") } // TimeSince calculates the time interval and generate user-friendly string. func TimeSince(then time.Time) string { now := time.Now() lbl := "ago" diff := now.Unix() - then.Unix() if then.After(now) { lbl = "from now" diff = then.Unix() - now.Unix() } switch { case diff <= 0: return "now" case diff <= 2: return fmt.Sprintf("1 second %s", lbl) case diff < 1*Minute: return fmt.Sprintf("%d seconds %s", diff, lbl) case diff < 2*Minute: return fmt.Sprintf("1 minute %s", lbl) case diff < 1*Hour: return fmt.Sprintf("%d minutes %s", diff/Minute, lbl) case diff < 2*Hour: return fmt.Sprintf("1 hour %s", lbl) case diff < 1*Day: return fmt.Sprintf("%d hours %s", diff/Hour, lbl) case diff < 2*Day: return fmt.Sprintf("1 day %s", lbl) case diff < 1*Week: return fmt.Sprintf("%d days %s", diff/Day, lbl) case diff < 2*Week: return fmt.Sprintf("1 week %s", lbl) case diff < 1*Month: return fmt.Sprintf("%d weeks %s", diff/Week, lbl) case diff < 2*Month: return fmt.Sprintf("1 month %s", lbl) case diff < 1*Year: return fmt.Sprintf("%d months %s", diff/Month, lbl) case diff < 2*Year: return fmt.Sprintf("1 year %s", lbl) default: return fmt.Sprintf("%d years %s", diff/Year, lbl) } return then.String() } const ( Byte = 1 KByte = Byte * 1024 MByte = KByte * 1024 GByte = MByte * 1024 TByte = GByte * 1024 PByte = TByte * 1024 EByte = PByte * 1024 ) var bytesSizeTable = map[string]uint64{ "b": Byte, "kb": KByte, "mb": MByte, "gb": GByte, "tb": TByte, "pb": PByte, "eb": EByte, } func logn(n, b float64) float64 { return math.Log(n) / math.Log(b) } func humanateBytes(s uint64, base float64, sizes []string) string { if s < 10 { return fmt.Sprintf("%dB", s) } e := math.Floor(logn(float64(s), base)) suffix := sizes[int(e)] val := float64(s) / math.Pow(base, math.Floor(e)) f := "%.0f" if val < 10 { f = "%.1f" } return fmt.Sprintf(f+"%s", val, suffix) } // FileSize calculates the file size and generate user-friendly string. func FileSize(s int64) string { sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} return humanateBytes(uint64(s), 1024, sizes) } // Subtract deals with subtraction of all types of number. func Subtract(left interface{}, right interface{}) interface{} { var rleft, rright int64 var fleft, fright float64 var isInt bool = true switch left.(type) { case int: rleft = int64(left.(int)) case int8: rleft = int64(left.(int8)) case int16: rleft = int64(left.(int16)) case int32: rleft = int64(left.(int32)) case int64: rleft = left.(int64) case float32: fleft = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } switch right.(type) { case int: rright = int64(right.(int)) case int8: rright = int64(right.(int8)) case int16: rright = int64(right.(int16)) case int32: rright = int64(right.(int32)) case int64: rright = right.(int64) case float32: fright = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } if isInt { return rleft - rright } else { return fleft + float64(rleft) - (fright + float64(rright)) } } // DateFormat pattern rules. var datePatterns = []string{ // year "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 "y", "06", //A two digit representation of a year Examples: 99 or 03 // month "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 "M", "Jan", // A short textual representation of a month, three letters Jan through Dec "F", "January", // A full textual representation of a month, such as January or March January through December // day "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 "j", "2", // Day of the month without leading zeros 1 to 31 // week "D", "Mon", // A textual representation of a day, three letters Mon through Sun "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday // time "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM "i", "04", // Minutes with leading zeros 00 to 59 "s", "05", // Seconds, with leading zeros 00 through 59 // time zone "T", "MST", "P", "-07:00", "O", "-0700", // RFC 2822 "r", time.RFC1123Z, } // Parse Date use PHP time format. func DateParse(dateString, format string) (time.Time, error) { replacer := strings.NewReplacer(datePatterns...) format = replacer.Replace(format) return time.ParseInLocation(format, dateString, time.Local) } // Date takes a PHP like date func to Go's time format. func DateFormat(t time.Time, format string) string { replacer := strings.NewReplacer(datePatterns...) format = replacer.Replace(format) return t.Format(format) } // convert string to specify type type StrTo string func (f StrTo) Exist() bool { return string(f) != string(0x1E) } func (f StrTo) Int() (int, error) { v, err := strconv.ParseInt(f.String(), 10, 32) return int(v), err } func (f StrTo) String() string { if f.Exist() { return string(f) } return "" } // convert any type to string func ToStr(value interface{}, args ...int) (s string) { switch v := value.(type) { case bool: s = strconv.FormatBool(v) case float32: s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) case float64: s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) case int: s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) case int8: s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) case int16: s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) case int32: s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) case int64: s = strconv.FormatInt(v, argInt(args).Get(0, 10)) case uint: s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) case uint8: s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) case uint16: s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) case uint32: s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) case uint64: s = strconv.FormatUint(v, argInt(args).Get(0, 10)) case string: s = v case []byte: s = string(v) default: s = fmt.Sprintf("%v", v) } return s } type argInt []int func (a argInt) Get(i int, args ...int) (r int) { if i >= 0 && i < len(a) { r = a[i] } if len(args) > 0 { r = args[0] } return } type Actioner interface { GetOpType() int GetActUserName() string GetRepoName() string GetBranch() string GetContent() string } // ActionIcon accepts a int that represents action operation type // and returns a icon class name. func ActionIcon(opType int) string { switch opType { case 1: // Create repository. return "plus-circle" case 5: // Commit repository. return "arrow-circle-o-right" default: return "invalid type" } } const ( TPL_CREATE_REPO = `%s created repository %s` TPL_COMMIT_REPO = `%s pushed to %s at %s/%s%s` TPL_COMMIT_REPO_LI = `