2014-04-10 20:20:58 +02:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-05-29 09:17:15 +02:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2014-04-10 20:20:58 +02:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-05-26 02:11:25 +02:00
package setting
2014-04-10 20:20:58 +02:00
import (
2016-12-26 02:16:37 +01:00
"encoding/base64"
2019-04-02 09:48:31 +02:00
"fmt"
2019-03-13 23:49:43 +01:00
"io"
"io/ioutil"
2017-10-02 15:55:09 +02:00
"net"
2014-09-14 19:35:22 +02:00
"net/url"
2014-04-10 20:20:58 +02:00
"os"
"os/exec"
"path"
"path/filepath"
2019-04-28 21:48:46 +02:00
"runtime"
2016-08-11 23:46:33 +02:00
"strconv"
2014-04-10 20:20:58 +02:00
"strings"
2014-07-24 22:31:59 +02:00
"time"
2014-04-10 20:20:58 +02:00
2018-02-18 19:14:37 +01:00
"code.gitea.io/gitea/modules/generate"
2019-03-27 10:33:00 +01:00
"code.gitea.io/gitea/modules/git"
2016-12-22 19:12:23 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
2017-01-23 23:44:23 +01:00
2019-01-09 18:22:57 +01:00
shellquote "github.com/kballard/go-shellquote"
version "github.com/mcuadros/go-version"
2019-08-23 18:40:30 +02:00
"github.com/unknwon/cae/zip"
"github.com/unknwon/com"
2019-01-09 18:22:57 +01:00
ini "gopkg.in/ini.v1"
2016-11-04 12:57:27 +01:00
"strk.kbt.io/projects/go/libravatar"
2014-04-10 20:20:58 +02:00
)
2016-11-27 11:14:25 +01:00
// Scheme describes protocol types
2014-05-26 02:11:25 +02:00
type Scheme string
2014-04-14 00:12:07 +02:00
2016-11-27 11:14:25 +01:00
// enumerates all the scheme types
2014-05-26 02:11:25 +02:00
const (
2016-11-27 11:14:25 +01:00
HTTP Scheme = "http"
HTTPS Scheme = "https"
FCGI Scheme = "fcgi"
UnixSocket Scheme = "unix"
2014-05-26 02:11:25 +02:00
)
2014-04-10 20:20:58 +02:00
2016-11-27 11:14:25 +01:00
// LandingPage describes the default page
2014-11-25 00:47:59 +01:00
type LandingPage string
2016-11-27 11:14:25 +01:00
// enumerates all the landing page types
2014-11-25 00:47:59 +01:00
const (
2017-11-20 08:48:09 +01:00
LandingPageHome LandingPage = "/"
LandingPageExplore LandingPage = "/explore"
LandingPageOrganizations LandingPage = "/explore/organizations"
2014-11-25 00:47:59 +01:00
)
2018-07-05 06:13:05 +02:00
// enumerates all the types of captchas
const (
ImageCaptcha = "image"
ReCaptcha = "recaptcha"
)
2016-11-27 11:14:25 +01:00
// settings
2014-04-10 20:20:58 +02:00
var (
2016-11-27 11:14:25 +01:00
// AppVer settings
2016-01-31 21:38:20 +01:00
AppVer string
2017-02-28 01:40:02 +01:00
AppBuiltWith string
2016-01-31 21:38:20 +01:00
AppName string
2016-11-27 11:14:25 +01:00
AppURL string
AppSubURL string
AppSubURLDepth int // Number of slashes
2016-01-31 21:38:20 +01:00
AppPath string
2016-03-10 02:53:42 +01:00
AppDataPath string
2017-11-03 09:56:20 +01:00
AppWorkPath string
2014-05-26 02:11:25 +02:00
2015-12-18 04:31:34 +01:00
// Server settings
2016-08-11 23:46:33 +02:00
Protocol Scheme
Domain string
2016-11-27 11:14:25 +01:00
HTTPAddr string
HTTPPort string
2016-08-11 23:46:33 +02:00
LocalURL string
2017-12-25 23:23:43 +01:00
RedirectOtherPort bool
PortToRedirect string
2016-08-11 23:46:33 +02:00
OfflineMode bool
2016-11-27 11:14:25 +01:00
CertFile string
KeyFile string
2016-08-11 23:46:33 +02:00
StaticRootPath string
2019-10-15 00:05:57 +02:00
StaticCacheTime time . Duration
2016-08-11 23:46:33 +02:00
EnableGzip bool
2016-08-18 01:10:07 +02:00
LandingPageURL LandingPage
2016-08-11 23:46:33 +02:00
UnixSocketPermission uint32
2017-02-05 14:06:25 +01:00
EnablePprof bool
2018-08-07 20:49:18 +02:00
PprofDataPath string
2018-08-21 15:56:50 +02:00
EnableLetsEncrypt bool
LetsEncryptTOS bool
LetsEncryptDirectory string
LetsEncryptEmail string
2019-10-15 15:39:51 +02:00
GracefulRestartable bool
GracefulHammerTime time . Duration
2019-11-21 19:32:02 +01:00
StartupTimeout time . Duration
2019-10-22 14:11:01 +02:00
StaticURLPrefix string
2014-05-26 02:11:25 +02:00
2016-12-29 11:51:15 +01:00
SSH = struct {
2018-11-01 14:41:07 +01:00
Disabled bool ` ini:"DISABLE_SSH" `
StartBuiltinServer bool ` ini:"START_SSH_SERVER" `
BuiltinServerUser string ` ini:"BUILTIN_SSH_SERVER_USER" `
Domain string ` ini:"SSH_DOMAIN" `
Port int ` ini:"SSH_PORT" `
ListenHost string ` ini:"SSH_LISTEN_HOST" `
ListenPort int ` ini:"SSH_LISTEN_PORT" `
RootPath string ` ini:"SSH_ROOT_PATH" `
ServerCiphers [ ] string ` ini:"SSH_SERVER_CIPHERS" `
ServerKeyExchanges [ ] string ` ini:"SSH_SERVER_KEY_EXCHANGES" `
ServerMACs [ ] string ` ini:"SSH_SERVER_MACS" `
KeyTestPath string ` ini:"SSH_KEY_TEST_PATH" `
KeygenPath string ` ini:"SSH_KEYGEN_PATH" `
AuthorizedKeysBackup bool ` ini:"SSH_AUTHORIZED_KEYS_BACKUP" `
MinimumKeySizeCheck bool ` ini:"-" `
MinimumKeySizes map [ string ] int ` ini:"-" `
CreateAuthorizedKeysFile bool ` ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE" `
ExposeAnonymous bool ` ini:"SSH_EXPOSE_ANONYMOUS" `
2016-12-29 11:51:15 +01:00
} {
Disabled : false ,
StartBuiltinServer : false ,
2017-02-14 17:07:37 +01:00
Domain : "" ,
2016-12-29 11:51:15 +01:00
Port : 22 ,
2017-10-23 17:20:44 +02:00
ServerCiphers : [ ] string { "aes128-ctr" , "aes192-ctr" , "aes256-ctr" , "aes128-gcm@openssh.com" , "arcfour256" , "arcfour128" } ,
2017-11-02 16:26:41 +01:00
ServerKeyExchanges : [ ] string { "diffie-hellman-group1-sha1" , "diffie-hellman-group14-sha1" , "ecdh-sha2-nistp256" , "ecdh-sha2-nistp384" , "ecdh-sha2-nistp521" , "curve25519-sha256@libssh.org" } ,
ServerMACs : [ ] string { "hmac-sha2-256-etm@openssh.com" , "hmac-sha2-256" , "hmac-sha1" , "hmac-sha1-96" } ,
2016-12-29 11:51:15 +01:00
KeygenPath : "ssh-keygen" ,
2016-02-28 02:48:39 +01:00
}
2016-12-26 02:16:37 +01:00
LFS struct {
2018-05-29 10:07:16 +02:00
StartServer bool ` ini:"LFS_START_SERVER" `
ContentPath string ` ini:"LFS_CONTENT_PATH" `
JWTSecretBase64 string ` ini:"LFS_JWT_SECRET" `
JWTSecretBytes [ ] byte ` ini:"-" `
HTTPAuthExpiry time . Duration ` ini:"LFS_HTTP_AUTH_EXPIRY" `
2016-12-26 02:16:37 +01:00
}
2015-12-18 04:31:34 +01:00
// Security settings
2019-11-14 23:39:48 +01:00
InstallLock bool
SecretKey string
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
OnlyAllowPushIfGiteaEnvironmentSet bool
PasswordComplexity [ ] string
PasswordHashAlgo string
2014-04-10 20:20:58 +02:00
2015-12-18 04:31:34 +01:00
// UI settings
2016-12-23 08:18:05 +01:00
UI = struct {
2019-08-25 19:06:36 +02:00
ExplorePagingNum int
IssuePagingNum int
RepoSearchPagingNum int
FeedMaxCommitNum int
GraphMaxCommitNum int
CodeCommentLines int
ReactionMaxUserNum int
ThemeColorMetaTag string
MaxDisplayFileSize int64
ShowUserEmail bool
DefaultShowFullName bool
DefaultTheme string
Themes [ ] string
2019-12-01 23:57:24 +01:00
Reactions [ ] string
2019-08-25 19:06:36 +02:00
SearchRepoDescription bool
2019-11-21 21:06:23 +01:00
UseServiceWorker bool
2016-07-23 18:23:54 +02:00
Admin struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} ` ini:"ui.admin" `
User struct {
RepoPagingNum int
} ` ini:"ui.user" `
2017-04-01 03:03:01 +02:00
Meta struct {
Author string
Description string
Keywords string
} ` ini:"ui.meta" `
2016-12-23 08:18:05 +01:00
} {
2017-10-27 08:10:54 +02:00
ExplorePagingNum : 20 ,
IssuePagingNum : 10 ,
RepoSearchPagingNum : 10 ,
FeedMaxCommitNum : 5 ,
2018-07-23 16:12:06 +02:00
GraphMaxCommitNum : 100 ,
2018-08-06 06:43:22 +02:00
CodeCommentLines : 4 ,
2017-12-04 00:14:26 +01:00
ReactionMaxUserNum : 10 ,
2017-10-27 08:10:54 +02:00
ThemeColorMetaTag : ` #6cc644 ` ,
MaxDisplayFileSize : 8388608 ,
2018-07-05 23:25:04 +02:00
DefaultTheme : ` gitea ` ,
2019-01-09 18:22:57 +01:00
Themes : [ ] string { ` gitea ` , ` arc-green ` } ,
2019-12-01 23:57:24 +01:00
Reactions : [ ] string { ` +1 ` , ` -1 ` , ` laugh ` , ` hooray ` , ` confused ` , ` heart ` , ` rocket ` , ` eyes ` } ,
2016-12-23 08:18:05 +01:00
Admin : struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} {
UserPagingNum : 50 ,
RepoPagingNum : 50 ,
NoticePagingNum : 25 ,
OrgPagingNum : 50 ,
} ,
User : struct {
RepoPagingNum int
} {
RepoPagingNum : 15 ,
} ,
2017-04-01 03:03:01 +02:00
Meta : struct {
Author string
Description string
Keywords string
} {
Author : "Gitea - Git with a cup of tea" ,
Description : "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go" ,
Keywords : "go,git,self-hosted,gitea" ,
} ,
2016-07-23 18:23:54 +02:00
}
2014-04-10 20:20:58 +02:00
2017-03-15 01:52:01 +01:00
// Markdown settings
2016-12-23 08:18:05 +01:00
Markdown = struct {
2015-09-01 14:32:02 +02:00
EnableHardLineBreak bool
2016-01-14 07:20:03 +01:00
CustomURLSchemes [ ] string ` ini:"CUSTOM_URL_SCHEMES" `
2016-08-12 11:29:29 +02:00
FileExtensions [ ] string
2016-12-23 08:18:05 +01:00
} {
EnableHardLineBreak : false ,
FileExtensions : strings . Split ( ".md,.markdown,.mdown,.mkd" , "," ) ,
2015-09-01 14:32:02 +02:00
}
2017-02-14 13:16:00 +01:00
// Admin settings
Admin struct {
DisableRegularOrgCreation bool
2019-08-29 16:05:42 +02:00
DefaultEmailNotification string
2017-02-14 13:16:00 +01:00
}
2015-12-18 04:31:34 +01:00
// Picture settings
2019-06-02 08:40:12 +02:00
AvatarUploadPath string
AvatarMaxWidth int
AvatarMaxHeight int
GravatarSource string
GravatarSourceURL * url . URL
DisableGravatar bool
EnableFederatedAvatar bool
LibravatarService * libravatar . Libravatar
AvatarMaxFileSize int64
RepositoryAvatarUploadPath string
RepositoryAvatarFallback string
RepositoryAvatarFallbackImage string
2014-05-26 02:11:25 +02:00
2015-12-18 04:31:34 +01:00
// Log settings
2019-02-06 04:06:41 +01:00
LogLevel string
2019-04-02 09:48:31 +02:00
StacktraceLogLevel string
2019-02-06 04:06:41 +01:00
LogRootPath string
2019-04-02 09:48:31 +02:00
LogDescriptions = make ( map [ string ] * LogDescription )
2019-02-06 04:06:41 +01:00
RedirectMacaronLog bool
2019-04-02 09:48:31 +02:00
DisableRouterLog bool
RouterLogLevel log . Level
RouterLogMode string
EnableAccessLog bool
AccessLogTemplate string
EnableXORMLog bool
2014-04-10 20:20:58 +02:00
2015-12-18 04:31:34 +01:00
// Attachment settings
2014-07-23 21:15:47 +02:00
AttachmentPath string
AttachmentAllowedTypes string
2014-07-24 15:19:59 +02:00
AttachmentMaxSize int64
AttachmentMaxFiles int
2014-07-24 15:51:40 +02:00
AttachmentEnabled bool
2014-07-23 21:15:47 +02:00
2015-12-18 04:31:34 +01:00
// Time settings
2014-07-24 22:31:59 +02:00
TimeFormat string
2019-08-15 16:46:21 +02:00
// UILocation is the location on the UI, so that we can display the time on UI.
DefaultUILocation = time . Local
2014-07-24 22:31:59 +02:00
2019-07-12 15:57:31 +02:00
CSRFCookieName = "_csrf"
CSRFCookieHTTPOnly = true
2014-04-10 20:20:58 +02:00
2016-08-10 08:58:15 +02:00
// Mirror settings
2017-04-08 17:27:26 +02:00
Mirror struct {
DefaultInterval time . Duration
MinInterval time . Duration
2016-08-10 08:47:16 +02:00
}
2016-07-04 11:27:06 +02:00
// API settings
2016-12-23 08:18:05 +01:00
API = struct {
2019-02-06 19:19:26 +01:00
EnableSwagger bool
2019-06-12 23:07:24 +02:00
SwaggerURL string
2019-02-06 19:19:26 +01:00
MaxResponseItems int
DefaultPagingNum int
DefaultGitTreesPerPage int
2019-04-17 18:06:35 +02:00
DefaultMaxBlobSize int64
2016-12-23 08:18:05 +01:00
} {
2019-02-06 19:19:26 +01:00
EnableSwagger : true ,
2019-06-12 23:07:24 +02:00
SwaggerURL : "" ,
2019-02-06 19:19:26 +01:00
MaxResponseItems : 50 ,
DefaultPagingNum : 30 ,
DefaultGitTreesPerPage : 1000 ,
2019-04-17 18:06:35 +02:00
DefaultMaxBlobSize : 10485760 ,
2016-07-04 11:27:06 +02:00
}
2019-03-08 17:42:50 +01:00
OAuth2 = struct {
Enable bool
AccessTokenExpirationTime int64
RefreshTokenExpirationTime int64
2019-04-12 09:50:21 +02:00
InvalidateRefreshTokens bool
2019-03-08 17:42:50 +01:00
JWTSecretBytes [ ] byte ` ini:"-" `
JWTSecretBase64 string ` ini:"JWT_SECRET" `
} {
Enable : true ,
AccessTokenExpirationTime : 3600 ,
RefreshTokenExpirationTime : 730 ,
2019-04-12 09:50:21 +02:00
InvalidateRefreshTokens : false ,
2019-03-08 17:42:50 +01:00
}
2018-05-19 16:12:37 +02:00
U2F = struct {
AppID string
TrustedFacets [ ] string
} { }
2018-11-05 04:20:00 +01:00
// Metrics settings
Metrics = struct {
Enabled bool
Token string
} {
Enabled : false ,
Token : "" ,
}
2015-12-18 04:31:34 +01:00
// I18n settings
2016-11-27 11:14:25 +01:00
Langs [ ] string
Names [ ] string
dateLangs map [ string ] string
2014-09-17 20:22:51 +02:00
2017-03-15 01:52:01 +01:00
// Highlight settings are loaded in modules/template/highlight.go
2015-12-18 04:31:34 +01:00
// Other settings
2016-09-01 07:01:32 +02:00
ShowFooterBranding bool
ShowFooterVersion bool
ShowFooterTemplateLoadTime bool
2015-03-23 15:19:19 +01:00
2015-12-18 04:31:34 +01:00
// Global setting objects
2019-08-24 11:24:45 +02:00
Cfg * ini . File
CustomPath string // Custom directory path
CustomConf string
CustomPID string
ProdMode bool
RunUser string
IsWindows bool
HasRobotsTxt bool
InternalToken string // internal access token
// UILocation is the location on the UI, so that we can display the time on UI.
// Currently only show the default time.Local, it could be added to app.ini after UI is ready
UILocation = time . Local
2014-04-10 20:20:58 +02:00
)
2016-08-18 01:10:07 +02:00
// DateLang transforms standard language locale name to corresponding value in datetime plugin.
2015-08-05 09:24:26 +02:00
func DateLang ( lang string ) string {
name , ok := dateLangs [ lang ]
if ok {
return name
}
return "en"
}
2017-11-03 09:56:20 +01:00
func getAppPath ( ) ( string , error ) {
var appPath string
var err error
if IsWindows && filepath . IsAbs ( os . Args [ 0 ] ) {
appPath = filepath . Clean ( os . Args [ 0 ] )
} else {
appPath , err = exec . LookPath ( os . Args [ 0 ] )
2017-09-12 14:27:44 +02:00
}
2017-11-03 09:56:20 +01:00
2014-05-26 02:11:25 +02:00
if err != nil {
return "" , err
}
2017-11-03 09:56:20 +01:00
appPath , err = filepath . Abs ( appPath )
if err != nil {
return "" , err
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
return strings . Replace ( appPath , "\\" , "/" , - 1 ) , err
}
func getWorkPath ( appPath string ) string {
2019-04-29 20:08:21 +02:00
workPath := AppWorkPath
2017-11-03 09:56:20 +01:00
2019-04-29 20:08:21 +02:00
if giteaWorkPath , ok := os . LookupEnv ( "GITEA_WORK_DIR" ) ; ok {
2017-11-03 09:56:20 +01:00
workPath = giteaWorkPath
2019-04-29 20:08:21 +02:00
}
if len ( workPath ) == 0 {
2017-11-03 09:56:20 +01:00
i := strings . LastIndex ( appPath , "/" )
if i == - 1 {
workPath = appPath
} else {
workPath = appPath [ : i ]
}
}
return strings . Replace ( workPath , "\\" , "/" , - 1 )
2015-11-08 22:59:56 +01:00
}
func init ( ) {
2019-04-28 21:48:46 +02:00
IsWindows = runtime . GOOS == "windows"
2019-04-02 09:48:31 +02:00
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
log . NewLogger ( 0 , "console" , "console" , fmt . Sprintf ( ` { "level": "trace", "colorize": %t, "stacktraceLevel": "none"} ` , log . CanColorStdout ) )
2015-11-08 22:59:56 +01:00
var err error
2017-11-03 09:56:20 +01:00
if AppPath , err = getAppPath ( ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to get app path: %v" , err )
2014-05-26 02:11:25 +02:00
}
2017-11-03 09:56:20 +01:00
AppWorkPath = getWorkPath ( AppPath )
2014-05-26 02:11:25 +02:00
}
2015-03-18 09:25:55 +01:00
func forcePathSeparator ( path string ) {
if strings . Contains ( path , "\\" ) {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places" )
2015-03-18 09:25:55 +01:00
}
}
2016-08-10 02:41:18 +02:00
// IsRunUserMatchCurrentUser returns false if configured run user does not match
// actual user that runs the app. The first return value is the actual user name.
// This check is ignored under Windows since SSH remote login is not the main
// method to login on Windows.
func IsRunUserMatchCurrentUser ( runUser string ) ( string , bool ) {
2019-06-16 04:49:07 +02:00
if IsWindows || SSH . StartBuiltinServer {
2016-08-10 02:41:18 +02:00
return "" , true
}
currentUser := user . CurrentUsername ( )
return currentUser , runUser == currentUser
}
2017-01-09 12:54:57 +01:00
func createPIDFile ( pidPath string ) {
currentPid := os . Getpid ( )
if err := os . MkdirAll ( filepath . Dir ( pidPath ) , os . ModePerm ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create PID folder: %v" , err )
2017-01-09 12:54:57 +01:00
}
file , err := os . Create ( pidPath )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create PID file: %v" , err )
2017-01-09 12:54:57 +01:00
}
defer file . Close ( )
if _ , err := file . WriteString ( strconv . FormatInt ( int64 ( currentPid ) , 10 ) ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to write PID information: %v" , err )
2017-01-09 12:54:57 +01:00
}
}
2018-12-19 02:17:43 +01:00
// CheckLFSVersion will check lfs version, if not satisfied, then disable it.
func CheckLFSVersion ( ) {
if LFS . StartServer {
//Disable LFS client hooks if installed for the current OS user
//Needs at least git v2.1.2
binVersion , err := git . BinVersion ( )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error retrieving git version: %v" , err )
2018-12-19 02:17:43 +01:00
}
if ! version . Compare ( binVersion , "2.1.2" , ">=" ) {
LFS . StartServer = false
2019-04-02 09:48:31 +02:00
log . Error ( "LFS server support needs at least Git v2.1.2" )
2018-12-19 02:17:43 +01:00
} else {
git . GlobalCommandArgs = append ( git . GlobalCommandArgs , "-c" , "filter.lfs.required=" ,
"-c" , "filter.lfs.smudge=" , "-c" , "filter.lfs.clean=" )
}
}
}
2019-04-29 20:08:21 +02:00
// SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
// GITEA_CUSTOM environment variable and with provided overrides before stepping
// back to the default
2019-05-14 17:20:35 +02:00
func SetCustomPathAndConf ( providedCustom , providedConf , providedWorkPath string ) {
if len ( providedWorkPath ) != 0 {
AppWorkPath = filepath . ToSlash ( providedWorkPath )
}
2019-04-29 20:08:21 +02:00
if giteaCustom , ok := os . LookupEnv ( "GITEA_CUSTOM" ) ; ok {
CustomPath = giteaCustom
}
if len ( providedCustom ) != 0 {
CustomPath = providedCustom
}
2014-05-26 02:11:25 +02:00
if len ( CustomPath ) == 0 {
2017-11-03 09:56:20 +01:00
CustomPath = path . Join ( AppWorkPath , "custom" )
} else if ! filepath . IsAbs ( CustomPath ) {
CustomPath = path . Join ( AppWorkPath , CustomPath )
2014-05-26 02:11:25 +02:00
}
2019-04-29 20:08:21 +02:00
if len ( providedConf ) != 0 {
CustomConf = providedConf
2017-01-09 12:54:57 +01:00
}
2015-02-05 11:12:37 +01:00
if len ( CustomConf ) == 0 {
2017-11-03 09:56:20 +01:00
CustomConf = path . Join ( CustomPath , "conf/app.ini" )
2017-07-01 05:10:04 +02:00
} else if ! filepath . IsAbs ( CustomConf ) {
2017-11-03 09:56:20 +01:00
CustomConf = path . Join ( CustomPath , CustomConf )
2015-02-05 11:12:37 +01:00
}
2019-04-29 20:08:21 +02:00
}
// NewContext initializes configuration context.
// NOTE: do not print any log except error.
func NewContext ( ) {
Cfg = ini . Empty ( )
if len ( CustomPID ) > 0 {
createPIDFile ( CustomPID )
}
2015-02-05 11:12:37 +01:00
if com . IsFile ( CustomConf ) {
2017-11-03 09:56:20 +01:00
if err := Cfg . Append ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to load custom conf '%s': %v" , CustomConf , err )
2014-05-26 02:11:25 +02:00
}
} else {
2015-12-20 03:43:32 +01:00
log . Warn ( "Custom config '%s' not found, ignore this if you're running first time" , CustomConf )
2014-05-26 02:11:25 +02:00
}
2019-10-15 17:45:39 +02:00
Cfg . NameMapper = ini . SnackCase
2014-05-26 02:11:25 +02:00
2015-12-20 03:43:32 +01:00
homeDir , err := com . HomeDir ( )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to get home directory: %v" , err )
2015-12-20 03:43:32 +01:00
}
homeDir = strings . Replace ( homeDir , "\\" , "/" , - 1 )
2019-04-02 09:48:31 +02:00
LogLevel = getLogLevel ( Cfg . Section ( "log" ) , "LEVEL" , "Info" )
StacktraceLogLevel = getStacktraceLogLevel ( Cfg . Section ( "log" ) , "STACKTRACE_LEVEL" , "None" )
2017-11-03 09:56:20 +01:00
LogRootPath = Cfg . Section ( "log" ) . Key ( "ROOT_PATH" ) . MustString ( path . Join ( AppWorkPath , "log" ) )
2015-03-18 09:25:55 +01:00
forcePathSeparator ( LogRootPath )
2019-02-06 04:06:41 +01:00
RedirectMacaronLog = Cfg . Section ( "log" ) . Key ( "REDIRECT_MACARON_LOG" ) . MustBool ( false )
2019-04-02 09:48:31 +02:00
RouterLogLevel = log . FromString ( Cfg . Section ( "log" ) . Key ( "ROUTER_LOG_LEVEL" ) . MustString ( "Info" ) )
2014-12-31 11:37:29 +01:00
sec := Cfg . Section ( "server" )
2016-12-02 06:23:37 +01:00
AppName = Cfg . Section ( "" ) . Key ( "APP_NAME" ) . MustString ( "Gitea: Git with a cup of tea" )
2014-09-14 19:35:22 +02:00
2014-05-26 02:11:25 +02:00
Protocol = HTTP
2019-06-12 21:41:28 +02:00
switch sec . Key ( "PROTOCOL" ) . String ( ) {
case "https" :
2014-05-26 02:11:25 +02:00
Protocol = HTTPS
2014-12-31 11:37:29 +01:00
CertFile = sec . Key ( "CERT_FILE" ) . String ( )
KeyFile = sec . Key ( "KEY_FILE" ) . String ( )
2019-06-12 21:41:28 +02:00
case "fcgi" :
2014-11-04 02:46:53 +01:00
Protocol = FCGI
2019-06-12 21:41:28 +02:00
case "unix" :
2016-11-27 11:14:25 +01:00
Protocol = UnixSocket
2016-08-11 23:55:10 +02:00
UnixSocketPermissionRaw := sec . Key ( "UNIX_SOCKET_PERMISSION" ) . MustString ( "666" )
UnixSocketPermissionParsed , err := strconv . ParseUint ( UnixSocketPermissionRaw , 8 , 32 )
if err != nil || UnixSocketPermissionParsed > 0777 {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to parse unixSocketPermission: %s" , UnixSocketPermissionRaw )
2016-08-11 23:55:10 +02:00
}
UnixSocketPermission = uint32 ( UnixSocketPermissionParsed )
2014-11-04 02:46:53 +01:00
}
2018-09-12 06:06:44 +02:00
EnableLetsEncrypt = sec . Key ( "ENABLE_LETSENCRYPT" ) . MustBool ( false )
LetsEncryptTOS = sec . Key ( "LETSENCRYPT_ACCEPTTOS" ) . MustBool ( false )
2018-08-21 15:56:50 +02:00
if ! LetsEncryptTOS && EnableLetsEncrypt {
log . Warn ( "Failed to enable Let's Encrypt due to Let's Encrypt TOS not being accepted" )
EnableLetsEncrypt = false
}
LetsEncryptDirectory = sec . Key ( "LETSENCRYPT_DIRECTORY" ) . MustString ( "https" )
LetsEncryptEmail = sec . Key ( "LETSENCRYPT_EMAIL" ) . MustString ( "" )
2014-12-31 11:37:29 +01:00
Domain = sec . Key ( "DOMAIN" ) . MustString ( "localhost" )
2016-08-11 23:55:10 +02:00
HTTPAddr = sec . Key ( "HTTP_ADDR" ) . MustString ( "0.0.0.0" )
HTTPPort = sec . Key ( "HTTP_PORT" ) . MustString ( "3000" )
2019-10-15 15:39:51 +02:00
GracefulRestartable = sec . Key ( "ALLOW_GRACEFUL_RESTARTS" ) . MustBool ( true )
GracefulHammerTime = sec . Key ( "GRACEFUL_HAMMER_TIME" ) . MustDuration ( 60 * time . Second )
2019-11-21 19:32:02 +01:00
StartupTimeout = sec . Key ( "STARTUP_TIMEOUT" ) . MustDuration ( 0 * time . Second )
2017-06-22 09:35:14 +02:00
defaultAppURL := string ( Protocol ) + "://" + Domain
if ( Protocol == HTTP && HTTPPort != "80" ) || ( Protocol == HTTPS && HTTPPort != "443" ) {
defaultAppURL += ":" + HTTPPort
}
AppURL = sec . Key ( "ROOT_URL" ) . MustString ( defaultAppURL )
2019-10-22 14:11:01 +02:00
AppURL = strings . TrimSuffix ( AppURL , "/" ) + "/"
2017-06-22 09:35:14 +02:00
// Check if has app suburl.
2019-06-12 23:07:24 +02:00
appURL , err := url . Parse ( AppURL )
2017-06-22 09:35:14 +02:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Invalid ROOT_URL '%s': %s" , AppURL , err )
2017-06-22 09:35:14 +02:00
}
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
2019-06-12 23:07:24 +02:00
AppSubURL = strings . TrimSuffix ( appURL . Path , "/" )
2019-10-22 14:11:01 +02:00
StaticURLPrefix = strings . TrimSuffix ( sec . Key ( "STATIC_URL_PREFIX" ) . MustString ( AppSubURL ) , "/" )
2017-06-22 09:35:14 +02:00
AppSubURLDepth = strings . Count ( AppSubURL , "/" )
2017-10-02 15:55:09 +02:00
// Check if Domain differs from AppURL domain than update it to AppURL's domain
// TODO: Can be replaced with url.Hostname() when minimal GoLang version is 1.8
2019-06-12 23:07:24 +02:00
urlHostname := strings . SplitN ( appURL . Host , ":" , 2 ) [ 0 ]
2017-10-02 15:55:09 +02:00
if urlHostname != Domain && net . ParseIP ( urlHostname ) == nil {
Domain = urlHostname
}
2017-06-22 09:35:14 +02:00
2017-08-03 17:32:13 +02:00
var defaultLocalURL string
switch Protocol {
case UnixSocket :
defaultLocalURL = "http://unix/"
case FCGI :
defaultLocalURL = AppURL
default :
defaultLocalURL = string ( Protocol ) + "://"
if HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += HTTPAddr
}
defaultLocalURL += ":" + HTTPPort + "/"
}
LocalURL = sec . Key ( "LOCAL_ROOT_URL" ) . MustString ( defaultLocalURL )
2017-12-25 23:23:43 +01:00
RedirectOtherPort = sec . Key ( "REDIRECT_OTHER_PORT" ) . MustBool ( false )
PortToRedirect = sec . Key ( "PORT_TO_REDIRECT" ) . MustString ( "80" )
2014-12-31 11:37:29 +01:00
OfflineMode = sec . Key ( "OFFLINE_MODE" ) . MustBool ( )
DisableRouterLog = sec . Key ( "DISABLE_ROUTER_LOG" ) . MustBool ( )
2017-11-03 09:56:20 +01:00
StaticRootPath = sec . Key ( "STATIC_ROOT_PATH" ) . MustString ( AppWorkPath )
2019-10-15 00:05:57 +02:00
StaticCacheTime = sec . Key ( "STATIC_CACHE_TIME" ) . MustDuration ( 6 * time . Hour )
2017-11-03 09:56:20 +01:00
AppDataPath = sec . Key ( "APP_DATA_PATH" ) . MustString ( path . Join ( AppWorkPath , "data" ) )
2014-12-31 11:37:29 +01:00
EnableGzip = sec . Key ( "ENABLE_GZIP" ) . MustBool ( )
2017-02-05 14:06:25 +01:00
EnablePprof = sec . Key ( "ENABLE_PPROF" ) . MustBool ( false )
2018-08-07 20:49:18 +02:00
PprofDataPath = sec . Key ( "PPROF_DATA_PATH" ) . MustString ( path . Join ( AppWorkPath , "data/tmp/pprof" ) )
if ! filepath . IsAbs ( PprofDataPath ) {
PprofDataPath = filepath . Join ( AppWorkPath , PprofDataPath )
}
2014-12-31 11:37:29 +01:00
switch sec . Key ( "LANDING_PAGE" ) . MustString ( "home" ) {
2014-11-25 00:47:59 +01:00
case "explore" :
2016-11-27 11:14:25 +01:00
LandingPageURL = LandingPageExplore
2017-11-20 08:48:09 +01:00
case "organizations" :
LandingPageURL = LandingPageOrganizations
2014-11-25 00:47:59 +01:00
default :
2016-11-27 11:14:25 +01:00
LandingPageURL = LandingPageHome
2014-11-25 00:47:59 +01:00
}
2017-02-14 17:07:37 +01:00
if len ( SSH . Domain ) == 0 {
SSH . Domain = Domain
}
2016-02-28 02:48:39 +01:00
SSH . RootPath = path . Join ( homeDir , ".ssh" )
2017-10-23 17:20:44 +02:00
serverCiphers := sec . Key ( "SSH_SERVER_CIPHERS" ) . Strings ( "," )
if len ( serverCiphers ) > 0 {
SSH . ServerCiphers = serverCiphers
}
2017-11-02 16:26:41 +01:00
serverKeyExchanges := sec . Key ( "SSH_SERVER_KEY_EXCHANGES" ) . Strings ( "," )
if len ( serverKeyExchanges ) > 0 {
SSH . ServerKeyExchanges = serverKeyExchanges
}
serverMACs := sec . Key ( "SSH_SERVER_MACS" ) . Strings ( "," )
if len ( serverMACs ) > 0 {
SSH . ServerMACs = serverMACs
}
2016-02-28 02:48:39 +01:00
SSH . KeyTestPath = os . TempDir ( )
if err = Cfg . Section ( "server" ) . MapTo ( & SSH ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map SSH settings: %v" , err )
2016-02-28 02:48:39 +01:00
}
2016-12-22 19:12:23 +01:00
SSH . KeygenPath = sec . Key ( "SSH_KEYGEN_PATH" ) . MustString ( "ssh-keygen" )
SSH . Port = sec . Key ( "SSH_PORT" ) . MustInt ( 22 )
2017-01-08 04:14:45 +01:00
SSH . ListenPort = sec . Key ( "SSH_LISTEN_PORT" ) . MustInt ( SSH . Port )
2016-12-22 19:12:23 +01:00
2016-02-28 02:48:39 +01:00
// When disable SSH, start builtin server value is ignored.
if SSH . Disabled {
SSH . StartBuiltinServer = false
}
if ! SSH . Disabled && ! SSH . StartBuiltinServer {
if err := os . MkdirAll ( SSH . RootPath , 0700 ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create '%s': %v" , SSH . RootPath , err )
2016-02-28 02:48:39 +01:00
} else if err = os . MkdirAll ( SSH . KeyTestPath , 0644 ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create '%s': %v" , SSH . KeyTestPath , err )
2016-02-28 02:48:39 +01:00
}
}
SSH . MinimumKeySizeCheck = sec . Key ( "MINIMUM_KEY_SIZE_CHECK" ) . MustBool ( )
SSH . MinimumKeySizes = map [ string ] int { }
minimumKeySizes := Cfg . Section ( "ssh.minimum_key_sizes" ) . Keys ( )
for _ , key := range minimumKeySizes {
if key . MustInt ( ) != - 1 {
SSH . MinimumKeySizes [ strings . ToLower ( key . Name ( ) ) ] = key . MustInt ( )
}
}
2017-06-28 03:35:35 +02:00
SSH . AuthorizedKeysBackup = sec . Key ( "SSH_AUTHORIZED_KEYS_BACKUP" ) . MustBool ( true )
2018-11-01 14:41:07 +01:00
SSH . CreateAuthorizedKeysFile = sec . Key ( "SSH_CREATE_AUTHORIZED_KEYS_FILE" ) . MustBool ( true )
2017-07-15 16:21:51 +02:00
SSH . ExposeAnonymous = sec . Key ( "SSH_EXPOSE_ANONYMOUS" ) . MustBool ( false )
2016-02-28 02:48:39 +01:00
2017-10-19 08:52:37 +02:00
sec = Cfg . Section ( "server" )
if err = sec . MapTo ( & LFS ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map LFS settings: %v" , err )
2016-12-26 02:16:37 +01:00
}
2017-10-19 08:52:37 +02:00
LFS . ContentPath = sec . Key ( "LFS_CONTENT_PATH" ) . MustString ( filepath . Join ( AppDataPath , "lfs" ) )
if ! filepath . IsAbs ( LFS . ContentPath ) {
2017-11-03 09:56:20 +01:00
LFS . ContentPath = filepath . Join ( AppWorkPath , LFS . ContentPath )
2017-10-19 08:52:37 +02:00
}
2016-12-26 02:16:37 +01:00
2018-05-29 10:07:16 +02:00
LFS . HTTPAuthExpiry = sec . Key ( "LFS_HTTP_AUTH_EXPIRY" ) . MustDuration ( 20 * time . Minute )
2016-12-26 02:16:37 +01:00
if LFS . StartServer {
if err := os . MkdirAll ( LFS . ContentPath , 0700 ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create '%s': %v" , LFS . ContentPath , err )
2016-12-26 02:16:37 +01:00
}
LFS . JWTSecretBytes = make ( [ ] byte , 32 )
n , err := base64 . RawURLEncoding . Decode ( LFS . JWTSecretBytes , [ ] byte ( LFS . JWTSecretBase64 ) )
if err != nil || n != 32 {
2019-03-08 17:42:50 +01:00
LFS . JWTSecretBase64 , err = generate . NewJwtSecret ( )
2016-12-26 02:16:37 +01:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error generating JWT Secret for custom config: %v" , err )
2018-02-18 19:14:37 +01:00
return
2016-12-26 02:16:37 +01:00
}
// Save secret
cfg := ini . Empty ( )
if com . IsFile ( CustomConf ) {
// Keeps custom settings if there is already something.
if err := cfg . Append ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Error ( "Failed to load custom conf '%s': %v" , CustomConf , err )
2016-12-26 02:16:37 +01:00
}
}
cfg . Section ( "server" ) . Key ( "LFS_JWT_SECRET" ) . SetValue ( LFS . JWTSecretBase64 )
2017-01-09 15:36:23 +01:00
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create '%s': %v" , CustomConf , err )
2017-01-09 15:36:23 +01:00
}
2016-12-26 02:16:37 +01:00
if err := cfg . SaveTo ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error saving generated JWT Secret to custom config: %v" , err )
2016-12-26 02:16:37 +01:00
return
}
}
}
2019-03-08 17:42:50 +01:00
if err = Cfg . Section ( "oauth2" ) . MapTo ( & OAuth2 ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to OAuth2 settings: %v" , err )
2019-03-08 17:42:50 +01:00
return
}
if OAuth2 . Enable {
OAuth2 . JWTSecretBytes = make ( [ ] byte , 32 )
n , err := base64 . RawURLEncoding . Decode ( OAuth2 . JWTSecretBytes , [ ] byte ( OAuth2 . JWTSecretBase64 ) )
if err != nil || n != 32 {
OAuth2 . JWTSecretBase64 , err = generate . NewJwtSecret ( )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "error generating JWT secret: %v" , err )
2019-03-08 17:42:50 +01:00
return
}
cfg := ini . Empty ( )
if com . IsFile ( CustomConf ) {
if err := cfg . Append ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Error ( "failed to load custom conf %s: %v" , CustomConf , err )
2019-03-08 17:42:50 +01:00
return
}
}
cfg . Section ( "oauth2" ) . Key ( "JWT_SECRET" ) . SetValue ( OAuth2 . JWTSecretBase64 )
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "failed to create '%s': %v" , CustomConf , err )
2019-03-08 17:42:50 +01:00
return
}
if err := cfg . SaveTo ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "error saving generating JWT secret to custom config: %v" , err )
2019-03-08 17:42:50 +01:00
return
}
}
}
2019-08-29 16:05:42 +02:00
sec = Cfg . Section ( "admin" )
Admin . DefaultEmailNotification = sec . Key ( "DEFAULT_EMAIL_NOTIFICATIONS" ) . MustString ( "enabled" )
2014-12-31 11:37:29 +01:00
sec = Cfg . Section ( "security" )
2016-12-23 08:18:05 +01:00
InstallLock = sec . Key ( "INSTALL_LOCK" ) . MustBool ( false )
SecretKey = sec . Key ( "SECRET_KEY" ) . MustString ( "!#@FDEWREWR&*(" )
LogInRememberDays = sec . Key ( "LOGIN_REMEMBER_DAYS" ) . MustInt ( 7 )
CookieUserName = sec . Key ( "COOKIE_USERNAME" ) . MustString ( "gitea_awesome" )
CookieRememberName = sec . Key ( "COOKIE_REMEMBER_NAME" ) . MustString ( "gitea_incredible" )
2014-12-31 11:37:29 +01:00
ReverseProxyAuthUser = sec . Key ( "REVERSE_PROXY_AUTHENTICATION_USER" ) . MustString ( "X-WEBAUTH-USER" )
2018-12-18 18:05:48 +01:00
ReverseProxyAuthEmail = sec . Key ( "REVERSE_PROXY_AUTHENTICATION_EMAIL" ) . MustString ( "X-WEBAUTH-EMAIL" )
2016-12-24 15:42:11 +01:00
MinPasswordLength = sec . Key ( "MIN_PASSWORD_LENGTH" ) . MustInt ( 6 )
2017-01-23 02:19:50 +01:00
ImportLocalPaths = sec . Key ( "IMPORT_LOCAL_PATHS" ) . MustBool ( false )
2017-09-12 11:25:42 +02:00
DisableGitHooks = sec . Key ( "DISABLE_GIT_HOOKS" ) . MustBool ( false )
2019-11-14 23:39:48 +01:00
OnlyAllowPushIfGiteaEnvironmentSet = sec . Key ( "ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET" ) . MustBool ( true )
2019-07-07 08:01:01 +02:00
PasswordHashAlgo = sec . Key ( "PASSWORD_HASH_ALGO" ) . MustString ( "pbkdf2" )
2019-07-12 15:57:31 +02:00
CSRFCookieHTTPOnly = sec . Key ( "CSRF_COOKIE_HTTP_ONLY" ) . MustBool ( true )
2019-03-13 23:49:43 +01:00
InternalToken = loadInternalToken ( sec )
2014-12-31 11:37:29 +01:00
2019-10-14 17:24:26 +02:00
cfgdata := sec . Key ( "PASSWORD_COMPLEXITY" ) . Strings ( "," )
2019-10-16 05:09:58 +02:00
PasswordComplexity = make ( [ ] string , 0 , len ( cfgdata ) )
for _ , name := range cfgdata {
name := strings . ToLower ( strings . Trim ( name , ` " ` ) )
if name != "" {
PasswordComplexity = append ( PasswordComplexity , name )
2019-10-14 17:24:26 +02:00
}
}
2014-12-31 11:37:29 +01:00
sec = Cfg . Section ( "attachment" )
2015-10-30 01:40:57 +01:00
AttachmentPath = sec . Key ( "PATH" ) . MustString ( path . Join ( AppDataPath , "attachments" ) )
2015-02-14 23:49:33 +01:00
if ! filepath . IsAbs ( AttachmentPath ) {
2017-11-03 09:56:20 +01:00
AttachmentPath = path . Join ( AppWorkPath , AttachmentPath )
2015-02-14 23:49:33 +01:00
}
2017-01-15 15:57:00 +01:00
AttachmentAllowedTypes = strings . Replace ( sec . Key ( "ALLOWED_TYPES" ) . MustString ( "image/jpeg,image/png,application/zip,application/gzip" ) , "|" , "," , - 1 )
2015-09-02 22:18:09 +02:00
AttachmentMaxSize = sec . Key ( "MAX_SIZE" ) . MustInt64 ( 4 )
2015-08-11 17:24:40 +02:00
AttachmentMaxFiles = sec . Key ( "MAX_FILES" ) . MustInt ( 5 )
2018-05-28 16:56:54 +02:00
AttachmentEnabled = sec . Key ( "ENABLED" ) . MustBool ( true )
2014-07-23 21:15:47 +02:00
2019-08-15 16:46:21 +02:00
timeFormatKey := Cfg . Section ( "time" ) . Key ( "FORMAT" ) . MustString ( "" )
if timeFormatKey != "" {
TimeFormat = map [ string ] string {
"ANSIC" : time . ANSIC ,
"UnixDate" : time . UnixDate ,
"RubyDate" : time . RubyDate ,
"RFC822" : time . RFC822 ,
"RFC822Z" : time . RFC822Z ,
"RFC850" : time . RFC850 ,
"RFC1123" : time . RFC1123 ,
"RFC1123Z" : time . RFC1123Z ,
"RFC3339" : time . RFC3339 ,
"RFC3339Nano" : time . RFC3339Nano ,
"Kitchen" : time . Kitchen ,
"Stamp" : time . Stamp ,
"StampMilli" : time . StampMilli ,
"StampMicro" : time . StampMicro ,
"StampNano" : time . StampNano ,
} [ timeFormatKey ]
// When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
if len ( TimeFormat ) == 0 {
TimeFormat = timeFormatKey
TestTimeFormat , _ := time . Parse ( TimeFormat , TimeFormat )
if TestTimeFormat . Format ( time . RFC3339 ) != "2006-01-02T15:04:05Z" {
log . Fatal ( "Can't create time properly, please check your time format has 2006, 01, 02, 15, 04 and 05" )
}
log . Trace ( "Custom TimeFormat: %s" , TimeFormat )
}
}
zone := Cfg . Section ( "time" ) . Key ( "DEFAULT_UI_LOCATION" ) . String ( )
if zone != "" {
DefaultUILocation , err = time . LoadLocation ( zone )
if err != nil {
log . Fatal ( "Load time zone failed: %v" , err )
} else {
log . Info ( "Default UI Location is %v" , zone )
2017-02-04 13:37:50 +01:00
}
2019-08-15 16:46:21 +02:00
}
if DefaultUILocation == nil {
DefaultUILocation = time . Local
2017-02-04 13:37:50 +01:00
}
2014-07-24 22:31:59 +02:00
2016-12-22 19:12:23 +01:00
RunUser = Cfg . Section ( "" ) . Key ( "RUN_USER" ) . MustString ( user . CurrentUsername ( ) )
2014-05-26 02:11:25 +02:00
// Does not check run user when the install lock is off.
2016-08-10 02:41:18 +02:00
if InstallLock {
currentUser , match := IsRunUserMatchCurrentUser ( RunUser )
if ! match {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Expect user '%s' but current user is: %s" , RunUser , currentUser )
2016-08-10 02:41:18 +02:00
}
2014-05-26 02:11:25 +02:00
}
2017-10-14 17:51:00 +02:00
SSH . BuiltinServerUser = Cfg . Section ( "server" ) . Key ( "BUILTIN_SSH_SERVER_USER" ) . MustString ( RunUser )
2019-03-16 04:12:44 +01:00
newRepository ( )
2016-08-11 14:48:08 +02:00
2014-12-31 11:37:29 +01:00
sec = Cfg . Section ( "picture" )
2015-10-30 01:40:57 +01:00
AvatarUploadPath = sec . Key ( "AVATAR_UPLOAD_PATH" ) . MustString ( path . Join ( AppDataPath , "avatars" ) )
2015-03-18 09:25:55 +01:00
forcePathSeparator ( AvatarUploadPath )
2015-02-14 23:49:33 +01:00
if ! filepath . IsAbs ( AvatarUploadPath ) {
2017-11-03 09:56:20 +01:00
AvatarUploadPath = path . Join ( AppWorkPath , AvatarUploadPath )
2015-02-14 23:49:33 +01:00
}
2019-05-30 04:22:26 +02:00
RepositoryAvatarUploadPath = sec . Key ( "REPOSITORY_AVATAR_UPLOAD_PATH" ) . MustString ( path . Join ( AppDataPath , "repo-avatars" ) )
forcePathSeparator ( RepositoryAvatarUploadPath )
if ! filepath . IsAbs ( RepositoryAvatarUploadPath ) {
RepositoryAvatarUploadPath = path . Join ( AppWorkPath , RepositoryAvatarUploadPath )
}
2019-06-02 08:40:12 +02:00
RepositoryAvatarFallback = sec . Key ( "REPOSITORY_AVATAR_FALLBACK" ) . MustString ( "none" )
RepositoryAvatarFallbackImage = sec . Key ( "REPOSITORY_AVATAR_FALLBACK_IMAGE" ) . MustString ( "/img/repo_default.png" )
2018-07-03 05:56:32 +02:00
AvatarMaxWidth = sec . Key ( "AVATAR_MAX_WIDTH" ) . MustInt ( 4096 )
AvatarMaxHeight = sec . Key ( "AVATAR_MAX_HEIGHT" ) . MustInt ( 3072 )
2019-05-30 04:22:26 +02:00
AvatarMaxFileSize = sec . Key ( "AVATAR_MAX_FILE_SIZE" ) . MustInt64 ( 1048576 )
2015-08-10 02:44:43 +02:00
switch source := sec . Key ( "GRAVATAR_SOURCE" ) . MustString ( "gravatar" ) ; source {
2014-11-17 02:27:04 +01:00
case "duoshuo" :
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
2015-08-10 02:44:43 +02:00
case "gravatar" :
2016-01-29 06:06:17 +01:00
GravatarSource = "https://secure.gravatar.com/avatar/"
2016-12-19 17:04:39 +01:00
case "libravatar" :
GravatarSource = "https://seccdn.libravatar.org/avatar/"
2015-08-10 02:44:43 +02:00
default :
GravatarSource = source
2014-11-17 02:27:04 +01:00
}
2014-12-31 11:37:29 +01:00
DisableGravatar = sec . Key ( "DISABLE_GRAVATAR" ) . MustBool ( )
2018-01-07 10:41:09 +01:00
EnableFederatedAvatar = sec . Key ( "ENABLE_FEDERATED_AVATAR" ) . MustBool ( ! InstallLock )
2015-03-24 23:38:12 +01:00
if OfflineMode {
DisableGravatar = true
2016-08-07 19:27:38 +02:00
EnableFederatedAvatar = false
}
2016-08-07 20:01:47 +02:00
if DisableGravatar {
EnableFederatedAvatar = false
}
2017-12-03 12:55:13 +01:00
if EnableFederatedAvatar || ! DisableGravatar {
GravatarSourceURL , err = url . Parse ( GravatarSource )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to parse Gravatar URL(%s): %v" ,
2017-12-03 12:55:13 +01:00
GravatarSource , err )
}
}
2016-08-07 19:27:38 +02:00
2016-08-07 20:01:47 +02:00
if EnableFederatedAvatar {
2016-08-07 19:27:38 +02:00
LibravatarService = libravatar . New ( )
2017-12-03 12:55:13 +01:00
if GravatarSourceURL . Scheme == "https" {
LibravatarService . SetUseHTTPS ( true )
LibravatarService . SetSecureFallbackHost ( GravatarSourceURL . Host )
} else {
LibravatarService . SetUseHTTPS ( false )
LibravatarService . SetFallbackHost ( GravatarSourceURL . Host )
2016-08-07 19:27:38 +02:00
}
2015-03-24 23:38:12 +01:00
}
2014-07-26 06:24:27 +02:00
2016-07-23 18:23:54 +02:00
if err = Cfg . Section ( "ui" ) . MapTo ( & UI ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map UI settings: %v" , err )
2016-07-23 18:23:54 +02:00
} else if err = Cfg . Section ( "markdown" ) . MapTo ( & Markdown ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map Markdown settings: %v" , err )
2017-02-14 13:16:00 +01:00
} else if err = Cfg . Section ( "admin" ) . MapTo ( & Admin ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Fail to map Admin settings: %v" , err )
2016-08-10 08:58:15 +02:00
} else if err = Cfg . Section ( "api" ) . MapTo ( & API ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map API settings: %v" , err )
2018-11-05 04:20:00 +01:00
} else if err = Cfg . Section ( "metrics" ) . MapTo ( & Metrics ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map Metrics settings: %v" , err )
2016-08-10 08:47:16 +02:00
}
2019-06-12 23:07:24 +02:00
u := * appURL
u . Path = path . Join ( u . Path , "api" , "swagger" )
API . SwaggerURL = u . String ( )
2019-03-16 04:12:44 +01:00
newCron ( )
newGit ( )
2019-02-09 23:44:24 +01:00
2017-04-08 17:27:26 +02:00
sec = Cfg . Section ( "mirror" )
Mirror . MinInterval = sec . Key ( "MIN_INTERVAL" ) . MustDuration ( 10 * time . Minute )
Mirror . DefaultInterval = sec . Key ( "DEFAULT_INTERVAL" ) . MustDuration ( 8 * time . Hour )
if Mirror . MinInterval . Minutes ( ) < 1 {
log . Warn ( "Mirror.MinInterval is too low" )
Mirror . MinInterval = 1 * time . Minute
}
if Mirror . DefaultInterval < Mirror . MinInterval {
log . Warn ( "Mirror.DefaultInterval is less than Mirror.MinInterval" )
Mirror . DefaultInterval = time . Hour * 8
2015-01-02 13:14:43 +01:00
}
2014-09-17 06:03:03 +02:00
2014-12-31 11:37:29 +01:00
Langs = Cfg . Section ( "i18n" ) . Key ( "LANGS" ) . Strings ( "," )
2016-12-22 19:12:23 +01:00
if len ( Langs ) == 0 {
2019-01-19 21:39:27 +01:00
Langs = [ ] string {
"en-US" , "zh-CN" , "zh-HK" , "zh-TW" , "de-DE" , "fr-FR" , "nl-NL" , "lv-LV" ,
"ru-RU" , "uk-UA" , "ja-JP" , "es-ES" , "pt-BR" , "pl-PL" , "bg-BG" , "it-IT" ,
"fi-FI" , "tr-TR" , "cs-CZ" , "sr-SP" , "sv-SE" , "ko-KR" }
2016-12-22 19:12:23 +01:00
}
2014-12-31 11:37:29 +01:00
Names = Cfg . Section ( "i18n" ) . Key ( "NAMES" ) . Strings ( "," )
2016-12-22 19:12:23 +01:00
if len ( Names ) == 0 {
2019-01-19 21:39:27 +01:00
Names = [ ] string { "English" , "简体中文" , "繁體中文(香港)" , "繁體中文(台灣)" , "Deutsch" ,
"français" , "Nederlands" , "latviešu" , "русский" , "Українська" , "日本語" ,
"español" , "português do Brasil" , "polski" , "български" , "italiano" ,
"suomi" , "Türkçe" , "čeština" , "српски" , "svenska" , "한국어" }
2016-12-22 19:12:23 +01:00
}
2015-08-05 09:24:26 +02:00
dateLangs = Cfg . Section ( "i18n.datelang" ) . KeysHash ( )
2014-09-22 01:39:10 +02:00
2016-12-23 08:18:05 +01:00
ShowFooterBranding = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_BRANDING" ) . MustBool ( false )
ShowFooterVersion = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_VERSION" ) . MustBool ( true )
ShowFooterTemplateLoadTime = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_TEMPLATE_LOAD_TIME" ) . MustBool ( true )
2015-03-23 15:19:19 +01:00
2017-01-01 03:51:10 +01:00
UI . ShowUserEmail = Cfg . Section ( "ui" ) . Key ( "SHOW_USER_EMAIL" ) . MustBool ( true )
2019-05-08 10:41:35 +02:00
UI . DefaultShowFullName = Cfg . Section ( "ui" ) . Key ( "DEFAULT_SHOW_FULL_NAME" ) . MustBool ( false )
2019-08-25 19:06:36 +02:00
UI . SearchRepoDescription = Cfg . Section ( "ui" ) . Key ( "SEARCH_REPO_DESCRIPTION" ) . MustBool ( true )
2019-11-21 21:06:23 +01:00
UI . UseServiceWorker = Cfg . Section ( "ui" ) . Key ( "USE_SERVICE_WORKER" ) . MustBool ( true )
2017-01-01 03:51:10 +01:00
2014-09-22 01:39:10 +02:00
HasRobotsTxt = com . IsFile ( path . Join ( CustomPath , "robots.txt" ) )
2017-11-07 07:33:06 +01:00
2019-03-16 04:12:44 +01:00
newMarkup ( )
2019-02-19 15:39:39 +01:00
2018-05-19 16:12:37 +02:00
sec = Cfg . Section ( "U2F" )
U2F . TrustedFacets , _ = shellquote . Split ( sec . Key ( "TRUSTED_FACETS" ) . MustString ( strings . TrimRight ( AppURL , "/" ) ) )
U2F . AppID = sec . Key ( "APP_ID" ) . MustString ( strings . TrimRight ( AppURL , "/" ) )
2019-05-15 03:57:00 +02:00
zip . Verbose = false
2014-05-26 02:11:25 +02:00
}
2019-03-13 23:49:43 +01:00
func loadInternalToken ( sec * ini . Section ) string {
uri := sec . Key ( "INTERNAL_TOKEN_URI" ) . String ( )
if len ( uri ) == 0 {
return loadOrGenerateInternalToken ( sec )
}
tempURI , err := url . Parse ( uri )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to parse INTERNAL_TOKEN_URI (%s): %v" , uri , err )
2019-03-13 23:49:43 +01:00
}
switch tempURI . Scheme {
case "file" :
fp , err := os . OpenFile ( tempURI . RequestURI ( ) , os . O_RDWR , 0600 )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to open InternalTokenURI (%s): %v" , uri , err )
2019-03-13 23:49:43 +01:00
}
defer fp . Close ( )
buf , err := ioutil . ReadAll ( fp )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to read InternalTokenURI (%s): %v" , uri , err )
2019-03-13 23:49:43 +01:00
}
// No token in the file, generate one and store it.
if len ( buf ) == 0 {
token , err := generate . NewInternalToken ( )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error generate internal token: %v" , err )
2019-03-13 23:49:43 +01:00
}
if _ , err := io . WriteString ( fp , token ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error writing to InternalTokenURI (%s): %v" , uri , err )
2019-03-13 23:49:43 +01:00
}
return token
}
return string ( buf )
default :
2019-04-02 09:48:31 +02:00
log . Fatal ( "Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)" , tempURI . Scheme , uri )
2019-03-13 23:49:43 +01:00
}
return ""
}
func loadOrGenerateInternalToken ( sec * ini . Section ) string {
var err error
token := sec . Key ( "INTERNAL_TOKEN" ) . String ( )
if len ( token ) == 0 {
token , err = generate . NewInternalToken ( )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error generate internal token: %v" , err )
2019-03-13 23:49:43 +01:00
}
// Save secret
cfgSave := ini . Empty ( )
if com . IsFile ( CustomConf ) {
// Keeps custom settings if there is already something.
if err := cfgSave . Append ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Error ( "Failed to load custom conf '%s': %v" , CustomConf , err )
2019-03-13 23:49:43 +01:00
}
}
cfgSave . Section ( "security" ) . Key ( "INTERNAL_TOKEN" ) . SetValue ( token )
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create '%s': %v" , CustomConf , err )
2019-03-13 23:49:43 +01:00
}
if err := cfgSave . SaveTo ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Error saving generated INTERNAL_TOKEN to custom config: %v" , err )
2019-03-13 23:49:43 +01:00
}
}
return token
}
2016-11-27 11:14:25 +01:00
// NewServices initializes the services
2014-05-26 02:11:25 +02:00
func NewServices ( ) {
2019-08-24 11:24:45 +02:00
InitDBConfig ( )
2014-04-10 20:20:58 +02:00
newService ( )
2019-04-07 02:25:14 +02:00
NewLogServices ( false )
2014-04-10 20:20:58 +02:00
newCacheService ( )
newSessionService ( )
2019-05-13 17:38:53 +02:00
newCORSService ( )
2014-04-10 20:20:58 +02:00
newMailService ( )
newRegisterMailService ( )
newNotifyMailService ( )
2014-06-08 10:45:34 +02:00
newWebhookService ( )
2019-11-16 09:30:06 +01:00
newMigrationsService ( )
2019-02-19 15:39:39 +01:00
newIndexerService ( )
2019-10-13 15:23:14 +02:00
newTaskService ( )
2014-04-10 20:20:58 +02:00
}