gitea/modules/base/tool.go

326 lines
7.7 KiB
Go
Raw Normal View History

2014-02-18 16:31:16 -06:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2014-02-18 16:31:16 -06:00
2014-03-07 16:22:15 -06:00
package base
2014-02-18 16:31:16 -06:00
import (
"crypto/md5"
2014-03-19 06:21:23 -05:00
"crypto/sha1"
2019-05-04 10:45:34 -05:00
"crypto/sha256"
2014-11-07 13:46:13 -06:00
"encoding/base64"
2014-02-18 16:31:16 -06:00
"encoding/hex"
"errors"
2014-03-14 01:32:11 -05:00
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
2014-03-14 01:32:11 -05:00
"time"
2016-02-20 16:10:05 -06:00
"unicode"
"unicode/utf8"
2014-05-25 19:11:25 -05:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/dustin/go-humanize"
2014-02-18 16:31:16 -06:00
)
2015-11-24 17:49:34 -06:00
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5(str string) string {
2014-02-18 16:31:16 -06:00
m := md5.New()
2019-06-12 14:41:28 -05:00
_, _ = m.Write([]byte(str))
2014-02-18 16:31:16 -06:00
return hex.EncodeToString(m.Sum(nil))
}
2014-03-14 01:32:11 -05:00
2016-11-24 01:17:44 -06:00
// EncodeSha1 string to sha1 hex value.
2014-11-12 05:48:50 -06:00
func EncodeSha1(str string) string {
h := sha1.New()
2019-06-12 14:41:28 -05:00
_, _ = h.Write([]byte(str))
2014-11-12 05:48:50 -06:00
return hex.EncodeToString(h.Sum(nil))
}
// EncodeSha256 string to sha256 hex value.
2019-05-04 10:45:34 -05:00
func EncodeSha256(str string) string {
h := sha256.New()
2019-06-12 14:41:28 -05:00
_, _ = h.Write([]byte(str))
2019-05-04 10:45:34 -05:00
return hex.EncodeToString(h.Sum(nil))
}
2016-11-07 16:14:50 -06:00
// ShortSha is basically just truncating.
// It is DEPRECATED and will be removed in the future.
2015-11-13 16:10:25 -06:00
func ShortSha(sha1 string) string {
return TruncateString(sha1, 10)
2015-11-13 16:10:25 -06:00
}
2016-11-24 01:17:44 -06:00
// BasicAuthDecode decode basic auth string
2014-12-10 04:10:26 -06:00
func BasicAuthDecode(encoded string) (string, string, error) {
s, err := base64.StdEncoding.DecodeString(encoded)
2014-11-07 13:46:13 -06:00
if err != nil {
2014-12-10 04:10:26 -06:00
return "", "", err
2014-11-07 13:46:13 -06:00
}
2014-12-10 04:01:17 -06:00
auth := strings.SplitN(string(s), ":", 2)
if len(auth) != 2 {
return "", "", errors.New("invalid basic authentication")
}
2014-12-10 04:10:26 -06:00
return auth[0], auth[1], nil
2014-11-07 13:46:13 -06:00
}
2016-11-24 01:17:44 -06:00
// BasicAuthEncode encode basic auth string
2014-11-07 13:46:13 -06:00
func BasicAuthEncode(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
}
2016-11-24 01:17:44 -06:00
// VerifyTimeLimitCode verify time limit code
2014-03-19 11:50:44 -05:00
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 := strconv.ParseInt(lives, 10, 0); err == nil {
minutes = int(d)
2014-03-19 11:50:44 -05:00
}
// right active code
retCode := CreateTimeLimitCode(data, minutes, start)
if retCode == code && minutes > 0 {
// check time is expired or not
before, _ := time.ParseInLocation("200601021504", start, time.Local)
2014-03-19 11:50:44 -05:00
now := time.Now()
if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
return true
}
}
return false
}
2016-11-24 01:17:44 -06:00
// TimeLimitCodeLength default value for time limit code
2014-03-19 11:50:44 -05:00
const TimeLimitCodeLength = 12 + 6 + 40
2016-11-24 01:17:44 -06:00
// CreateTimeLimitCode create a time limit code
2014-03-19 06:21:23 -05:00
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
format := "200601021504"
2014-03-19 06:21:23 -05:00
var start, end time.Time
var startStr, endStr string
if startInf == nil {
// Use now time create code
start = time.Now()
startStr = start.Format(format)
2014-03-19 06:21:23 -05:00
} else {
// use start string create code
startStr = startInf.(string)
start, _ = time.ParseInLocation(format, startStr, time.Local)
startStr = start.Format(format)
2014-03-19 06:21:23 -05:00
}
end = start.Add(time.Minute * time.Duration(minutes))
endStr = end.Format(format)
2014-03-19 06:21:23 -05:00
// create sha1 encode string
sh := sha1.New()
_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, setting.SecretKey, startStr, endStr, minutes)))
2014-03-19 06:21:23 -05:00
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
return code
}
2014-03-15 11:29:49 -05:00
// FileSize calculates the file size and generate user-friendly string.
2014-03-15 11:31:12 -05:00
func FileSize(s int64) string {
return humanize.IBytes(uint64(s))
}
// PrettyNumber produces a string form of the given number in base 10 with
// commas after every three orders of magnitude
func PrettyNumber(i interface{}) string {
return humanize.Comma(util.NumberIntoInt64(i))
2014-03-15 11:29:49 -05:00
}
2014-03-14 18:34:59 -05:00
// Subtract deals with subtraction of all types of number.
func Subtract(left, right interface{}) interface{} {
2014-03-14 18:34:59 -05:00
var rleft, rright int64
var fleft, fright float64
isInt := true
switch v := left.(type) {
2014-03-14 18:34:59 -05:00
case int:
rleft = int64(v)
2014-03-14 18:34:59 -05:00
case int8:
rleft = int64(v)
2014-03-14 18:34:59 -05:00
case int16:
rleft = int64(v)
2014-03-14 18:34:59 -05:00
case int32:
rleft = int64(v)
2014-03-14 18:34:59 -05:00
case int64:
rleft = v
2014-03-14 18:34:59 -05:00
case float32:
fleft = float64(v)
2014-03-14 18:34:59 -05:00
isInt = false
case float64:
fleft = v
2014-03-14 18:34:59 -05:00
isInt = false
}
switch v := right.(type) {
2014-03-14 18:34:59 -05:00
case int:
rright = int64(v)
2014-03-14 18:34:59 -05:00
case int8:
rright = int64(v)
2014-03-14 18:34:59 -05:00
case int16:
rright = int64(v)
2014-03-14 18:34:59 -05:00
case int32:
rright = int64(v)
2014-03-14 18:34:59 -05:00
case int64:
rright = v
2014-03-14 18:34:59 -05:00
case float32:
fright = float64(v)
2014-03-14 18:34:59 -05:00
isInt = false
case float64:
fright = v
2014-03-14 18:34:59 -05:00
isInt = false
}
if isInt {
return rleft - rright
}
2016-11-24 01:17:44 -06:00
return fleft + float64(rleft) - (fright + float64(rright))
2014-03-14 18:34:59 -05:00
}
2015-08-10 03:52:08 -05:00
// EllipsisString returns a truncated short string,
// it appends '...' in the end of the length of string is too large.
func EllipsisString(str string, length int) string {
if length <= 3 {
return "..."
}
if utf8.RuneCountInString(str) <= length {
return str
}
return string([]rune(str)[:length-3]) + "..."
}
// TruncateString returns a truncated string with given limit,
// it returns input string if length is not reached limit.
func TruncateString(str string, limit int) string {
if utf8.RuneCountInString(str) < limit {
return str
}
return string([]rune(str)[:limit])
}
2015-08-10 03:52:08 -05:00
// StringsToInt64s converts a slice of string to a slice of int64.
func StringsToInt64s(strs []string) ([]int64, error) {
2015-08-10 03:52:08 -05:00
ints := make([]int64, len(strs))
for i := range strs {
n, err := strconv.ParseInt(strs[i], 10, 64)
if err != nil {
return ints, err
}
ints[i] = n
2015-08-10 03:52:08 -05:00
}
return ints, nil
2015-08-10 03:52:08 -05:00
}
2015-08-25 10:22:05 -05:00
// Int64sToStrings converts a slice of int64 to a slice of string.
func Int64sToStrings(ints []int64) []string {
strs := make([]string, len(ints))
for i := range ints {
strs[i] = strconv.FormatInt(ints[i], 10)
2015-08-25 10:22:05 -05:00
}
return strs
}
// Int64sContains returns if a int64 in a slice of int64
func Int64sContains(intsSlice []int64, a int64) bool {
for _, c := range intsSlice {
if c == a {
return true
}
}
return false
}
2016-02-20 16:10:05 -06:00
// IsLetter reports whether the rune is a letter (category L).
// https://github.com/golang/go/blob/c3b4918/src/go/scanner/scanner.go#L342
2016-02-20 16:10:05 -06:00
func IsLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
// EntryIcon returns the octicon class for displaying files/directories
func EntryIcon(entry *git.TreeEntry) string {
switch {
case entry.IsLink():
te, err := entry.FollowLink()
if err != nil {
log.Debug(err.Error())
return "file-symlink-file"
}
if te.IsDir() {
return "file-submodule"
}
return "file-symlink-file"
case entry.IsDir():
return "file-directory-fill"
case entry.IsSubModule():
return "file-submodule"
}
Add Octicon SVG spritemap (#10107) * Add octicon SVG sprite Signed-off-by: jolheiser <john.olheiser@gmail.com> * Static prefix Signed-off-by: jolheiser <john.olheiser@gmail.com> * SVG for all repo icons Signed-off-by: jolheiser <john.olheiser@gmail.com> * make vendor Signed-off-by: jolheiser <john.olheiser@gmail.com> * Swap out octicons Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move octicons to top of less imports Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix JS Signed-off-by: jolheiser <john.olheiser@gmail.com> * Definitely not a search/replace Signed-off-by: jolheiser <john.olheiser@gmail.com> * Missed regex Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move to more generic calls and webpack Signed-off-by: jolheiser <john.olheiser@gmail.com> * make svg -> make webpack Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove svg-sprite Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Missed a test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove svg from makefile Signed-off-by: jolheiser <john.olheiser@gmail.com> * Suggestions Signed-off-by: jolheiser <john.olheiser@gmail.com> * Attempt to fix test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Revert timetracking test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Swap .octicon for .svg in less Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add aria-hidden Signed-off-by: jolheiser <john.olheiser@gmail.com> * Replace mega-octicon Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix webpack globbing on Windows Signed-off-by: jolheiser <john.olheiser@gmail.com> * Revert Co-Authored-By: silverwind <me@silverwind.io> * Fix octions from upstream Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix Vue and missed JS function Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add JS helper and PWA Signed-off-by: jolheiser <john.olheiser@gmail.com> * Preload SVG Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <matti@mdranta.net>
2020-02-11 11:02:41 -06:00
return "file"
}
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
func SetupGiteaRoot() string {
giteaRoot := os.Getenv("GITEA_ROOT")
if giteaRoot == "" {
_, filename, _, _ := runtime.Caller(0)
giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go")
wd, err := os.Getwd()
if err != nil {
rel, err := filepath.Rel(giteaRoot, wd)
if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") {
giteaRoot = wd
}
}
if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) {
giteaRoot = ""
} else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil {
giteaRoot = ""
}
}
return giteaRoot
}
// FormatNumberSI format a number
func FormatNumberSI(data interface{}) string {
var num int64
if num1, ok := data.(int64); ok {
num = num1
} else if num1, ok := data.(int); ok {
num = int64(num1)
} else {
return ""
}
if num < 1000 {
return fmt.Sprintf("%d", num)
} else if num < 1000000 {
num2 := float32(num) / float32(1000.0)
return fmt.Sprintf("%.1fk", num2)
} else if num < 1000000000 {
num2 := float32(num) / float32(1000000.0)
return fmt.Sprintf("%.1fM", num2)
}
num2 := float32(num) / float32(1000000000.0)
return fmt.Sprintf("%.1fG", num2)
}