2014-02-18 17:31:16 -05: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-07 17:22:15 -05:00
package base
2014-02-18 17:31:16 -05:00
import (
"crypto/md5"
2014-03-19 07:21:23 -04:00
"crypto/sha1"
2019-05-04 11:45:34 -04:00
"crypto/sha256"
2014-11-07 14:46:13 -05:00
"encoding/base64"
2014-02-18 17:31:16 -05:00
"encoding/hex"
2014-03-14 02:32:11 -04:00
"fmt"
2016-02-20 17:10:05 -05:00
"net/http"
2017-12-03 03:55:13 -08:00
"net/url"
2019-04-17 10:06:35 -06:00
"os"
2017-12-03 03:55:13 -08:00
"path"
2019-04-17 10:06:35 -06:00
"path/filepath"
"runtime"
2016-11-07 21:49:50 +01:00
"strconv"
2014-03-15 15:28:06 +08:00
"strings"
2014-03-14 02:32:11 -04:00
"time"
2016-02-20 17:10:05 -05:00
"unicode"
2014-05-25 20:11:25 -04:00
2019-03-27 17:33:00 +08:00
"code.gitea.io/gitea/modules/git"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-03-27 17:33:00 +08:00
2020-02-03 21:50:37 +02:00
"github.com/dustin/go-humanize"
2019-08-23 09:40:30 -07:00
"github.com/unknwon/com"
2014-02-18 17:31:16 -05:00
)
2015-11-24 18:49:34 -05:00
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5 ( str string ) string {
2014-02-18 17:31:16 -05:00
m := md5 . New ( )
2019-06-12 21:41:28 +02:00
_ , _ = m . Write ( [ ] byte ( str ) )
2014-02-18 17:31:16 -05:00
return hex . EncodeToString ( m . Sum ( nil ) )
}
2014-03-14 02:32:11 -04:00
2016-11-24 15:17:44 +08:00
// EncodeSha1 string to sha1 hex value.
2014-11-12 06:48:50 -05:00
func EncodeSha1 ( str string ) string {
h := sha1 . New ( )
2019-06-12 21:41:28 +02:00
_ , _ = h . Write ( [ ] byte ( str ) )
2014-11-12 06:48:50 -05:00
return hex . EncodeToString ( h . Sum ( nil ) )
}
2019-05-04 11:45:34 -04:00
// EncodeSha256 string to sha1 hex value.
func EncodeSha256 ( str string ) string {
h := sha256 . New ( )
2019-06-12 21:41:28 +02:00
_ , _ = h . Write ( [ ] byte ( str ) )
2019-05-04 11:45:34 -04:00
return hex . EncodeToString ( h . Sum ( nil ) )
}
2016-11-07 23:14:50 +01:00
// ShortSha is basically just truncating.
// It is DEPRECATED and will be removed in the future.
2015-11-13 17:10:25 -05:00
func ShortSha ( sha1 string ) string {
2016-11-07 21:36:01 +01:00
return TruncateString ( sha1 , 10 )
2015-11-13 17:10:25 -05:00
}
2016-11-24 15:17:44 +08:00
// BasicAuthDecode decode basic auth string
2014-12-10 05:10:26 -05:00
func BasicAuthDecode ( encoded string ) ( string , string , error ) {
s , err := base64 . StdEncoding . DecodeString ( encoded )
2014-11-07 14:46:13 -05:00
if err != nil {
2014-12-10 05:10:26 -05:00
return "" , "" , err
2014-11-07 14:46:13 -05:00
}
2014-12-10 11:01:17 +01:00
auth := strings . SplitN ( string ( s ) , ":" , 2 )
2014-12-10 05:10:26 -05:00
return auth [ 0 ] , auth [ 1 ] , nil
2014-11-07 14:46:13 -05:00
}
2016-11-24 15:17:44 +08:00
// BasicAuthEncode encode basic auth string
2014-11-07 14:46:13 -05:00
func BasicAuthEncode ( username , password string ) string {
return base64 . StdEncoding . EncodeToString ( [ ] byte ( username + ":" + password ) )
}
2016-11-24 15:17:44 +08:00
// VerifyTimeLimitCode verify time limit code
2014-03-19 12: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 00:24:27 -04:00
if d , err := com . StrTo ( lives ) . Int ( ) ; err == nil {
2014-03-19 12: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 14:44:27 +02:00
before , _ := time . ParseInLocation ( "200601021504" , start , time . Local )
2014-03-19 12: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 15:17:44 +08:00
// TimeLimitCodeLength default value for time limit code
2014-03-19 12:50:44 -04:00
const TimeLimitCodeLength = 12 + 6 + 40
2016-11-24 15:17:44 +08:00
// CreateTimeLimitCode create a time limit code
2014-03-19 07: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 14:44:27 +02:00
format := "200601021504"
2014-03-19 07: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 14:44:27 +02:00
startStr = start . Format ( format )
2014-03-19 07:21:23 -04:00
} else {
// use start string create code
startStr = startInf . ( string )
2015-02-16 14:44:27 +02:00
start , _ = time . ParseInLocation ( format , startStr , time . Local )
startStr = start . Format ( format )
2014-03-19 07:21:23 -04:00
}
end = start . Add ( time . Minute * time . Duration ( minutes ) )
2015-02-16 14:44:27 +02:00
endStr = end . Format ( format )
2014-03-19 07:21:23 -04:00
// create sha1 encode string
sh := sha1 . New ( )
2019-06-12 21:41:28 +02:00
_ , _ = sh . Write ( [ ] byte ( data + setting . SecretKey + startStr + endStr + com . ToStr ( minutes ) ) )
2014-03-19 07:21:23 -04:00
encoded := hex . EncodeToString ( sh . Sum ( nil ) )
code := fmt . Sprintf ( "%s%06d%s" , startStr , minutes , encoded )
return code
}
2016-02-14 23:14:55 -05:00
// HashEmail hashes email address to MD5 string.
// https://en.gravatar.com/site/implement/hash/
func HashEmail ( email string ) string {
2016-11-06 01:53:11 +01:00
return EncodeMD5 ( strings . ToLower ( strings . TrimSpace ( email ) ) )
2016-02-14 23:14:55 -05:00
}
2017-06-29 12:11:34 -04:00
// DefaultAvatarLink the default avatar link
2017-06-29 12:10:33 -04:00
func DefaultAvatarLink ( ) string {
return setting . AppSubURL + "/img/avatar_default.png"
}
2017-12-03 03:55:13 -08:00
// DefaultAvatarSize is a sentinel value for the default avatar size, as
// determined by the avatar-hosting service.
const DefaultAvatarSize = - 1
// libravatarURL returns the URL for the given email. This function should only
// be called if a federated avatar service is enabled.
func libravatarURL ( email string ) ( * url . URL , error ) {
urlStr , err := setting . LibravatarService . FromEmail ( email )
if err != nil {
2019-04-02 08:48:31 +01:00
log . Error ( "LibravatarService.FromEmail(email=%s): error %v" , email , err )
2017-12-03 03:55:13 -08:00
return nil , err
}
u , err := url . Parse ( urlStr )
if err != nil {
2019-04-02 08:48:31 +01:00
log . Error ( "Failed to parse libravatar url(%s): error %v" , urlStr , err )
2017-12-03 03:55:13 -08:00
return nil , err
}
return u , nil
}
// SizedAvatarLink returns a sized link to the avatar for the given email
// address.
func SizedAvatarLink ( email string , size int ) string {
var avatarURL * url . URL
2016-08-07 11:01:47 -07:00
if setting . EnableFederatedAvatar && setting . LibravatarService != nil {
2017-12-03 03:55:13 -08:00
var err error
avatarURL , err = libravatarURL ( email )
2017-06-29 10:30:58 -04:00
if err != nil {
2017-06-29 12:10:33 -04:00
return DefaultAvatarLink ( )
2017-06-29 10:30:58 -04:00
}
2017-12-03 03:55:13 -08:00
} else if ! setting . DisableGravatar {
// copy GravatarSourceURL, because we will modify its Path.
copyOfGravatarSourceURL := * setting . GravatarSourceURL
avatarURL = & copyOfGravatarSourceURL
avatarURL . Path = path . Join ( avatarURL . Path , HashEmail ( email ) )
} else {
return DefaultAvatarLink ( )
2016-08-07 11:01:47 -07:00
}
2016-11-07 21:13:38 +01:00
2017-12-03 03:55:13 -08:00
vals := avatarURL . Query ( )
vals . Set ( "d" , "identicon" )
if size != DefaultAvatarSize {
vals . Set ( "s" , strconv . Itoa ( size ) )
2014-03-25 12:12:27 -04:00
}
2017-12-03 03:55:13 -08:00
avatarURL . RawQuery = vals . Encode ( )
return avatarURL . String ( )
}
2016-11-07 21:13:38 +01:00
2020-03-27 12:34:39 +00:00
// SizedAvatarLinkWithDomain returns a sized link to the avatar for the given email
// address.
func SizedAvatarLinkWithDomain ( email string , size int ) string {
var avatarURL * url . URL
if setting . EnableFederatedAvatar && setting . LibravatarService != nil {
var err error
avatarURL , err = libravatarURL ( email )
if err != nil {
return DefaultAvatarLink ( )
}
} else if ! setting . DisableGravatar {
// copy GravatarSourceURL, because we will modify its Path.
copyOfGravatarSourceURL := * setting . GravatarSourceURL
avatarURL = & copyOfGravatarSourceURL
avatarURL . Path = path . Join ( avatarURL . Path , HashEmail ( email ) )
} else {
return DefaultAvatarLink ( )
}
vals := avatarURL . Query ( )
vals . Set ( "d" , "identicon" )
if size != DefaultAvatarSize {
vals . Set ( "s" , strconv . Itoa ( size ) )
}
avatarURL . RawQuery = vals . Encode ( )
return avatarURL . String ( )
2014-03-17 00:57:18 -04:00
}
2014-03-15 12:29:49 -04:00
// FileSize calculates the file size and generate user-friendly string.
2014-03-15 12:31:12 -04:00
func FileSize ( s int64 ) string {
2020-02-03 21:50:37 +02: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 12:29:49 -04:00
}
2014-03-14 19: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 15:17:44 +08:00
var isInt = true
2019-05-28 23:45:54 +08:00
switch v := left . ( type ) {
2014-03-14 19:34:59 -04:00
case int :
2019-05-28 23:45:54 +08:00
rleft = int64 ( v )
2014-03-14 19:34:59 -04:00
case int8 :
2019-05-28 23:45:54 +08:00
rleft = int64 ( v )
2014-03-14 19:34:59 -04:00
case int16 :
2019-05-28 23:45:54 +08:00
rleft = int64 ( v )
2014-03-14 19:34:59 -04:00
case int32 :
2019-05-28 23:45:54 +08:00
rleft = int64 ( v )
2014-03-14 19:34:59 -04:00
case int64 :
2019-05-28 23:45:54 +08:00
rleft = v
2014-03-14 19:34:59 -04:00
case float32 :
2019-05-28 23:45:54 +08:00
fleft = float64 ( v )
2014-03-14 19:34:59 -04:00
isInt = false
case float64 :
2019-05-28 23:45:54 +08:00
fleft = v
2014-03-14 19:34:59 -04:00
isInt = false
}
2019-05-28 23:45:54 +08:00
switch v := right . ( type ) {
2014-03-14 19:34:59 -04:00
case int :
2019-05-28 23:45:54 +08:00
rright = int64 ( v )
2014-03-14 19:34:59 -04:00
case int8 :
2019-05-28 23:45:54 +08:00
rright = int64 ( v )
2014-03-14 19:34:59 -04:00
case int16 :
2019-05-28 23:45:54 +08:00
rright = int64 ( v )
2014-03-14 19:34:59 -04:00
case int32 :
2019-05-28 23:45:54 +08:00
rright = int64 ( v )
2014-03-14 19:34:59 -04:00
case int64 :
2019-05-28 23:45:54 +08:00
rright = v
2014-03-14 19:34:59 -04:00
case float32 :
2019-05-28 23:45:54 +08:00
fright = float64 ( v )
2014-03-14 19:34:59 -04:00
isInt = false
case float64 :
2019-05-28 23:45:54 +08:00
fright = v
2014-03-14 19:34:59 -04:00
isInt = false
}
if isInt {
return rleft - rright
}
2016-11-24 15:17:44 +08:00
return fleft + float64 ( rleft ) - ( fright + float64 ( rright ) )
2014-03-14 19:34:59 -04:00
}
2015-08-10 16:52:08 +08:00
2016-01-11 20:41:43 +08: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 21:27:14 +01:00
if length <= 3 {
return "..."
}
if len ( str ) <= length {
2016-01-11 20:41:43 +08:00
return str
}
return str [ : length - 3 ] + "..."
}
2016-03-05 00:51:51 -05: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 16:52:08 +08:00
// StringsToInt64s converts a slice of string to a slice of int64.
2016-12-22 03:58:04 -05:00
func StringsToInt64s ( strs [ ] string ) ( [ ] int64 , error ) {
2015-08-10 16:52:08 +08:00
ints := make ( [ ] int64 , len ( strs ) )
for i := range strs {
2016-12-22 03:58:04 -05:00
n , err := com . StrTo ( strs [ i ] ) . Int64 ( )
if err != nil {
return ints , err
}
ints [ i ] = n
2015-08-10 16:52:08 +08:00
}
2016-12-22 03:58:04 -05:00
return ints , nil
2015-08-10 16:52:08 +08:00
}
2015-08-25 23:22:05 +08: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 21:49:50 +01:00
strs [ i ] = strconv . FormatInt ( ints [ i ] , 10 )
2015-08-25 23:22:05 +08:00
}
return strs
}
2015-08-10 16:52:08 +08: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-20 17:10:05 -05:00
2017-09-14 16:16:22 +08: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-20 17:10:05 -05: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 13:59:23 -07:00
// IsTextFile returns true if file content format is plain text or empty.
2016-08-30 02:08:38 -07:00
func IsTextFile ( data [ ] byte ) bool {
2016-08-31 13:59:23 -07:00
if len ( data ) == 0 {
return true
}
2019-06-12 21:41:28 +02:00
return strings . Contains ( http . DetectContentType ( data ) , "text/" )
2016-02-20 17:10:05 -05:00
}
2017-02-28 12:56:15 +08:00
// IsImageFile detects if data is an image format
2016-08-30 02:08:38 -07:00
func IsImageFile ( data [ ] byte ) bool {
2019-06-12 21:41:28 +02:00
return strings . Contains ( http . DetectContentType ( data ) , "image/" )
2016-02-20 17:10:05 -05:00
}
2016-04-27 03:48:44 +02:00
2017-02-28 12:56:15 +08:00
// IsPDFFile detects if data is a pdf format
2016-08-30 02:08:38 -07:00
func IsPDFFile ( data [ ] byte ) bool {
2019-06-12 21:41:28 +02:00
return strings . Contains ( http . DetectContentType ( data ) , "application/pdf" )
2016-04-27 03:48:44 +02:00
}
2016-12-20 09:09:11 +01:00
2017-02-28 12:56:15 +08:00
// IsVideoFile detects if data is an video format
2016-12-20 09:09:11 +01:00
func IsVideoFile ( data [ ] byte ) bool {
2019-06-12 21:41:28 +02:00
return strings . Contains ( http . DetectContentType ( data ) , "video/" )
2016-12-20 09:09:11 +01:00
}
2018-05-01 17:04:36 +10:00
2018-10-30 03:17:26 +01:00
// IsAudioFile detects if data is an video format
func IsAudioFile ( data [ ] byte ) bool {
2019-06-12 21:41:28 +02:00
return strings . Contains ( http . DetectContentType ( data ) , "audio/" )
2018-10-30 03:17:26 +01:00
}
2018-05-01 17:04:36 +10: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 17:15:12 +02:00
return "file-submodule"
2018-05-01 17:04:36 +10:00
}
return "file-symlink-file"
case entry . IsDir ( ) :
return "file-directory"
case entry . IsSubModule ( ) :
return "file-submodule"
}
2020-02-11 11:02:41 -06:00
return "file"
2018-05-01 17:04:36 +10:00
}
2019-04-17 10:06:35 -06: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
}