code.gitea.io/gitea@v1.19.3/modules/base/tool.go (about) 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package base 5 6 import ( 7 "crypto/md5" 8 "crypto/sha1" 9 "crypto/sha256" 10 "encoding/base64" 11 "encoding/hex" 12 "errors" 13 "fmt" 14 "os" 15 "path/filepath" 16 "runtime" 17 "strconv" 18 "strings" 19 "time" 20 "unicode" 21 "unicode/utf8" 22 23 "code.gitea.io/gitea/modules/git" 24 "code.gitea.io/gitea/modules/log" 25 "code.gitea.io/gitea/modules/setting" 26 "code.gitea.io/gitea/modules/util" 27 28 "github.com/dustin/go-humanize" 29 ) 30 31 // EncodeMD5 encodes string to md5 hex value. 32 func 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. 39 func 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 sha256 hex value. 46 func 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. 54 func ShortSha(sha1 string) string { 55 return TruncateString(sha1, 10) 56 } 57 58 // BasicAuthDecode decode basic auth string 59 func 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 75 func BasicAuthEncode(username, password string) string { 76 return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 77 } 78 79 // VerifyTimeLimitCode verify time limit code 80 func 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 107 const 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 111 func 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. 141 func 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 magnitude 147 func PrettyNumber(i interface{}) string { 148 return humanize.Comma(util.NumberIntoInt64(i)) 149 } 150 151 // Subtract deals with subtraction of all types of number. 152 func Subtract(left, right interface{}) interface{} { 153 var rleft, rright int64 154 var fleft, fright float64 155 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. 202 func 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. 214 func 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. 222 func 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. 235 func 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 // Int64sContains returns if a int64 in a slice of int64 244 func Int64sContains(intsSlice []int64, a int64) bool { 245 for _, c := range intsSlice { 246 if c == a { 247 return true 248 } 249 } 250 return false 251 } 252 253 // IsLetter reports whether the rune is a letter (category L). 254 // https://github.com/golang/go/blob/c3b4918/src/go/scanner/scanner.go#L342 255 func IsLetter(ch rune) bool { 256 return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) 257 } 258 259 // EntryIcon returns the octicon class for displaying files/directories 260 func EntryIcon(entry *git.TreeEntry) string { 261 switch { 262 case entry.IsLink(): 263 te, err := entry.FollowLink() 264 if err != nil { 265 log.Debug(err.Error()) 266 return "file-symlink-file" 267 } 268 if te.IsDir() { 269 return "file-submodule" 270 } 271 return "file-symlink-file" 272 case entry.IsDir(): 273 return "file-directory-fill" 274 case entry.IsSubModule(): 275 return "file-submodule" 276 } 277 278 return "file" 279 } 280 281 // SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value 282 func SetupGiteaRoot() string { 283 giteaRoot := os.Getenv("GITEA_ROOT") 284 if giteaRoot == "" { 285 _, filename, _, _ := runtime.Caller(0) 286 giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go") 287 wd, err := os.Getwd() 288 if err != nil { 289 rel, err := filepath.Rel(giteaRoot, wd) 290 if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") { 291 giteaRoot = wd 292 } 293 } 294 if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) { 295 giteaRoot = "" 296 } else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil { 297 giteaRoot = "" 298 } 299 } 300 return giteaRoot 301 } 302 303 // FormatNumberSI format a number 304 func FormatNumberSI(data interface{}) string { 305 var num int64 306 if num1, ok := data.(int64); ok { 307 num = num1 308 } else if num1, ok := data.(int); ok { 309 num = int64(num1) 310 } else { 311 return "" 312 } 313 314 if num < 1000 { 315 return fmt.Sprintf("%d", num) 316 } else if num < 1000000 { 317 num2 := float32(num) / float32(1000.0) 318 return fmt.Sprintf("%.1fk", num2) 319 } else if num < 1000000000 { 320 num2 := float32(num) / float32(1000000.0) 321 return fmt.Sprintf("%.1fM", num2) 322 } 323 num2 := float32(num) / float32(1000000000.0) 324 return fmt.Sprintf("%.1fG", num2) 325 }