2014-02-19 02:31:16 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-02-19 02:31:16 +04:00
2014-03-08 02:22:15 +04:00
package base
2014-02-19 02:31:16 +04:00
import (
2024-05-20 18:12:50 +03:00
"crypto/hmac"
2014-03-19 15:21:23 +04:00
"crypto/sha1"
2024-02-25 16:32:13 +03:00
"crypto/sha256"
2024-05-20 18:12:50 +03:00
"crypto/subtle"
2014-11-07 22:46:13 +03:00
"encoding/base64"
2014-02-19 02:31:16 +04:00
"encoding/hex"
2020-12-18 04:51:28 +03:00
"errors"
2014-03-14 10:32:11 +04:00
"fmt"
2024-05-20 18:12:50 +03:00
"hash"
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"
2021-05-13 10:50:57 +03:00
"unicode/utf8"
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"
2014-02-19 02:31:16 +04:00
)
2022-11-26 19:21:54 +03:00
// EncodeSha256 string to sha256 hex value.
2019-05-04 18:45:34 +03:00
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
}
2024-07-23 15:43:03 +03:00
if username , password , ok := strings . Cut ( string ( s ) , ":" ) ; ok {
return username , password , nil
2020-12-18 04:51:28 +03:00
}
2024-07-23 15:43:03 +03:00
return "" , "" , errors . New ( "invalid basic authentication" )
2014-11-07 22:46:13 +03:00
}
2016-11-24 10:17:44 +03:00
// VerifyTimeLimitCode verify time limit code
2024-05-20 18:12:50 +03:00
func VerifyTimeLimitCode ( now time . Time , data string , minutes int , code string ) bool {
2014-03-19 20:50:44 +04:00
if len ( code ) <= 18 {
return false
}
2024-05-20 18:12:50 +03:00
startTimeStr := code [ : 12 ]
aliveTimeStr := code [ 12 : 18 ]
aliveTime , _ := strconv . Atoi ( aliveTimeStr ) // no need to check err, if anything wrong, the following code check will fail soon
2014-03-19 20:50:44 +04:00
2024-05-20 18:12:50 +03:00
// check code
retCode := CreateTimeLimitCode ( data , aliveTime , startTimeStr , nil )
if subtle . ConstantTimeCompare ( [ ] byte ( retCode ) , [ ] byte ( code ) ) != 1 {
retCode = CreateTimeLimitCode ( data , aliveTime , startTimeStr , sha1 . New ( ) ) // TODO: this is only for the support of legacy codes, remove this in/after 1.23
if subtle . ConstantTimeCompare ( [ ] byte ( retCode ) , [ ] byte ( code ) ) != 1 {
return false
2014-03-19 20:50:44 +04:00
}
}
2024-05-20 18:12:50 +03:00
// check time is expired or not: startTime <= now && now < startTime + minutes
startTime , _ := time . ParseInLocation ( "200601021504" , startTimeStr , time . Local )
return ( startTime . Before ( now ) || startTime . Equal ( now ) ) && now . Before ( startTime . Add ( time . Minute * time . Duration ( minutes ) ) )
2014-03-19 20:50:44 +04:00
}
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
2024-05-20 18:12:50 +03:00
// CreateTimeLimitCode create a time-limited code.
// Format: 12 length date time string + 6 minutes string (not used) + 40 hash string, some other code depends on this fixed length
// If h is nil, then use the default hmac hash.
func CreateTimeLimitCode [ T time . Time | string ] ( data string , minutes int , startTimeGeneric T , h hash . Hash ) string {
const format = "200601021504"
2014-03-19 15:21:23 +04:00
2024-05-20 18:12:50 +03:00
var start time . Time
var startTimeAny any = startTimeGeneric
if t , ok := startTimeAny . ( time . Time ) ; ok {
start = t
2014-03-19 15:21:23 +04:00
} else {
2024-05-20 18:12:50 +03:00
var err error
start , err = time . ParseInLocation ( format , startTimeAny . ( string ) , time . Local )
if err != nil {
return "" // return an invalid code because the "parse" failed
}
2014-03-19 15:21:23 +04:00
}
2024-05-20 18:12:50 +03:00
startStr := start . Format ( format )
end := start . Add ( time . Minute * time . Duration ( minutes ) )
2014-03-19 15:21:23 +04:00
2024-05-20 18:12:50 +03:00
if h == nil {
h = hmac . New ( sha1 . New , setting . GetGeneralTokenSigningSecret ( ) )
}
_ , _ = fmt . Fprintf ( h , "%s%s%s%s%d" , data , hex . EncodeToString ( setting . GetGeneralTokenSigningSecret ( ) ) , startStr , end . Format ( format ) , minutes )
encoded := hex . EncodeToString ( h . Sum ( nil ) )
2014-03-19 15:21:23 +04:00
code := fmt . Sprintf ( "%s%06d%s" , startStr , minutes , encoded )
2024-05-20 18:12:50 +03:00
if len ( code ) != TimeLimitCodeLength {
panic ( "there is a hard requirement for the length of time-limited code" ) // it shouldn't happen
}
2014-03-19 15:21:23 +04:00
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 ) )
}
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 "..."
}
2021-05-13 10:50:57 +03:00
if utf8 . RuneCountInString ( str ) <= length {
2016-01-11 15:41:43 +03:00
return str
}
2021-05-13 10:50:57 +03:00
return string ( [ ] rune ( str ) [ : length - 3 ] ) + "..."
2016-01-11 15:41:43 +03:00
}
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 {
2021-05-13 10:50:57 +03:00
if utf8 . RuneCountInString ( str ) < limit {
2016-03-05 08:51:51 +03:00
return str
}
2021-05-13 10:50:57 +03:00
return string ( [ ] rune ( str ) [ : limit ] )
2016-03-05 08:51:51 +03:00
}
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 ) {
2024-03-21 18:07:35 +03:00
if strs == nil {
return nil , nil
}
ints := make ( [ ] int64 , 0 , len ( strs ) )
for _ , s := range strs {
2024-11-10 23:07:54 +03:00
if s == "" {
continue
}
2024-03-21 18:07:35 +03:00
n , err := strconv . ParseInt ( s , 10 , 64 )
2016-12-22 11:58:04 +03:00
if err != nil {
2024-03-21 18:07:35 +03:00
return nil , err
2016-12-22 11:58:04 +03:00
}
2024-03-21 18:07:35 +03:00
ints = append ( ints , 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
}
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 ( ) {
2023-06-23 01:05:52 +03:00
return "file-directory-symlink"
2018-05-01 10:04:36 +03:00
}
return "file-symlink-file"
case entry . IsDir ( ) :
2022-04-01 03:15:46 +03:00
return "file-directory-fill"
2018-05-01 10:04:36 +03:00
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
2023-07-04 21:36:08 +03:00
func FormatNumberSI ( data any ) string {
2020-09-16 07:07:18 +03:00
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 )
}