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 5package base 6 7import ( 8 "crypto/md5" 9 "crypto/sha1" 10 "crypto/sha256" 11 "encoding/base64" 12 "encoding/hex" 13 "errors" 14 "fmt" 15 "os" 16 "path/filepath" 17 "runtime" 18 "strconv" 19 "strings" 20 "time" 21 "unicode" 22 "unicode/utf8" 23 24 "code.gitea.io/gitea/modules/git" 25 "code.gitea.io/gitea/modules/log" 26 "code.gitea.io/gitea/modules/setting" 27 28 "github.com/dustin/go-humanize" 29) 30 31// EncodeMD5 encodes string to md5 hex value. 32func EncodeMD5(str string) string { 33 m := md5.New() 34 _, _ = m.Write([]byte(str)) 35 return hex.EncodeToString(m.Sum(nil)) 36} 37 38// EncodeSha1 string to sha1 hex value. 39func EncodeSha1(str string) string { 40 h := sha1.New() 41 _, _ = h.Write([]byte(str)) 42 return hex.EncodeToString(h.Sum(nil)) 43} 44 45// EncodeSha256 string to sha1 hex value. 46func EncodeSha256(str string) string { 47 h := sha256.New() 48 _, _ = h.Write([]byte(str)) 49 return hex.EncodeToString(h.Sum(nil)) 50} 51 52// ShortSha is basically just truncating. 53// It is DEPRECATED and will be removed in the future. 54func ShortSha(sha1 string) string { 55 return TruncateString(sha1, 10) 56} 57 58// BasicAuthDecode decode basic auth string 59func BasicAuthDecode(encoded string) (string, string, error) { 60 s, err := base64.StdEncoding.DecodeString(encoded) 61 if err != nil { 62 return "", "", err 63 } 64 65 auth := strings.SplitN(string(s), ":", 2) 66 67 if len(auth) != 2 { 68 return "", "", errors.New("invalid basic authentication") 69 } 70 71 return auth[0], auth[1], nil 72} 73 74// BasicAuthEncode encode basic auth string 75func BasicAuthEncode(username, password string) string { 76 return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 77} 78 79// VerifyTimeLimitCode verify time limit code 80func VerifyTimeLimitCode(data string, minutes int, code string) bool { 81 if len(code) <= 18 { 82 return false 83 } 84 85 // split code 86 start := code[:12] 87 lives := code[12:18] 88 if d, err := strconv.ParseInt(lives, 10, 0); err == nil { 89 minutes = int(d) 90 } 91 92 // right active code 93 retCode := CreateTimeLimitCode(data, minutes, start) 94 if retCode == code && minutes > 0 { 95 // check time is expired or not 96 before, _ := time.ParseInLocation("200601021504", start, time.Local) 97 now := time.Now() 98 if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { 99 return true 100 } 101 } 102 103 return false 104} 105 106// TimeLimitCodeLength default value for time limit code 107const TimeLimitCodeLength = 12 + 6 + 40 108 109// CreateTimeLimitCode create a time limit code 110// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string 111func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { 112 format := "200601021504" 113 114 var start, end time.Time 115 var startStr, endStr string 116 117 if startInf == nil { 118 // Use now time create code 119 start = time.Now() 120 startStr = start.Format(format) 121 } else { 122 // use start string create code 123 startStr = startInf.(string) 124 start, _ = time.ParseInLocation(format, startStr, time.Local) 125 startStr = start.Format(format) 126 } 127 128 end = start.Add(time.Minute * time.Duration(minutes)) 129 endStr = end.Format(format) 130 131 // create sha1 encode string 132 sh := sha1.New() 133 _, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, setting.SecretKey, startStr, endStr, minutes))) 134 encoded := hex.EncodeToString(sh.Sum(nil)) 135 136 code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) 137 return code 138} 139 140// FileSize calculates the file size and generate user-friendly string. 141func FileSize(s int64) string { 142 return humanize.IBytes(uint64(s)) 143} 144 145// PrettyNumber produces a string form of the given number in base 10 with 146// commas after every three orders of magnitud 147func PrettyNumber(v int64) string { 148 return humanize.Comma(v) 149} 150 151// Subtract deals with subtraction of all types of number. 152func Subtract(left, right interface{}) interface{} { 153 var rleft, rright int64 154 var fleft, fright float64 155 var isInt = true 156 switch v := left.(type) { 157 case int: 158 rleft = int64(v) 159 case int8: 160 rleft = int64(v) 161 case int16: 162 rleft = int64(v) 163 case int32: 164 rleft = int64(v) 165 case int64: 166 rleft = v 167 case float32: 168 fleft = float64(v) 169 isInt = false 170 case float64: 171 fleft = v 172 isInt = false 173 } 174 175 switch v := right.(type) { 176 case int: 177 rright = int64(v) 178 case int8: 179 rright = int64(v) 180 case int16: 181 rright = int64(v) 182 case int32: 183 rright = int64(v) 184 case int64: 185 rright = v 186 case float32: 187 fright = float64(v) 188 isInt = false 189 case float64: 190 fright = v 191 isInt = false 192 } 193 194 if isInt { 195 return rleft - rright 196 } 197 return fleft + float64(rleft) - (fright + float64(rright)) 198} 199 200// EllipsisString returns a truncated short string, 201// it appends '...' in the end of the length of string is too large. 202func EllipsisString(str string, length int) string { 203 if length <= 3 { 204 return "..." 205 } 206 if utf8.RuneCountInString(str) <= length { 207 return str 208 } 209 return string([]rune(str)[:length-3]) + "..." 210} 211 212// TruncateString returns a truncated string with given limit, 213// it returns input string if length is not reached limit. 214func TruncateString(str string, limit int) string { 215 if utf8.RuneCountInString(str) < limit { 216 return str 217 } 218 return string([]rune(str)[:limit]) 219} 220 221// StringsToInt64s converts a slice of string to a slice of int64. 222func StringsToInt64s(strs []string) ([]int64, error) { 223 ints := make([]int64, len(strs)) 224 for i := range strs { 225 n, err := strconv.ParseInt(strs[i], 10, 64) 226 if err != nil { 227 return ints, err 228 } 229 ints[i] = n 230 } 231 return ints, nil 232} 233 234// Int64sToStrings converts a slice of int64 to a slice of string. 235func Int64sToStrings(ints []int64) []string { 236 strs := make([]string, len(ints)) 237 for i := range ints { 238 strs[i] = strconv.FormatInt(ints[i], 10) 239 } 240 return strs 241} 242 243// Int64sToMap converts a slice of int64 to a int64 map. 244func Int64sToMap(ints []int64) map[int64]bool { 245 m := make(map[int64]bool) 246 for _, i := range ints { 247 m[i] = true 248 } 249 return m 250} 251 252// Int64sContains returns if a int64 in a slice of int64 253func Int64sContains(intsSlice []int64, a int64) bool { 254 for _, c := range intsSlice { 255 if c == a { 256 return true 257 } 258 } 259 return false 260} 261 262// IsLetter reports whether the rune is a letter (category L). 263// https://github.com/golang/go/blob/c3b4918/src/go/scanner/scanner.go#L342 264func IsLetter(ch rune) bool { 265 return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) 266} 267 268// EntryIcon returns the octicon class for displaying files/directories 269func EntryIcon(entry *git.TreeEntry) string { 270 switch { 271 case entry.IsLink(): 272 te, err := entry.FollowLink() 273 if err != nil { 274 log.Debug(err.Error()) 275 return "file-symlink-file" 276 } 277 if te.IsDir() { 278 return "file-submodule" 279 } 280 return "file-symlink-file" 281 case entry.IsDir(): 282 return "file-directory" 283 case entry.IsSubModule(): 284 return "file-submodule" 285 } 286 287 return "file" 288} 289 290// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value 291func SetupGiteaRoot() string { 292 giteaRoot := os.Getenv("GITEA_ROOT") 293 if giteaRoot == "" { 294 _, filename, _, _ := runtime.Caller(0) 295 giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go") 296 wd, err := os.Getwd() 297 if err != nil { 298 rel, err := filepath.Rel(giteaRoot, wd) 299 if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") { 300 giteaRoot = wd 301 } 302 } 303 if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) { 304 giteaRoot = "" 305 } else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil { 306 giteaRoot = "" 307 } 308 } 309 return giteaRoot 310} 311 312// FormatNumberSI format a number 313func FormatNumberSI(data interface{}) string { 314 var num int64 315 if num1, ok := data.(int64); ok { 316 num = num1 317 } else if num1, ok := data.(int); ok { 318 num = int64(num1) 319 } else { 320 return "" 321 } 322 323 if num < 1000 { 324 return fmt.Sprintf("%d", num) 325 } else if num < 1000000 { 326 num2 := float32(num) / float32(1000.0) 327 return fmt.Sprintf("%.1fk", num2) 328 } else if num < 1000000000 { 329 num2 := float32(num) / float32(1000000.0) 330 return fmt.Sprintf("%.1fM", num2) 331 } 332 num2 := float32(num) / float32(1000000000.0) 333 return fmt.Sprintf("%.1fG", num2) 334} 335