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  }