| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 | // 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.package settingimport (	"fmt"	"net/url"	"os"	"os/exec"	"path"	"path/filepath"	"runtime"	"strings"	"time"	"github.com/Unknwon/com"	_ "github.com/go-macaron/cache/memcache"	_ "github.com/go-macaron/cache/redis"	"github.com/go-macaron/session"	_ "github.com/go-macaron/session/redis"	"gopkg.in/ini.v1"	"github.com/gogits/gogs/modules/bindata"	"github.com/gogits/gogs/modules/log"	"github.com/gogits/gogs/modules/user")type Scheme stringconst (	HTTP  Scheme = "http"	HTTPS Scheme = "https"	FCGI  Scheme = "fcgi")type LandingPage stringconst (	LANDING_PAGE_HOME    LandingPage = "/"	LANDING_PAGE_EXPLORE LandingPage = "/explore")var (	// Build information	BuildTime    string	BuildGitHash string	// App settings	AppVer      string	AppName     string	AppUrl      string	AppSubUrl   string	AppPath     string	AppDataPath = "data"	// Server settings	Protocol           Scheme	Domain             string	HttpAddr, HttpPort string	LocalURL           string	DisableSSH         bool	StartSSHServer     bool	SSHDomain          string	SSHPort            int	SSHRootPath        string	OfflineMode        bool	DisableRouterLog   bool	CertFile, KeyFile  string	StaticRootPath     string	EnableGzip         bool	LandingPageUrl     LandingPage	// Security settings	InstallLock          bool	SecretKey            string	LogInRememberDays    int	CookieUserName       string	CookieRememberName   string	ReverseProxyAuthUser string	// Database settings	UseSQLite3    bool	UseMySQL      bool	UsePostgreSQL bool	UseTiDB       bool	// Webhook settings	Webhook struct {		QueueLength    int		DeliverTimeout int		SkipTLSVerify  bool		Types          []string		PagingNum      int	}	// Repository settings	Repository struct {		AnsiCharset            string		ForcePrivate           bool		MaxCreationLimit       int		PullRequestQueueLength int	}	RepoRootPath string	ScriptType   string	// UI settings	ExplorePagingNum     int	IssuePagingNum       int	FeedMaxCommitNum     int	AdminUserPagingNum   int	AdminRepoPagingNum   int	AdminNoticePagingNum int	AdminOrgPagingNum    int	// Markdown sttings	Markdown struct {		EnableHardLineBreak bool	}	// Picture settings	PictureService   string	AvatarUploadPath string	GravatarSource   string	DisableGravatar  bool	// Log settings	LogRootPath string	LogModes    []string	LogConfigs  []string	// Attachment settings	AttachmentPath         string	AttachmentAllowedTypes string	AttachmentMaxSize      int64	AttachmentMaxFiles     int	AttachmentEnabled      bool	// Time settings	TimeFormat string	// Cache settings	CacheAdapter  string	CacheInternal int	CacheConn     string	// Session settings	SessionConfig session.Options	// Git settings	Git struct {		MaxGitDiffLines int		GcArgs          []string `delim:" "`	}	// Cron tasks	Cron struct {		UpdateMirror struct {			Enabled    bool			RunAtStart bool			Schedule   string		} `ini:"cron.update_mirrors"`		RepoHealthCheck struct {			Enabled    bool			RunAtStart bool			Schedule   string			Timeout    time.Duration			Args       []string `delim:" "`		} `ini:"cron.repo_health_check"`		CheckRepoStats struct {			Enabled    bool			RunAtStart bool			Schedule   string		} `ini:"cron.check_repo_stats"`	}	// I18n settings	Langs, Names []string	dateLangs    map[string]string	// Highlight settings are loaded in modules/template/hightlight.go	// Other settings	ShowFooterBranding    bool	ShowFooterVersion     bool	SupportMiniWinService bool	// Global setting objects	Cfg          *ini.File	CustomPath   string // Custom directory path	CustomConf   string	ProdMode     bool	RunUser      string	IsWindows    bool	HasRobotsTxt bool)func DateLang(lang string) string {	name, ok := dateLangs[lang]	if ok {		return name	}	return "en"}// execPath returns the executable path.func execPath() (string, error) {	file, err := exec.LookPath(os.Args[0])	if err != nil {		return "", err	}	return filepath.Abs(file)}func init() {	IsWindows = runtime.GOOS == "windows"	log.NewLogger(0, "console", `{"level": 0}`)	var err error	if AppPath, err = execPath(); err != nil {		log.Fatal(4, "fail to get app path: %v\n", err)	}	// Note: we don't use path.Dir here because it does not handle case	//	which path starts with two "/" in Windows: "//psf/Home/..."	AppPath = strings.Replace(AppPath, "\\", "/", -1)}// WorkDir returns absolute path of work directory.func WorkDir() (string, error) {	wd := os.Getenv("GOGS_WORK_DIR")	if len(wd) > 0 {		return wd, nil	}	i := strings.LastIndex(AppPath, "/")	if i == -1 {		return AppPath, nil	}	return AppPath[:i], nil}func forcePathSeparator(path string) {	if strings.Contains(path, "\\") {		log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")	}}// NewContext initializes configuration context.// NOTE: do not print any log except error.func NewContext() {	workDir, err := WorkDir()	if err != nil {		log.Fatal(4, "Fail to get work directory: %v", err)	}	Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini"))	if err != nil {		log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)	}	CustomPath = os.Getenv("GOGS_CUSTOM")	if len(CustomPath) == 0 {		CustomPath = workDir + "/custom"	}	if len(CustomConf) == 0 {		CustomConf = CustomPath + "/conf/app.ini"	}	if com.IsFile(CustomConf) {		if err = Cfg.Append(CustomConf); err != nil {			log.Fatal(4, "Fail to load custom conf '%s': %v", CustomConf, err)		}	} else {		log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)	}	Cfg.NameMapper = ini.AllCapsUnderscore	homeDir, err := com.HomeDir()	if err != nil {		log.Fatal(4, "Fail to get home directory: %v", err)	}	homeDir = strings.Replace(homeDir, "\\", "/", -1)	LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))	forcePathSeparator(LogRootPath)	sec := Cfg.Section("server")	AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs: Go Git Service")	AppUrl = sec.Key("ROOT_URL").MustString("http://localhost:3000/")	if AppUrl[len(AppUrl)-1] != '/' {		AppUrl += "/"	}	// Check if has app suburl.	url, err := url.Parse(AppUrl)	if err != nil {		log.Fatal(4, "Invalid ROOT_URL '%s': %s", AppUrl, err)	}	AppSubUrl = strings.TrimSuffix(url.Path, "/")	Protocol = HTTP	if sec.Key("PROTOCOL").String() == "https" {		Protocol = HTTPS		CertFile = sec.Key("CERT_FILE").String()		KeyFile = sec.Key("KEY_FILE").String()	} else if sec.Key("PROTOCOL").String() == "fcgi" {		Protocol = FCGI	}	Domain = sec.Key("DOMAIN").MustString("localhost")	HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")	HttpPort = sec.Key("HTTP_PORT").MustString("3000")	LocalURL = sec.Key("LOCAL_ROOT_URL").MustString("http://localhost:" + HttpPort + "/")	DisableSSH = sec.Key("DISABLE_SSH").MustBool()	if !DisableSSH {		StartSSHServer = sec.Key("START_SSH_SERVER").MustBool()	}	SSHDomain = sec.Key("SSH_DOMAIN").MustString(Domain)	SSHPort = sec.Key("SSH_PORT").MustInt(22)	SSHRootPath = sec.Key("SSH_ROOT_PATH").MustString(path.Join(homeDir, ".ssh"))	if err := os.MkdirAll(SSHRootPath, 0700); err != nil {		log.Fatal(4, "Fail to create '%s': %v", SSHRootPath, err)	}	OfflineMode = sec.Key("OFFLINE_MODE").MustBool()	DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()	StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)	EnableGzip = sec.Key("ENABLE_GZIP").MustBool()	switch sec.Key("LANDING_PAGE").MustString("home") {	case "explore":		LandingPageUrl = LANDING_PAGE_EXPLORE	default:		LandingPageUrl = LANDING_PAGE_HOME	}	sec = Cfg.Section("security")	InstallLock = sec.Key("INSTALL_LOCK").MustBool()	SecretKey = sec.Key("SECRET_KEY").String()	LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()	CookieUserName = sec.Key("COOKIE_USERNAME").String()	CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")	sec = Cfg.Section("attachment")	AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))	if !filepath.IsAbs(AttachmentPath) {		AttachmentPath = path.Join(workDir, AttachmentPath)	}	AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)	AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)	AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)	AttachmentEnabled = sec.Key("ENABLE").MustBool(true)	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,	}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]	RunUser = Cfg.Section("").Key("RUN_USER").String()	curUser := user.CurrentUsername()	// Does not check run user when the install lock is off.	if InstallLock && RunUser != curUser {		log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)	}	// Determine and create root git repository path.	sec = Cfg.Section("repository")	RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gogs-repositories"))	forcePathSeparator(RepoRootPath)	if !filepath.IsAbs(RepoRootPath) {		RepoRootPath = path.Join(workDir, RepoRootPath)	} else {		RepoRootPath = path.Clean(RepoRootPath)	}	ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")	if err = Cfg.Section("repository").MapTo(&Repository); err != nil {		log.Fatal(4, "Fail to map Repository settings: %v", err)	}	// UI settings.	sec = Cfg.Section("ui")	ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20)	IssuePagingNum = sec.Key("ISSUE_PAGING_NUM").MustInt(10)	FeedMaxCommitNum = sec.Key("FEED_MAX_COMMIT_NUM").MustInt(5)	sec = Cfg.Section("ui.admin")	AdminUserPagingNum = sec.Key("USER_PAGING_NUM").MustInt(50)	AdminRepoPagingNum = sec.Key("REPO_PAGING_NUM").MustInt(50)	AdminNoticePagingNum = sec.Key("NOTICE_PAGING_NUM").MustInt(50)	AdminOrgPagingNum = sec.Key("ORG_PAGING_NUM").MustInt(50)	sec = Cfg.Section("picture")	PictureService = sec.Key("SERVICE").In("server", []string{"server"})	AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))	forcePathSeparator(AvatarUploadPath)	if !filepath.IsAbs(AvatarUploadPath) {		AvatarUploadPath = path.Join(workDir, AvatarUploadPath)	}	switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {	case "duoshuo":		GravatarSource = "http://gravatar.duoshuo.com/avatar/"	case "gravatar":		GravatarSource = "//secure.gravatar.com/avatar/"	default:		GravatarSource = source	}	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()	if OfflineMode {		DisableGravatar = true	}	if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {		log.Fatal(4, "Fail to map Markdown settings: %v", err)	} else if err = Cfg.Section("git").MapTo(&Git); err != nil {		log.Fatal(4, "Fail to map Git settings: %v", err)	} else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {		log.Fatal(4, "Fail to map Cron settings: %v", err)	}	Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")	Names = Cfg.Section("i18n").Key("NAMES").Strings(",")	dateLangs = Cfg.Section("i18n.datelang").KeysHash()	ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()	ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool()	HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))}var Service struct {	ActiveCodeLives                int	ResetPwdCodeLives              int	RegisterEmailConfirm           bool	DisableRegistration            bool	ShowRegistrationButton         bool	RequireSignInView              bool	EnableCacheAvatar              bool	EnableNotifyMail               bool	EnableReverseProxyAuth         bool	EnableReverseProxyAutoRegister bool	EnableCaptcha                  bool}func newService() {	sec := Cfg.Section("service")	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)	Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)	Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()	Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)	Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()	Service.EnableCacheAvatar = sec.Key("ENABLE_CACHE_AVATAR").MustBool()	Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()	Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()	Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()}var logLevels = map[string]string{	"Trace":    "0",	"Debug":    "1",	"Info":     "2",	"Warn":     "3",	"Error":    "4",	"Critical": "5",}func newLogService() {	log.Info("%s %s", AppName, AppVer)	if len(BuildTime) > 0 {		log.Info("Build Time: %s", BuildTime)		log.Info("Build Git Hash: %s", BuildGitHash)	}	// Get and check log mode.	LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")	LogConfigs = make([]string, len(LogModes))	for i, mode := range LogModes {		mode = strings.TrimSpace(mode)		sec, err := Cfg.GetSection("log." + mode)		if err != nil {			log.Fatal(4, "Unknown log mode: %s", mode)		}		validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}		// Log level.		levelName := Cfg.Section("log."+mode).Key("LEVEL").In(			Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),			validLevels)		level, ok := logLevels[levelName]		if !ok {			log.Fatal(4, "Unknown log level: %s", levelName)		}		// Generate log configuration.		switch mode {		case "console":			LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)		case "file":			logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gogs.log"))			if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {				panic(err.Error())			}			LogConfigs[i] = fmt.Sprintf(				`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,				logPath,				sec.Key("LOG_ROTATE").MustBool(true),				sec.Key("MAX_LINES").MustInt(1000000),				1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),				sec.Key("DAILY_ROTATE").MustBool(true),				sec.Key("MAX_DAYS").MustInt(7))		case "conn":			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,				sec.Key("RECONNECT_ON_MSG").MustBool(),				sec.Key("RECONNECT").MustBool(),				sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),				sec.Key("ADDR").MustString(":7020"))		case "smtp":			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,				sec.Key("USER").MustString("[email protected]"),				sec.Key("PASSWD").MustString("******"),				sec.Key("HOST").MustString("127.0.0.1:25"),				sec.Key("RECEIVERS").MustString("[]"),				sec.Key("SUBJECT").MustString("Diagnostic message from serve"))		case "database":			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,				sec.Key("DRIVER").String(),				sec.Key("CONN").String())		}		log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])		log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)	}}func newCacheService() {	CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})	switch CacheAdapter {	case "memory":		CacheInternal = Cfg.Section("cache").Key("INTERVAL").MustInt(60)	case "redis", "memcache":		CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")	default:		log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)	}	log.Info("Cache Service Enabled")}func newSessionService() {	SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",		[]string{"memory", "file", "redis", "mysql"})	SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")	SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")	SessionConfig.CookiePath = AppSubUrl	SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()	SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)	SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)	log.Info("Session Service Enabled")}// Mailer represents mail service.type Mailer struct {	QueueLength       int	Name              string	Host              string	From              string	User, Passwd      string	DisableHelo       bool	HeloHostname      string	SkipVerify        bool	UseCertificate    bool	CertFile, KeyFile string}var (	MailService *Mailer)func newMailService() {	sec := Cfg.Section("mailer")	// Check mailer setting.	if !sec.Key("ENABLED").MustBool() {		return	}	MailService = &Mailer{		QueueLength:    sec.Key("SEND_BUFFER_LEN").MustInt(100),		Name:           sec.Key("NAME").MustString(AppName),		Host:           sec.Key("HOST").String(),		User:           sec.Key("USER").String(),		Passwd:         sec.Key("PASSWD").String(),		DisableHelo:    sec.Key("DISABLE_HELO").MustBool(),		HeloHostname:   sec.Key("HELO_HOSTNAME").String(),		SkipVerify:     sec.Key("SKIP_VERIFY").MustBool(),		UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),		CertFile:       sec.Key("CERT_FILE").String(),		KeyFile:        sec.Key("KEY_FILE").String(),	}	MailService.From = sec.Key("FROM").MustString(MailService.User)	log.Info("Mail Service Enabled")}func newRegisterMailService() {	if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {		return	} else if MailService == nil {		log.Warn("Register Mail Service: Mail Service is not enabled")		return	}	Service.RegisterEmailConfirm = true	log.Info("Register Mail Service Enabled")}func newNotifyMailService() {	if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {		return	} else if MailService == nil {		log.Warn("Notify Mail Service: Mail Service is not enabled")		return	}	Service.EnableNotifyMail = true	log.Info("Notify Mail Service Enabled")}func newWebhookService() {	sec := Cfg.Section("webhook")	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()	Webhook.Types = []string{"gogs", "slack"}	Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)}func NewServices() {	newService()	newLogService()	newCacheService()	newSessionService()	newMailService()	newRegisterMailService()	newNotifyMailService()	newWebhookService()}
 |