2014-02-19 02:31:16 +04:00
// 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.
2014-03-08 02:22:15 +04:00
package base
2014-02-19 02:31:16 +04:00
import (
"crypto/md5"
2014-03-19 15:21:23 +04:00
"crypto/sha1"
2019-05-04 18:45:34 +03:00
"crypto/sha256"
2014-11-07 22:46:13 +03:00
"encoding/base64"
2014-02-19 02:31:16 +04:00
"encoding/hex"
2014-03-14 10:32:11 +04:00
"fmt"
2016-02-21 01:10:05 +03:00
"net/http"
2019-04-17 19:06:35 +03:00
"os"
"path/filepath"
"runtime"
2016-11-07 23:49:50 +03:00
"strconv"
2014-03-15 11:28:06 +04:00
"strings"
2014-03-14 10:32:11 +04:00
"time"
2016-02-21 01:10:05 +03:00
"unicode"
2014-05-26 04:11:25 +04:00
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-03-27 12:33:00 +03:00
2020-02-03 22:50:37 +03:00
"github.com/dustin/go-humanize"
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
2014-02-19 02:31:16 +04:00
)
2015-11-25 02:49:34 +03:00
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5 ( str string ) string {
2014-02-19 02:31:16 +04:00
m := md5 . New ( )
2019-06-12 22:41:28 +03:00
_ , _ = m . Write ( [ ] byte ( str ) )
2014-02-19 02:31:16 +04:00
return hex . EncodeToString ( m . Sum ( nil ) )
}
2014-03-14 10:32:11 +04:00
2016-11-24 10:17:44 +03:00
// EncodeSha1 string to sha1 hex value.
2014-11-12 14:48:50 +03:00
func EncodeSha1 ( str string ) string {
h := sha1 . New ( )
2019-06-12 22:41:28 +03:00
_ , _ = h . Write ( [ ] byte ( str ) )
2014-11-12 14:48:50 +03:00
return hex . EncodeToString ( h . Sum ( nil ) )
}
2019-05-04 18:45:34 +03:00
// EncodeSha256 string to sha1 hex value.
func EncodeSha256 ( str string ) string {
h := sha256 . New ( )
2019-06-12 22:41:28 +03:00
_ , _ = h . Write ( [ ] byte ( str ) )
2019-05-04 18:45:34 +03:00
return hex . EncodeToString ( h . Sum ( nil ) )
}
2016-11-08 01:14:50 +03:00
// ShortSha is basically just truncating.
// It is DEPRECATED and will be removed in the future.
2015-11-14 01:10:25 +03:00
func ShortSha ( sha1 string ) string {
2016-11-07 23:36:01 +03:00
return TruncateString ( sha1 , 10 )
2015-11-14 01:10:25 +03:00
}
2016-11-24 10:17:44 +03:00
// BasicAuthDecode decode basic auth string
2014-12-10 13:10:26 +03:00
func BasicAuthDecode ( encoded string ) ( string , string , error ) {
s , err := base64 . StdEncoding . DecodeString ( encoded )
2014-11-07 22:46:13 +03:00
if err != nil {
2014-12-10 13:10:26 +03:00
return "" , "" , err
2014-11-07 22:46:13 +03:00
}
2014-12-10 13:01:17 +03:00
auth := strings . SplitN ( string ( s ) , ":" , 2 )
2014-12-10 13:10:26 +03:00
return auth [ 0 ] , auth [ 1 ] , nil
2014-11-07 22:46:13 +03:00
}
2016-11-24 10:17:44 +03:00
// BasicAuthEncode encode basic auth string
2014-11-07 22:46:13 +03:00
func BasicAuthEncode ( username , password string ) string {
return base64 . StdEncoding . EncodeToString ( [ ] byte ( username + ":" + password ) )
}
2016-11-24 10:17:44 +03:00
// VerifyTimeLimitCode verify time limit code
2014-03-19 20:50:44 +04: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 ]
2014-07-26 08:24:27 +04:00
if d , err := com . StrTo ( lives ) . Int ( ) ; err == nil {
2014-03-19 20:50:44 +04:00
minutes = d
}
// right active code
retCode := CreateTimeLimitCode ( data , minutes , start )
if retCode == code && minutes > 0 {
// check time is expired or not
2015-02-16 15:44:27 +03:00
before , _ := time . ParseInLocation ( "200601021504" , start , time . Local )
2014-03-19 20:50:44 +04:00
now := time . Now ( )
if before . Add ( time . Minute * time . Duration ( minutes ) ) . Unix ( ) > now . Unix ( ) {
return true
}
}
return false
}
2016-11-24 10:17:44 +03:00
// TimeLimitCodeLength default value for time limit code
2014-03-19 20:50:44 +04:00
const TimeLimitCodeLength = 12 + 6 + 40
2016-11-24 10:17:44 +03:00
// CreateTimeLimitCode create a time limit code
2014-03-19 15:21:23 +04:00
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
func CreateTimeLimitCode ( data string , minutes int , startInf interface { } ) string {
2015-02-16 15:44:27 +03:00
format := "200601021504"
2014-03-19 15:21:23 +04:00
var start , end time . Time
var startStr , endStr string
if startInf == nil {
// Use now time create code
start = time . Now ( )
2015-02-16 15:44:27 +03:00
startStr = start . Format ( format )
2014-03-19 15:21:23 +04:00
} else {
// use start string create code
startStr = startInf . ( string )
2015-02-16 15:44:27 +03:00
start , _ = time . ParseInLocation ( format , startStr , time . Local )
startStr = start . Format ( format )
2014-03-19 15:21:23 +04:00
}
end = start . Add ( time . Minute * time . Duration ( minutes ) )
2015-02-16 15:44:27 +03:00
endStr = end . Format ( format )
2014-03-19 15:21:23 +04:00
// create sha1 encode string
sh := sha1 . New ( )
2019-06-12 22:41:28 +03:00
_ , _ = sh . Write ( [ ] byte ( data + setting . SecretKey + startStr + endStr + com . ToStr ( minutes ) ) )
2014-03-19 15:21:23 +04:00
encoded := hex . EncodeToString ( sh . Sum ( nil ) )
code := fmt . Sprintf ( "%s%06d%s" , startStr , minutes , encoded )
return code
}
2014-03-15 20:29:49 +04:00
// FileSize calculates the file size and generate user-friendly string.
2014-03-15 20:31:12 +04:00
func FileSize ( s int64 ) string {
2020-02-03 22:50:37 +03:00
return humanize . IBytes ( uint64 ( s ) )
}
// PrettyNumber produces a string form of the given number in base 10 with
// commas after every three orders of magnitud
func PrettyNumber ( v int64 ) string {
return humanize . Comma ( v )
2014-03-15 20:29:49 +04:00
}
2014-03-15 03:34:59 +04:00
// Subtract deals with subtraction of all types of number.
func Subtract ( left interface { } , right interface { } ) interface { } {
var rleft , rright int64
var fleft , fright float64
2016-11-24 10:17:44 +03:00
var isInt = true
2019-05-28 18:45:54 +03:00
switch v := left . ( type ) {
2014-03-15 03:34:59 +04:00
case int :
2019-05-28 18:45:54 +03:00
rleft = int64 ( v )
2014-03-15 03:34:59 +04:00
case int8 :
2019-05-28 18:45:54 +03:00
rleft = int64 ( v )
2014-03-15 03:34:59 +04:00
case int16 :
2019-05-28 18:45:54 +03:00
rleft = int64 ( v )
2014-03-15 03:34:59 +04:00
case int32 :
2019-05-28 18:45:54 +03:00
rleft = int64 ( v )
2014-03-15 03:34:59 +04:00
case int64 :
2019-05-28 18:45:54 +03:00
rleft = v
2014-03-15 03:34:59 +04:00
case float32 :
2019-05-28 18:45:54 +03:00
fleft = float64 ( v )
2014-03-15 03:34:59 +04:00
isInt = false
case float64 :
2019-05-28 18:45:54 +03:00
fleft = v
2014-03-15 03:34:59 +04:00
isInt = false
}
2019-05-28 18:45:54 +03:00
switch v := right . ( type ) {
2014-03-15 03:34:59 +04:00
case int :
2019-05-28 18:45:54 +03:00
rright = int64 ( v )
2014-03-15 03:34:59 +04:00
case int8 :
2019-05-28 18:45:54 +03:00
rright = int64 ( v )
2014-03-15 03:34:59 +04:00
case int16 :
2019-05-28 18:45:54 +03:00
rright = int64 ( v )
2014-03-15 03:34:59 +04:00
case int32 :
2019-05-28 18:45:54 +03:00
rright = int64 ( v )
2014-03-15 03:34:59 +04:00
case int64 :
2019-05-28 18:45:54 +03:00
rright = v
2014-03-15 03:34:59 +04:00
case float32 :
2019-05-28 18:45:54 +03:00
fright = float64 ( v )
2014-03-15 03:34:59 +04:00
isInt = false
case float64 :
2019-05-28 18:45:54 +03:00
fright = v
2014-03-15 03:34:59 +04:00
isInt = false
}
if isInt {
return rleft - rright
}
2016-11-24 10:17:44 +03:00
return fleft + float64 ( rleft ) - ( fright + float64 ( rright ) )
2014-03-15 03:34:59 +04:00
}
2015-08-10 11:52:08 +03:00
2016-01-11 15:41:43 +03: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 {
2016-11-07 23:27:14 +03:00
if length <= 3 {
return "..."
}
if len ( str ) <= length {
2016-01-11 15:41:43 +03:00
return str
}
return str [ : length - 3 ] + "..."
}
2016-03-05 08:51:51 +03:00
// 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 len ( str ) < limit {
return str
}
return str [ : limit ]
}
2015-08-10 11:52:08 +03:00
// StringsToInt64s converts a slice of string to a slice of int64.
2016-12-22 11:58:04 +03:00
func StringsToInt64s ( strs [ ] string ) ( [ ] int64 , error ) {
2015-08-10 11:52:08 +03:00
ints := make ( [ ] int64 , len ( strs ) )
for i := range strs {
2016-12-22 11:58:04 +03:00
n , err := com . StrTo ( strs [ i ] ) . Int64 ( )
if err != nil {
return ints , err
}
ints [ i ] = n
2015-08-10 11:52:08 +03:00
}
2016-12-22 11:58:04 +03:00
return ints , nil
2015-08-10 11:52:08 +03:00
}
2015-08-25 18:22:05 +03: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 {
2016-11-07 23:49:50 +03:00
strs [ i ] = strconv . FormatInt ( ints [ i ] , 10 )
2015-08-25 18:22:05 +03:00
}
return strs
}
2015-08-10 11:52:08 +03:00
// Int64sToMap converts a slice of int64 to a int64 map.
func Int64sToMap ( ints [ ] int64 ) map [ int64 ] bool {
m := make ( map [ int64 ] bool )
for _ , i := range ints {
m [ i ] = true
}
return m
}
2016-02-21 01:10:05 +03:00
2017-09-14 11:16:22 +03:00
// 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-21 01:10:05 +03:00
// IsLetter reports whether the rune is a letter (category L).
// https://github.com/golang/go/blob/master/src/go/scanner/scanner.go#L257
func IsLetter ( ch rune ) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode . IsLetter ( ch )
}
2016-08-31 23:59:23 +03:00
// IsTextFile returns true if file content format is plain text or empty.
2016-08-30 12:08:38 +03:00
func IsTextFile ( data [ ] byte ) bool {
2016-08-31 23:59:23 +03:00
if len ( data ) == 0 {
return true
}
2019-06-12 22:41:28 +03:00
return strings . Contains ( http . DetectContentType ( data ) , "text/" )
2016-02-21 01:10:05 +03:00
}
2017-02-28 07:56:15 +03:00
// IsImageFile detects if data is an image format
2016-08-30 12:08:38 +03:00
func IsImageFile ( data [ ] byte ) bool {
2019-06-12 22:41:28 +03:00
return strings . Contains ( http . DetectContentType ( data ) , "image/" )
2016-02-21 01:10:05 +03:00
}
2016-04-27 04:48:44 +03:00
2017-02-28 07:56:15 +03:00
// IsPDFFile detects if data is a pdf format
2016-08-30 12:08:38 +03:00
func IsPDFFile ( data [ ] byte ) bool {
2019-06-12 22:41:28 +03:00
return strings . Contains ( http . DetectContentType ( data ) , "application/pdf" )
2016-04-27 04:48:44 +03:00
}
2016-12-20 11:09:11 +03:00
2017-02-28 07:56:15 +03:00
// IsVideoFile detects if data is an video format
2016-12-20 11:09:11 +03:00
func IsVideoFile ( data [ ] byte ) bool {
2019-06-12 22:41:28 +03:00
return strings . Contains ( http . DetectContentType ( data ) , "video/" )
2016-12-20 11:09:11 +03:00
}
2018-05-01 10:04:36 +03:00
2018-10-30 05:17:26 +03:00
// IsAudioFile detects if data is an video format
func IsAudioFile ( data [ ] byte ) bool {
2019-06-12 22:41:28 +03:00
return strings . Contains ( http . DetectContentType ( data ) , "audio/" )
2018-10-30 05:17:26 +03:00
}
2018-05-01 10:04:36 +03:00
// 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 ( ) {
2020-07-17 18:15:12 +03:00
return "file-submodule"
2018-05-01 10:04:36 +03:00
}
return "file-symlink-file"
case entry . IsDir ( ) :
return "file-directory"
case entry . IsSubModule ( ) :
return "file-submodule"
}
2020-02-11 20:02:41 +03:00
return "file"
2018-05-01 10:04:36 +03:00
}
2019-04-17 19:06:35 +03:00
// 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
}
2020-09-16 07:07:18 +03:00
// 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 )
}