setting.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package setting
  5. import (
  6. "net/mail"
  7. "net/url"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "path/filepath"
  12. "runtime"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/Unknwon/com"
  17. _ "github.com/go-macaron/cache/memcache"
  18. _ "github.com/go-macaron/cache/redis"
  19. "github.com/go-macaron/session"
  20. _ "github.com/go-macaron/session/redis"
  21. "github.com/mcuadros/go-version"
  22. log "gopkg.in/clog.v1"
  23. "gopkg.in/ini.v1"
  24. "github.com/gogits/go-libravatar"
  25. "github.com/G-Node/gogs/pkg/bindata"
  26. "github.com/G-Node/gogs/pkg/process"
  27. "github.com/G-Node/gogs/pkg/user"
  28. )
  29. type Scheme string
  30. const (
  31. SCHEME_HTTP Scheme = "http"
  32. SCHEME_HTTPS Scheme = "https"
  33. SCHEME_FCGI Scheme = "fcgi"
  34. SCHEME_UNIX_SOCKET Scheme = "unix"
  35. )
  36. type LandingPage string
  37. const (
  38. LANDING_PAGE_HOME LandingPage = "/"
  39. LANDING_PAGE_EXPLORE LandingPage = "/explore"
  40. )
  41. var (
  42. // Build information should only be set by -ldflags.
  43. BuildTime string
  44. BuildGitHash string
  45. // App settings
  46. AppVer string
  47. AppName string
  48. AppURL string
  49. AppSubURL string
  50. AppSubURLDepth int // Number of slashes
  51. AppPath string
  52. AppDataPath string
  53. // Server settings
  54. Protocol Scheme
  55. Domain string
  56. HTTPAddr string
  57. HTTPPort string
  58. LocalURL string
  59. OfflineMode bool
  60. DisableRouterLog bool
  61. CertFile, KeyFile string
  62. TLSMinVersion string
  63. StaticRootPath string
  64. EnableGzip bool
  65. LandingPageURL LandingPage
  66. UnixSocketPermission uint32
  67. HTTP struct {
  68. AccessControlAllowOrigin string
  69. }
  70. SSH struct {
  71. Disabled bool `ini:"DISABLE_SSH"`
  72. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  73. Domain string `ini:"SSH_DOMAIN"`
  74. Port int `ini:"SSH_PORT"`
  75. ListenHost string `ini:"SSH_LISTEN_HOST"`
  76. ListenPort int `ini:"SSH_LISTEN_PORT"`
  77. RootPath string `ini:"SSH_ROOT_PATH"`
  78. ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
  79. KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
  80. KeygenPath string `ini:"SSH_KEYGEN_PATH"`
  81. MinimumKeySizeCheck bool `ini:"MINIMUM_KEY_SIZE_CHECK"`
  82. MinimumKeySizes map[string]int `ini:"-"`
  83. }
  84. // Security settings
  85. InstallLock bool
  86. SecretKey string
  87. LoginRememberDays int
  88. CookieUserName string
  89. CookieRememberName string
  90. CookieSecure bool
  91. ReverseProxyAuthUser string
  92. EnableLoginStatusCookie bool
  93. LoginStatusCookieName string
  94. DoiKey string
  95. // Database settings
  96. UseSQLite3 bool
  97. UseMySQL bool
  98. UsePostgreSQL bool
  99. UseMSSQL bool
  100. // Repository settings
  101. Repository struct {
  102. AnsiCharset string
  103. ForcePrivate bool
  104. MaxCreationLimit int
  105. MirrorQueueLength int
  106. PullRequestQueueLength int
  107. PreferredLicenses []string
  108. DisableHTTPGit bool `ini:"DISABLE_HTTP_GIT"`
  109. EnableLocalPathMigration bool
  110. CommitsFetchConcurrency int
  111. EnableRawFileRenderMode bool
  112. RawCaptchaMinFileSize int64
  113. CaptchaMinFileSize int64
  114. // Repository editor settings
  115. Editor struct {
  116. LineWrapExtensions []string
  117. PreviewableFileModes []string
  118. } `ini:"-"`
  119. // Repository upload settings
  120. Upload struct {
  121. Enabled bool
  122. TempPath string
  123. AllowedTypes []string `delim:"|"`
  124. FileMaxSize int64
  125. AnexFileMinSize int64
  126. MaxFiles int
  127. } `ini:"-"`
  128. }
  129. RepoRootPath string
  130. ScriptType string
  131. // Webhook settings
  132. Webhook struct {
  133. Types []string
  134. QueueLength int
  135. DeliverTimeout int
  136. SkipTLSVerify bool `ini:"SKIP_TLS_VERIFY"`
  137. PagingNum int
  138. }
  139. // Release settigns
  140. Release struct {
  141. Attachment struct {
  142. Enabled bool
  143. TempPath string
  144. AllowedTypes []string `delim:"|"`
  145. MaxSize int64
  146. MaxFiles int
  147. } `ini:"-"`
  148. }
  149. // Markdown sttings
  150. Markdown struct {
  151. EnableHardLineBreak bool
  152. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  153. FileExtensions []string
  154. }
  155. // Smartypants settings
  156. Smartypants struct {
  157. Enabled bool
  158. Fractions bool
  159. Dashes bool
  160. LatexDashes bool
  161. AngledQuotes bool
  162. }
  163. // Admin settings
  164. Admin struct {
  165. DisableRegularOrgCreation bool
  166. }
  167. // Picture settings
  168. AvatarUploadPath string
  169. GravatarSource string
  170. DisableGravatar bool
  171. EnableFederatedAvatar bool
  172. LibravatarService *libravatar.Libravatar
  173. // Log settings
  174. LogRootPath string
  175. LogModes []string
  176. LogConfigs []interface{}
  177. // Attachment settings
  178. AttachmentPath string
  179. AttachmentAllowedTypes string
  180. AttachmentMaxSize int64
  181. AttachmentMaxFiles int
  182. AttachmentEnabled bool
  183. // Time settings
  184. TimeFormat string
  185. // Cache settings
  186. CacheAdapter string
  187. CacheInterval int
  188. CacheConn string
  189. // Session settings
  190. SessionConfig session.Options
  191. CSRFCookieName string
  192. // Cron tasks
  193. Cron struct {
  194. UpdateMirror struct {
  195. Enabled bool
  196. RunAtStart bool
  197. Schedule string
  198. } `ini:"cron.update_mirrors"`
  199. RepoHealthCheck struct {
  200. Enabled bool
  201. RunAtStart bool
  202. Schedule string
  203. Timeout time.Duration
  204. Args []string `delim:" "`
  205. } `ini:"cron.repo_health_check"`
  206. CheckRepoStats struct {
  207. Enabled bool
  208. RunAtStart bool
  209. Schedule string
  210. } `ini:"cron.check_repo_stats"`
  211. RepoArchiveCleanup struct {
  212. Enabled bool
  213. RunAtStart bool
  214. Schedule string
  215. OlderThan time.Duration
  216. } `ini:"cron.repo_archive_cleanup"`
  217. }
  218. // Git settings
  219. Git struct {
  220. Version string `ini:"-"`
  221. DisableDiffHighlight bool
  222. MaxGitDiffLines int
  223. MaxGitDiffLineCharacters int
  224. MaxGitDiffFiles int
  225. GCArgs []string `delim:" "`
  226. Timeout struct {
  227. Migrate int
  228. Mirror int
  229. Clone int
  230. Pull int
  231. GC int `ini:"GC"`
  232. } `ini:"git.timeout"`
  233. }
  234. // Mirror settings
  235. Mirror struct {
  236. DefaultInterval int
  237. }
  238. // API settings
  239. API struct {
  240. MaxResponseItems int
  241. }
  242. // UI settings
  243. UI struct {
  244. ExplorePagingNum int
  245. IssuePagingNum int
  246. FeedMaxCommitNum int
  247. ThemeColorMetaTag string
  248. MaxDisplayFileSize int64
  249. MaxLineHighlight int
  250. Admin struct {
  251. UserPagingNum int
  252. RepoPagingNum int
  253. NoticePagingNum int
  254. OrgPagingNum int
  255. } `ini:"ui.admin"`
  256. User struct {
  257. RepoPagingNum int
  258. NewsFeedPagingNum int
  259. CommitsPagingNum int
  260. } `ini:"ui.user"`
  261. }
  262. // I18n settings
  263. Langs []string
  264. Names []string
  265. dateLangs map[string]string
  266. // Highlight settings are loaded in modules/template/hightlight.go
  267. // Other settings
  268. ShowFooterBranding bool
  269. ShowFooterVersion bool
  270. ShowFooterTemplateLoadTime bool
  271. SupportMiniWinService bool
  272. // Global setting objects
  273. Cfg *ini.File
  274. CustomPath string // Custom directory path
  275. CustomConf string
  276. ProdMode bool
  277. RunUser string
  278. IsWindows bool
  279. HasRobotsTxt bool
  280. Search struct {
  281. Do bool
  282. IndexUrl string
  283. SearchUrl string
  284. }
  285. Doi struct {
  286. Do bool
  287. DoiUrl string
  288. DoiKey string
  289. }
  290. )
  291. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  292. func DateLang(lang string) string {
  293. name, ok := dateLangs[lang]
  294. if ok {
  295. return name
  296. }
  297. return "en"
  298. }
  299. // execPath returns the executable path.
  300. func execPath() (string, error) {
  301. file, err := exec.LookPath(os.Args[0])
  302. if err != nil {
  303. return "", err
  304. }
  305. return filepath.Abs(file)
  306. }
  307. func init() {
  308. IsWindows = runtime.GOOS == "windows"
  309. log.New(log.CONSOLE, log.ConsoleConfig{})
  310. var err error
  311. if AppPath, err = execPath(); err != nil {
  312. log.Fatal(2, "Fail to get app path: %v\n", err)
  313. }
  314. // Note: we don't use path.Dir here because it does not handle case
  315. // which path starts with two "/" in Windows: "//psf/Home/..."
  316. AppPath = strings.Replace(AppPath, "\\", "/", -1)
  317. }
  318. // WorkDir returns absolute path of work directory.
  319. func WorkDir() (string, error) {
  320. wd := os.Getenv("GOGS_WORK_DIR")
  321. if len(wd) > 0 {
  322. return wd, nil
  323. }
  324. i := strings.LastIndex(AppPath, "/")
  325. if i == -1 {
  326. return AppPath, nil
  327. }
  328. return AppPath[:i], nil
  329. }
  330. func forcePathSeparator(path string) {
  331. if strings.Contains(path, "\\") {
  332. log.Fatal(2, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
  333. }
  334. }
  335. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  336. // actual user that runs the app. The first return value is the actual user name.
  337. // This check is ignored under Windows since SSH remote login is not the main
  338. // method to login on Windows.
  339. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  340. if IsWindows {
  341. return "", true
  342. }
  343. currentUser := user.CurrentUsername()
  344. return currentUser, runUser == currentUser
  345. }
  346. // getOpenSSHVersion parses and returns string representation of OpenSSH version
  347. // returned by command "ssh -V".
  348. func getOpenSSHVersion() string {
  349. // Note: somehow version is printed to stderr
  350. _, stderr, err := process.Exec("getOpenSSHVersion", "ssh", "-V")
  351. if err != nil {
  352. log.Fatal(2, "Fail to get OpenSSH version: %v - %s", err, stderr)
  353. }
  354. // Trim unused information: https://github.com/gogits/gogs/issues/4507#issuecomment-305150441
  355. version := strings.TrimRight(strings.Fields(stderr)[0], ",1234567890")
  356. version = strings.TrimSuffix(strings.TrimPrefix(version, "OpenSSH_"), "p")
  357. return version
  358. }
  359. // NewContext initializes configuration context.
  360. // NOTE: do not print any log except error.
  361. func NewContext() {
  362. workDir, err := WorkDir()
  363. if err != nil {
  364. log.Fatal(2, "Fail to get work directory: %v", err)
  365. }
  366. Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini"))
  367. if err != nil {
  368. log.Fatal(2, "Fail to parse 'conf/app.ini': %v", err)
  369. }
  370. CustomPath = os.Getenv("GOGS_CUSTOM")
  371. if len(CustomPath) == 0 {
  372. CustomPath = workDir + "/custom"
  373. }
  374. if len(CustomConf) == 0 {
  375. CustomConf = CustomPath + "/conf/app.ini"
  376. }
  377. if com.IsFile(CustomConf) {
  378. if err = Cfg.Append(CustomConf); err != nil {
  379. log.Fatal(2, "Fail to load custom conf '%s': %v", CustomConf, err)
  380. }
  381. } else {
  382. log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
  383. }
  384. Cfg.NameMapper = ini.AllCapsUnderscore
  385. homeDir, err := com.HomeDir()
  386. if err != nil {
  387. log.Fatal(2, "Fail to get home directory: %v", err)
  388. }
  389. homeDir = strings.Replace(homeDir, "\\", "/", -1)
  390. LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))
  391. forcePathSeparator(LogRootPath)
  392. sec := Cfg.Section("server")
  393. AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs")
  394. AppURL = sec.Key("ROOT_URL").MustString("http://localhost:3000/")
  395. if AppURL[len(AppURL)-1] != '/' {
  396. AppURL += "/"
  397. }
  398. // Check if has app suburl.
  399. url, err := url.Parse(AppURL)
  400. if err != nil {
  401. log.Fatal(2, "Invalid ROOT_URL '%s': %s", AppURL, err)
  402. }
  403. // Suburl should start with '/' and end without '/', such as '/{subpath}'.
  404. // This value is empty if site does not have sub-url.
  405. AppSubURL = strings.TrimSuffix(url.Path, "/")
  406. AppSubURLDepth = strings.Count(AppSubURL, "/")
  407. Protocol = SCHEME_HTTP
  408. if sec.Key("PROTOCOL").String() == "https" {
  409. Protocol = SCHEME_HTTPS
  410. CertFile = sec.Key("CERT_FILE").String()
  411. KeyFile = sec.Key("KEY_FILE").String()
  412. TLSMinVersion = sec.Key("TLS_MIN_VERSION").String()
  413. } else if sec.Key("PROTOCOL").String() == "fcgi" {
  414. Protocol = SCHEME_FCGI
  415. } else if sec.Key("PROTOCOL").String() == "unix" {
  416. Protocol = SCHEME_UNIX_SOCKET
  417. UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
  418. UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
  419. if err != nil || UnixSocketPermissionParsed > 0777 {
  420. log.Fatal(2, "Fail to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
  421. }
  422. UnixSocketPermission = uint32(UnixSocketPermissionParsed)
  423. }
  424. Domain = sec.Key("DOMAIN").MustString("localhost")
  425. HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
  426. HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
  427. LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(string(Protocol) + "://localhost:" + HTTPPort + "/")
  428. OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
  429. DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
  430. StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
  431. AppDataPath = sec.Key("APP_DATA_PATH").MustString("data")
  432. EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
  433. switch sec.Key("LANDING_PAGE").MustString("home") {
  434. case "explore":
  435. LandingPageURL = LANDING_PAGE_EXPLORE
  436. default:
  437. LandingPageURL = LANDING_PAGE_HOME
  438. }
  439. SSH.RootPath = path.Join(homeDir, ".ssh")
  440. SSH.ServerCiphers = sec.Key("SSH_SERVER_CIPHERS").Strings(",")
  441. SSH.KeyTestPath = os.TempDir()
  442. if err = Cfg.Section("server").MapTo(&SSH); err != nil {
  443. log.Fatal(2, "Fail to map SSH settings: %v", err)
  444. }
  445. if SSH.Disabled {
  446. SSH.StartBuiltinServer = false
  447. SSH.MinimumKeySizeCheck = false
  448. }
  449. if !SSH.Disabled && !SSH.StartBuiltinServer {
  450. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  451. log.Fatal(2, "Fail to create '%s': %v", SSH.RootPath, err)
  452. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  453. log.Fatal(2, "Fail to create '%s': %v", SSH.KeyTestPath, err)
  454. }
  455. }
  456. // Check if server is eligible for minimum key size check when user choose to enable.
  457. // Windows server and OpenSSH version lower than 5.1 (https://github.com/gogits/gogs/issues/4507)
  458. // are forced to be disabled because the "ssh-keygen" in Windows does not print key type.
  459. if SSH.MinimumKeySizeCheck &&
  460. (IsWindows || version.Compare(getOpenSSHVersion(), "5.1", "<")) {
  461. SSH.MinimumKeySizeCheck = false
  462. log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
  463. 1. Windows server
  464. 2. OpenSSH version is lower than 5.1`)
  465. }
  466. if SSH.MinimumKeySizeCheck {
  467. SSH.MinimumKeySizes = map[string]int{}
  468. for _, key := range Cfg.Section("ssh.minimum_key_sizes").Keys() {
  469. if key.MustInt() != -1 {
  470. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  471. }
  472. }
  473. }
  474. sec = Cfg.Section("security")
  475. InstallLock = sec.Key("INSTALL_LOCK").MustBool()
  476. SecretKey = sec.Key("SECRET_KEY").String()
  477. LoginRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
  478. CookieUserName = sec.Key("COOKIE_USERNAME").String()
  479. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
  480. CookieSecure = sec.Key("COOKIE_SECURE").MustBool(false)
  481. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  482. EnableLoginStatusCookie = sec.Key("ENABLE_LOGIN_STATUS_COOKIE").MustBool(false)
  483. LoginStatusCookieName = sec.Key("LOGIN_STATUS_COOKIE_NAME").MustString("login_status")
  484. sec = Cfg.Section("attachment")
  485. AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
  486. if !filepath.IsAbs(AttachmentPath) {
  487. AttachmentPath = path.Join(workDir, AttachmentPath)
  488. }
  489. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
  490. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  491. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  492. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  493. TimeFormat = map[string]string{
  494. "ANSIC": time.ANSIC,
  495. "UnixDate": time.UnixDate,
  496. "RubyDate": time.RubyDate,
  497. "RFC822": time.RFC822,
  498. "RFC822Z": time.RFC822Z,
  499. "RFC850": time.RFC850,
  500. "RFC1123": time.RFC1123,
  501. "RFC1123Z": time.RFC1123Z,
  502. "RFC3339": time.RFC3339,
  503. "RFC3339Nano": time.RFC3339Nano,
  504. "Kitchen": time.Kitchen,
  505. "Stamp": time.Stamp,
  506. "StampMilli": time.StampMilli,
  507. "StampMicro": time.StampMicro,
  508. "StampNano": time.StampNano,
  509. }[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
  510. RunUser = Cfg.Section("").Key("RUN_USER").String()
  511. // Does not check run user when the install lock is off.
  512. if InstallLock {
  513. currentUser, match := IsRunUserMatchCurrentUser(RunUser)
  514. if !match {
  515. log.Fatal(2, "Expect user '%s' but current user is: %s", RunUser, currentUser)
  516. }
  517. }
  518. ProdMode = Cfg.Section("").Key("RUN_MODE").String() == "prod"
  519. // Determine and create root git repository path.
  520. sec = Cfg.Section("repository")
  521. RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gogs-repositories"))
  522. forcePathSeparator(RepoRootPath)
  523. if !filepath.IsAbs(RepoRootPath) {
  524. RepoRootPath = path.Join(workDir, RepoRootPath)
  525. } else {
  526. RepoRootPath = path.Clean(RepoRootPath)
  527. }
  528. ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
  529. if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
  530. log.Fatal(2, "Fail to map Repository settings: %v", err)
  531. } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
  532. log.Fatal(2, "Fail to map Repository.Editor settings: %v", err)
  533. } else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
  534. log.Fatal(2, "Fail to map Repository.Upload settings: %v", err)
  535. }
  536. if !filepath.IsAbs(Repository.Upload.TempPath) {
  537. Repository.Upload.TempPath = path.Join(workDir, Repository.Upload.TempPath)
  538. }
  539. sec = Cfg.Section("picture")
  540. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))
  541. forcePathSeparator(AvatarUploadPath)
  542. if !filepath.IsAbs(AvatarUploadPath) {
  543. AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
  544. }
  545. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  546. case "duoshuo":
  547. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  548. case "gravatar":
  549. GravatarSource = "https://secure.gravatar.com/avatar/"
  550. case "libravatar":
  551. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  552. default:
  553. GravatarSource = source
  554. }
  555. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  556. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(true)
  557. if OfflineMode {
  558. DisableGravatar = true
  559. EnableFederatedAvatar = false
  560. }
  561. if DisableGravatar {
  562. EnableFederatedAvatar = false
  563. }
  564. if EnableFederatedAvatar {
  565. LibravatarService = libravatar.New()
  566. parts := strings.Split(GravatarSource, "/")
  567. if len(parts) >= 3 {
  568. if parts[0] == "https:" {
  569. LibravatarService.SetUseHTTPS(true)
  570. LibravatarService.SetSecureFallbackHost(parts[2])
  571. } else {
  572. LibravatarService.SetUseHTTPS(false)
  573. LibravatarService.SetFallbackHost(parts[2])
  574. }
  575. }
  576. }
  577. if err = Cfg.Section("http").MapTo(&HTTP); err != nil {
  578. log.Fatal(2, "Fail to map HTTP settings: %v", err)
  579. } else if err = Cfg.Section("webhook").MapTo(&Webhook); err != nil {
  580. log.Fatal(2, "Fail to map Webhook settings: %v", err)
  581. } else if err = Cfg.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
  582. log.Fatal(2, "Fail to map Release.Attachment settings: %v", err)
  583. } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
  584. log.Fatal(2, "Fail to map Markdown settings: %v", err)
  585. } else if err = Cfg.Section("smartypants").MapTo(&Smartypants); err != nil {
  586. log.Fatal(2, "Fail to map Smartypants settings: %v", err)
  587. } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
  588. log.Fatal(2, "Fail to map Admin settings: %v", err)
  589. } else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {
  590. log.Fatal(2, "Fail to map Cron settings: %v", err)
  591. } else if err = Cfg.Section("git").MapTo(&Git); err != nil {
  592. log.Fatal(2, "Fail to map Git settings: %v", err)
  593. } else if err = Cfg.Section("mirror").MapTo(&Mirror); err != nil {
  594. log.Fatal(2, "Fail to map Mirror settings: %v", err)
  595. } else if err = Cfg.Section("api").MapTo(&API); err != nil {
  596. log.Fatal(2, "Fail to map API settings: %v", err)
  597. } else if err = Cfg.Section("ui").MapTo(&UI); err != nil {
  598. log.Fatal(2, "Fail to map UI settings: %v", err)
  599. } else if err = Cfg.Section("search").MapTo(&Search); err != nil {
  600. log.Fatal(2, "Fail to map Search settings: %v", err)
  601. } else if err = Cfg.Section("doi").MapTo(&Doi); err != nil {
  602. log.Fatal(2, "Fail to map Doi settings: %v", err)
  603. }
  604. if Mirror.DefaultInterval <= 0 {
  605. Mirror.DefaultInterval = 24
  606. }
  607. Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
  608. Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
  609. dateLangs = Cfg.Section("i18n.datelang").KeysHash()
  610. ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
  611. ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool()
  612. ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool()
  613. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  614. }
  615. var Service struct {
  616. ActiveCodeLives int
  617. ResetPwdCodeLives int
  618. RegisterEmailConfirm bool
  619. DisableRegistration bool
  620. ShowRegistrationButton bool
  621. RequireSignInView bool
  622. EnableNotifyMail bool
  623. EnableReverseProxyAuth bool
  624. EnableReverseProxyAutoRegister bool
  625. EnableCaptcha bool
  626. }
  627. func newService() {
  628. sec := Cfg.Section("service")
  629. Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
  630. Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
  631. Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
  632. Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
  633. Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
  634. Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
  635. Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
  636. Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
  637. }
  638. func newLogService() {
  639. if len(BuildTime) > 0 {
  640. log.Trace("Build Time: %s", BuildTime)
  641. log.Trace("Build Git Hash: %s", BuildGitHash)
  642. }
  643. // Because we always create a console logger as primary logger before all settings are loaded,
  644. // thus if user doesn't set console logger, we should remove it after other loggers are created.
  645. hasConsole := false
  646. // Get and check log modes.
  647. LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
  648. LogConfigs = make([]interface{}, len(LogModes))
  649. levelNames := map[string]log.LEVEL{
  650. "trace": log.TRACE,
  651. "info": log.INFO,
  652. "warn": log.WARN,
  653. "error": log.ERROR,
  654. "fatal": log.FATAL,
  655. }
  656. for i, mode := range LogModes {
  657. mode = strings.ToLower(strings.TrimSpace(mode))
  658. sec, err := Cfg.GetSection("log." + mode)
  659. if err != nil {
  660. log.Fatal(2, "Unknown logger mode: %s", mode)
  661. }
  662. validLevels := []string{"trace", "info", "warn", "error", "fatal"}
  663. name := Cfg.Section("log." + mode).Key("LEVEL").Validate(func(v string) string {
  664. v = strings.ToLower(v)
  665. if com.IsSliceContainsStr(validLevels, v) {
  666. return v
  667. }
  668. return "trace"
  669. })
  670. level := levelNames[name]
  671. // Generate log configuration.
  672. switch log.MODE(mode) {
  673. case log.CONSOLE:
  674. hasConsole = true
  675. LogConfigs[i] = log.ConsoleConfig{
  676. Level: level,
  677. BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
  678. }
  679. case log.FILE:
  680. logPath := path.Join(LogRootPath, "gogs.log")
  681. if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  682. log.Fatal(2, "Fail to create log directory '%s': %v", path.Dir(logPath), err)
  683. }
  684. LogConfigs[i] = log.FileConfig{
  685. Level: level,
  686. BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
  687. Filename: logPath,
  688. FileRotationConfig: log.FileRotationConfig{
  689. Rotate: sec.Key("LOG_ROTATE").MustBool(true),
  690. Daily: sec.Key("DAILY_ROTATE").MustBool(true),
  691. MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  692. MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
  693. MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
  694. },
  695. }
  696. case log.SLACK:
  697. LogConfigs[i] = log.SlackConfig{
  698. Level: level,
  699. BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
  700. URL: sec.Key("URL").String(),
  701. }
  702. }
  703. log.New(log.MODE(mode), LogConfigs[i])
  704. log.Trace("Log Mode: %s (%s)", strings.Title(mode), strings.Title(name))
  705. }
  706. // Make sure everyone gets version info printed.
  707. log.Info("%s %s", AppName, AppVer)
  708. if !hasConsole {
  709. log.Delete(log.CONSOLE)
  710. }
  711. }
  712. func newCacheService() {
  713. CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
  714. switch CacheAdapter {
  715. case "memory":
  716. CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
  717. case "redis", "memcache":
  718. CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
  719. default:
  720. log.Fatal(2, "Unknown cache adapter: %s", CacheAdapter)
  721. }
  722. log.Info("Cache Service Enabled")
  723. }
  724. func newSessionService() {
  725. SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
  726. []string{"memory", "file", "redis", "mysql"})
  727. SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
  728. SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")
  729. SessionConfig.CookiePath = AppSubURL
  730. SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
  731. SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(3600)
  732. SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
  733. CSRFCookieName = Cfg.Section("session").Key("CSRF_COOKIE_NAME").MustString("_csrf")
  734. log.Info("Session Service Enabled")
  735. }
  736. // Mailer represents mail service.
  737. type Mailer struct {
  738. QueueLength int
  739. Subject string
  740. Host string
  741. From string
  742. FromEmail string
  743. User, Passwd string
  744. DisableHelo bool
  745. HeloHostname string
  746. SkipVerify bool
  747. UseCertificate bool
  748. CertFile, KeyFile string
  749. UsePlainText bool
  750. }
  751. var (
  752. MailService *Mailer
  753. )
  754. func newMailService() {
  755. sec := Cfg.Section("mailer")
  756. // Check mailer setting.
  757. if !sec.Key("ENABLED").MustBool() {
  758. return
  759. }
  760. MailService = &Mailer{
  761. QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
  762. Subject: sec.Key("SUBJECT").MustString(AppName),
  763. Host: sec.Key("HOST").String(),
  764. User: sec.Key("USER").String(),
  765. Passwd: sec.Key("PASSWD").String(),
  766. DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
  767. HeloHostname: sec.Key("HELO_HOSTNAME").String(),
  768. SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
  769. UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
  770. CertFile: sec.Key("CERT_FILE").String(),
  771. KeyFile: sec.Key("KEY_FILE").String(),
  772. UsePlainText: sec.Key("USE_PLAIN_TEXT").MustBool(),
  773. }
  774. MailService.From = sec.Key("FROM").MustString(MailService.User)
  775. if len(MailService.From) > 0 {
  776. parsed, err := mail.ParseAddress(MailService.From)
  777. if err != nil {
  778. log.Fatal(2, "Invalid mailer.FROM (%s): %v", MailService.From, err)
  779. }
  780. MailService.FromEmail = parsed.Address
  781. }
  782. log.Info("Mail Service Enabled")
  783. }
  784. func newRegisterMailService() {
  785. if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  786. return
  787. } else if MailService == nil {
  788. log.Warn("Register Mail Service: Mail Service is not enabled")
  789. return
  790. }
  791. Service.RegisterEmailConfirm = true
  792. log.Info("Register Mail Service Enabled")
  793. }
  794. func newNotifyMailService() {
  795. if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  796. return
  797. } else if MailService == nil {
  798. log.Warn("Notify Mail Service: Mail Service is not enabled")
  799. return
  800. }
  801. Service.EnableNotifyMail = true
  802. log.Info("Notify Mail Service Enabled")
  803. }
  804. func NewService() {
  805. newService()
  806. }
  807. func NewServices() {
  808. newService()
  809. newLogService()
  810. newCacheService()
  811. newSessionService()
  812. newMailService()
  813. newRegisterMailService()
  814. newNotifyMailService()
  815. }