conf.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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 conf
  5. import (
  6. "fmt"
  7. "net/mail"
  8. "net/url"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "time"
  15. _ "github.com/go-macaron/cache/memcache"
  16. _ "github.com/go-macaron/cache/redis"
  17. "github.com/go-macaron/session"
  18. _ "github.com/go-macaron/session/redis"
  19. "github.com/mcuadros/go-version"
  20. "github.com/pkg/errors"
  21. "gopkg.in/ini.v1"
  22. log "unknwon.dev/clog/v2"
  23. "github.com/gogs/go-libravatar"
  24. "gogs.io/gogs/internal/assets/conf"
  25. "gogs.io/gogs/internal/osutil"
  26. )
  27. func init() {
  28. // Initialize the primary logger until logging service is up.
  29. err := log.NewConsole()
  30. if err != nil {
  31. panic("init console logger: " + err.Error())
  32. }
  33. }
  34. // Asset is a wrapper for getting conf assets.
  35. func Asset(name string) ([]byte, error) {
  36. return conf.Asset(name)
  37. }
  38. // AssetDir is a wrapper for getting conf assets.
  39. func AssetDir(name string) ([]string, error) {
  40. return conf.AssetDir(name)
  41. }
  42. // MustAsset is a wrapper for getting conf assets.
  43. func MustAsset(name string) []byte {
  44. return conf.MustAsset(name)
  45. }
  46. // File is the configuration object.
  47. var File *ini.File
  48. // Init initializes configuration from conf assets and given custom configuration file.
  49. // If `customConf` is empty, it falls back to default location, i.e. "<WORK DIR>/custom".
  50. // It is safe to call this function multiple times with desired `customConf`, but it is
  51. // not concurrent safe.
  52. //
  53. // ⚠️ WARNING: Do not print anything in this function other than wanrings.
  54. func Init(customConf string) error {
  55. var err error
  56. File, err = ini.LoadSources(ini.LoadOptions{
  57. IgnoreInlineComment: true,
  58. }, conf.MustAsset("conf/app.ini"))
  59. if err != nil {
  60. return errors.Wrap(err, "parse 'conf/app.ini'")
  61. }
  62. File.NameMapper = ini.SnackCase
  63. if customConf == "" {
  64. customConf = filepath.Join(CustomDir(), "conf", "app.ini")
  65. } else {
  66. customConf, err = filepath.Abs(customConf)
  67. if err != nil {
  68. return errors.Wrap(err, "get absolute path")
  69. }
  70. }
  71. CustomConf = customConf
  72. if osutil.IsFile(customConf) {
  73. if err = File.Append(customConf); err != nil {
  74. return errors.Wrapf(err, "append %q", customConf)
  75. }
  76. } else {
  77. log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
  78. }
  79. if err = File.Section(ini.DefaultSection).MapTo(&App); err != nil {
  80. return errors.Wrap(err, "mapping default section")
  81. }
  82. // ***************************
  83. // ----- Server settings -----
  84. // ***************************
  85. if err = File.Section("server").MapTo(&Server); err != nil {
  86. return errors.Wrap(err, "mapping [server] section")
  87. }
  88. Server.AppDataPath = ensureAbs(Server.AppDataPath)
  89. if !strings.HasSuffix(Server.ExternalURL, "/") {
  90. Server.ExternalURL += "/"
  91. }
  92. Server.URL, err = url.Parse(Server.ExternalURL)
  93. if err != nil {
  94. return errors.Wrapf(err, "parse '[server] EXTERNAL_URL' %q", err)
  95. }
  96. // Subpath should start with '/' and end without '/', i.e. '/{subpath}'.
  97. Server.Subpath = strings.TrimRight(Server.URL.Path, "/")
  98. Server.SubpathDepth = strings.Count(Server.Subpath, "/")
  99. unixSocketMode, err := strconv.ParseUint(Server.UnixSocketPermission, 8, 32)
  100. if err != nil {
  101. return errors.Wrapf(err, "parse '[server] UNIX_SOCKET_PERMISSION' %q", Server.UnixSocketPermission)
  102. }
  103. if unixSocketMode > 0777 {
  104. unixSocketMode = 0666
  105. }
  106. Server.UnixSocketMode = os.FileMode(unixSocketMode)
  107. // ************************
  108. // ----- SSH settings -----
  109. // ************************
  110. SSH.RootPath = filepath.Join(HomeDir(), ".ssh")
  111. SSH.KeyTestPath = os.TempDir()
  112. if err = File.Section("server").MapTo(&SSH); err != nil {
  113. return errors.Wrap(err, "mapping SSH settings from [server] section")
  114. }
  115. SSH.RootPath = ensureAbs(SSH.RootPath)
  116. SSH.KeyTestPath = ensureAbs(SSH.KeyTestPath)
  117. if !SSH.Disabled {
  118. if !SSH.StartBuiltinServer {
  119. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  120. return errors.Wrap(err, "create SSH root directory")
  121. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  122. return errors.Wrap(err, "create SSH key test directory")
  123. }
  124. } else {
  125. SSH.RewriteAuthorizedKeysAtStart = false
  126. }
  127. // Check if server is eligible for minimum key size check when user choose to enable.
  128. // Windows server and OpenSSH version lower than 5.1 are forced to be disabled because
  129. // the "ssh-keygen" in Windows does not print key type.
  130. // See https://github.com/gogs/gogs/issues/4507.
  131. if SSH.MinimumKeySizeCheck {
  132. sshVersion, err := openSSHVersion()
  133. if err != nil {
  134. return errors.Wrap(err, "get OpenSSH version")
  135. }
  136. if IsWindowsRuntime() || version.Compare(sshVersion, "5.1", "<") {
  137. log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
  138. 1. Windows server
  139. 2. OpenSSH version is lower than 5.1`)
  140. } else {
  141. SSH.MinimumKeySizes = map[string]int{}
  142. for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
  143. if key.MustInt() != -1 {
  144. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  145. }
  146. }
  147. }
  148. }
  149. }
  150. // *******************************
  151. // ----- Repository settings -----
  152. // *******************************
  153. Repository.Root = filepath.Join(HomeDir(), "gogs-repositories")
  154. if err = File.Section("repository").MapTo(&Repository); err != nil {
  155. return errors.Wrap(err, "mapping [repository] section")
  156. }
  157. Repository.Root = ensureAbs(Repository.Root)
  158. Repository.Upload.TempPath = ensureAbs(Repository.Upload.TempPath)
  159. // *******************************
  160. // ----- Database settings -----
  161. // *******************************
  162. if err = File.Section("database").MapTo(&Database); err != nil {
  163. return errors.Wrap(err, "mapping [database] section")
  164. }
  165. Database.Path = ensureAbs(Database.Path)
  166. // *******************************
  167. // ----- Security settings -----
  168. // *******************************
  169. if err = File.Section("security").MapTo(&Security); err != nil {
  170. return errors.Wrap(err, "mapping [security] section")
  171. }
  172. // Check run user when the install is locked.
  173. if Security.InstallLock {
  174. currentUser, match := CheckRunUser(App.RunUser)
  175. if !match {
  176. return fmt.Errorf("user configured to run Gogs is %q, but the current user is %q", App.RunUser, currentUser)
  177. }
  178. }
  179. handleDeprecated()
  180. // TODO
  181. sec := File.Section("attachment")
  182. AttachmentPath = sec.Key("PATH").MustString(filepath.Join(Server.AppDataPath, "attachments"))
  183. if !filepath.IsAbs(AttachmentPath) {
  184. AttachmentPath = path.Join(workDir, AttachmentPath)
  185. }
  186. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
  187. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  188. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  189. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  190. TimeFormat = map[string]string{
  191. "ANSIC": time.ANSIC,
  192. "UnixDate": time.UnixDate,
  193. "RubyDate": time.RubyDate,
  194. "RFC822": time.RFC822,
  195. "RFC822Z": time.RFC822Z,
  196. "RFC850": time.RFC850,
  197. "RFC1123": time.RFC1123,
  198. "RFC1123Z": time.RFC1123Z,
  199. "RFC3339": time.RFC3339,
  200. "RFC3339Nano": time.RFC3339Nano,
  201. "Kitchen": time.Kitchen,
  202. "Stamp": time.Stamp,
  203. "StampMilli": time.StampMilli,
  204. "StampMicro": time.StampMicro,
  205. "StampNano": time.StampNano,
  206. }[File.Section("time").Key("FORMAT").MustString("RFC1123")]
  207. sec = File.Section("picture")
  208. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "avatars"))
  209. if !filepath.IsAbs(AvatarUploadPath) {
  210. AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
  211. }
  212. RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "repo-avatars"))
  213. if !filepath.IsAbs(RepositoryAvatarUploadPath) {
  214. RepositoryAvatarUploadPath = path.Join(workDir, RepositoryAvatarUploadPath)
  215. }
  216. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  217. case "duoshuo":
  218. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  219. case "gravatar":
  220. GravatarSource = "https://secure.gravatar.com/avatar/"
  221. case "libravatar":
  222. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  223. default:
  224. GravatarSource = source
  225. }
  226. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  227. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(true)
  228. if Server.OfflineMode {
  229. DisableGravatar = true
  230. EnableFederatedAvatar = false
  231. }
  232. if DisableGravatar {
  233. EnableFederatedAvatar = false
  234. }
  235. if EnableFederatedAvatar {
  236. LibravatarService = libravatar.New()
  237. parts := strings.Split(GravatarSource, "/")
  238. if len(parts) >= 3 {
  239. if parts[0] == "https:" {
  240. LibravatarService.SetUseHTTPS(true)
  241. LibravatarService.SetSecureFallbackHost(parts[2])
  242. } else {
  243. LibravatarService.SetUseHTTPS(false)
  244. LibravatarService.SetFallbackHost(parts[2])
  245. }
  246. }
  247. }
  248. if err = File.Section("http").MapTo(&HTTP); err != nil {
  249. log.Fatal("Failed to map HTTP settings: %v", err)
  250. } else if err = File.Section("webhook").MapTo(&Webhook); err != nil {
  251. log.Fatal("Failed to map Webhook settings: %v", err)
  252. } else if err = File.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
  253. log.Fatal("Failed to map Release.Attachment settings: %v", err)
  254. } else if err = File.Section("markdown").MapTo(&Markdown); err != nil {
  255. log.Fatal("Failed to map Markdown settings: %v", err)
  256. } else if err = File.Section("smartypants").MapTo(&Smartypants); err != nil {
  257. log.Fatal("Failed to map Smartypants settings: %v", err)
  258. } else if err = File.Section("admin").MapTo(&Admin); err != nil {
  259. log.Fatal("Failed to map Admin settings: %v", err)
  260. } else if err = File.Section("cron").MapTo(&Cron); err != nil {
  261. log.Fatal("Failed to map Cron settings: %v", err)
  262. } else if err = File.Section("git").MapTo(&Git); err != nil {
  263. log.Fatal("Failed to map Git settings: %v", err)
  264. } else if err = File.Section("mirror").MapTo(&Mirror); err != nil {
  265. log.Fatal("Failed to map Mirror settings: %v", err)
  266. } else if err = File.Section("api").MapTo(&API); err != nil {
  267. log.Fatal("Failed to map API settings: %v", err)
  268. } else if err = File.Section("ui").MapTo(&UI); err != nil {
  269. log.Fatal("Failed to map UI settings: %v", err)
  270. } else if err = File.Section("prometheus").MapTo(&Prometheus); err != nil {
  271. log.Fatal("Failed to map Prometheus settings: %v", err)
  272. }
  273. if Mirror.DefaultInterval <= 0 {
  274. Mirror.DefaultInterval = 24
  275. }
  276. Langs = File.Section("i18n").Key("LANGS").Strings(",")
  277. Names = File.Section("i18n").Key("NAMES").Strings(",")
  278. dateLangs = File.Section("i18n.datelang").KeysHash()
  279. ShowFooterBranding = File.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
  280. ShowFooterTemplateLoadTime = File.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool()
  281. HasRobotsTxt = osutil.IsFile(path.Join(CustomDir(), "robots.txt"))
  282. return nil
  283. }
  284. // MustInit panics if configuration initialization failed.
  285. func MustInit(customConf string) {
  286. err := Init(customConf)
  287. if err != nil {
  288. panic(err)
  289. }
  290. }
  291. // TODO
  292. var (
  293. HTTP struct {
  294. AccessControlAllowOrigin string
  295. }
  296. // Database settings
  297. UseSQLite3 bool
  298. UseMySQL bool
  299. UsePostgreSQL bool
  300. UseMSSQL bool
  301. // Webhook settings
  302. Webhook struct {
  303. Types []string
  304. QueueLength int
  305. DeliverTimeout int
  306. SkipTLSVerify bool `ini:"SKIP_TLS_VERIFY"`
  307. PagingNum int
  308. }
  309. // Release settigns
  310. Release struct {
  311. Attachment struct {
  312. Enabled bool
  313. TempPath string
  314. AllowedTypes []string `delim:"|"`
  315. MaxSize int64
  316. MaxFiles int
  317. } `ini:"-"`
  318. }
  319. // Markdown sttings
  320. Markdown struct {
  321. EnableHardLineBreak bool
  322. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  323. FileExtensions []string
  324. }
  325. // Smartypants settings
  326. Smartypants struct {
  327. Enabled bool
  328. Fractions bool
  329. Dashes bool
  330. LatexDashes bool
  331. AngledQuotes bool
  332. }
  333. // Admin settings
  334. Admin struct {
  335. DisableRegularOrgCreation bool
  336. }
  337. // Picture settings
  338. AvatarUploadPath string
  339. RepositoryAvatarUploadPath string
  340. GravatarSource string
  341. DisableGravatar bool
  342. EnableFederatedAvatar bool
  343. LibravatarService *libravatar.Libravatar
  344. // Log settings
  345. LogRootPath string
  346. LogModes []string
  347. LogConfigs []interface{}
  348. // Attachment settings
  349. AttachmentPath string
  350. AttachmentAllowedTypes string
  351. AttachmentMaxSize int64
  352. AttachmentMaxFiles int
  353. AttachmentEnabled bool
  354. // Time settings
  355. TimeFormat string
  356. // Cache settings
  357. CacheAdapter string
  358. CacheInterval int
  359. CacheConn string
  360. // Session settings
  361. SessionConfig session.Options
  362. CSRFCookieName string
  363. // Cron tasks
  364. Cron struct {
  365. UpdateMirror struct {
  366. Enabled bool
  367. RunAtStart bool
  368. Schedule string
  369. } `ini:"cron.update_mirrors"`
  370. RepoHealthCheck struct {
  371. Enabled bool
  372. RunAtStart bool
  373. Schedule string
  374. Timeout time.Duration
  375. Args []string `delim:" "`
  376. } `ini:"cron.repo_health_check"`
  377. CheckRepoStats struct {
  378. Enabled bool
  379. RunAtStart bool
  380. Schedule string
  381. } `ini:"cron.check_repo_stats"`
  382. RepoArchiveCleanup struct {
  383. Enabled bool
  384. RunAtStart bool
  385. Schedule string
  386. OlderThan time.Duration
  387. } `ini:"cron.repo_archive_cleanup"`
  388. }
  389. // Git settings
  390. Git struct {
  391. Version string `ini:"-"`
  392. DisableDiffHighlight bool
  393. MaxGitDiffLines int
  394. MaxGitDiffLineCharacters int
  395. MaxGitDiffFiles int
  396. GCArgs []string `ini:"GC_ARGS" delim:" "`
  397. Timeout struct {
  398. Migrate int
  399. Mirror int
  400. Clone int
  401. Pull int
  402. GC int `ini:"GC"`
  403. } `ini:"git.timeout"`
  404. }
  405. // Mirror settings
  406. Mirror struct {
  407. DefaultInterval int
  408. }
  409. // API settings
  410. API struct {
  411. MaxResponseItems int
  412. }
  413. // UI settings
  414. UI struct {
  415. ExplorePagingNum int
  416. IssuePagingNum int
  417. FeedMaxCommitNum int
  418. ThemeColorMetaTag string
  419. MaxDisplayFileSize int64
  420. Admin struct {
  421. UserPagingNum int
  422. RepoPagingNum int
  423. NoticePagingNum int
  424. OrgPagingNum int
  425. } `ini:"ui.admin"`
  426. User struct {
  427. RepoPagingNum int
  428. NewsFeedPagingNum int
  429. CommitsPagingNum int
  430. } `ini:"ui.user"`
  431. }
  432. // Prometheus settings
  433. Prometheus struct {
  434. Enabled bool
  435. EnableBasicAuth bool
  436. BasicAuthUsername string
  437. BasicAuthPassword string
  438. }
  439. // I18n settings
  440. Langs []string
  441. Names []string
  442. dateLangs map[string]string
  443. // Highlight settings are loaded in modules/template/hightlight.go
  444. // Other settings
  445. ShowFooterBranding bool
  446. ShowFooterTemplateLoadTime bool
  447. // Global setting objects
  448. HasRobotsTxt bool
  449. )
  450. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  451. func DateLang(lang string) string {
  452. name, ok := dateLangs[lang]
  453. if ok {
  454. return name
  455. }
  456. return "en"
  457. }
  458. // InitLogging initializes the logging service of the application.
  459. func InitLogging() {
  460. LogRootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
  461. // Because we always create a console logger as the primary logger at init time,
  462. // we need to remove it in case the user doesn't configure to use it after the
  463. // logging service is initalized.
  464. hasConsole := false
  465. // Iterate over [log.*] sections to initialize individual logger.
  466. LogModes = strings.Split(File.Section("log").Key("MODE").MustString("console"), ",")
  467. LogConfigs = make([]interface{}, len(LogModes))
  468. levelMappings := map[string]log.Level{
  469. "trace": log.LevelTrace,
  470. "info": log.LevelInfo,
  471. "warn": log.LevelWarn,
  472. "error": log.LevelError,
  473. "fatal": log.LevelFatal,
  474. }
  475. type config struct {
  476. Buffer int64
  477. Config interface{}
  478. }
  479. for i, mode := range LogModes {
  480. mode = strings.ToLower(strings.TrimSpace(mode))
  481. secName := "log." + mode
  482. sec, err := File.GetSection(secName)
  483. if err != nil {
  484. log.Fatal("Missing configuration section [%s] for %q logger", secName, mode)
  485. return
  486. }
  487. level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
  488. buffer := sec.Key("BUFFER_LEN").MustInt64(100)
  489. c := new(config)
  490. switch mode {
  491. case log.DefaultConsoleName:
  492. hasConsole = true
  493. c = &config{
  494. Buffer: buffer,
  495. Config: log.ConsoleConfig{
  496. Level: level,
  497. },
  498. }
  499. err = log.NewConsole(c.Buffer, c.Config)
  500. case log.DefaultFileName:
  501. logPath := filepath.Join(LogRootPath, "gogs.log")
  502. logDir := filepath.Dir(logPath)
  503. err = os.MkdirAll(logDir, os.ModePerm)
  504. if err != nil {
  505. log.Fatal("Failed to create log directory %q: %v", logDir, err)
  506. return
  507. }
  508. c = &config{
  509. Buffer: buffer,
  510. Config: log.FileConfig{
  511. Level: level,
  512. Filename: logPath,
  513. FileRotationConfig: log.FileRotationConfig{
  514. Rotate: sec.Key("LOG_ROTATE").MustBool(true),
  515. Daily: sec.Key("DAILY_ROTATE").MustBool(true),
  516. MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  517. MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
  518. MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
  519. },
  520. },
  521. }
  522. err = log.NewFile(c.Buffer, c.Config)
  523. case log.DefaultSlackName:
  524. c = &config{
  525. Buffer: buffer,
  526. Config: log.SlackConfig{
  527. Level: level,
  528. URL: sec.Key("URL").String(),
  529. },
  530. }
  531. err = log.NewSlack(c.Buffer, c.Config)
  532. case log.DefaultDiscordName:
  533. c = &config{
  534. Buffer: buffer,
  535. Config: log.DiscordConfig{
  536. Level: level,
  537. URL: sec.Key("URL").String(),
  538. Username: sec.Key("USERNAME").String(),
  539. },
  540. }
  541. default:
  542. continue
  543. }
  544. if err != nil {
  545. log.Fatal("Failed to init %s logger: %v", mode, err)
  546. return
  547. }
  548. LogConfigs[i] = c
  549. log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
  550. }
  551. if !hasConsole {
  552. log.Remove(log.DefaultConsoleName)
  553. }
  554. }
  555. var Service struct {
  556. ActiveCodeLives int
  557. ResetPwdCodeLives int
  558. RegisterEmailConfirm bool
  559. DisableRegistration bool
  560. ShowRegistrationButton bool
  561. RequireSignInView bool
  562. EnableNotifyMail bool
  563. EnableReverseProxyAuth bool
  564. EnableReverseProxyAutoRegister bool
  565. EnableCaptcha bool
  566. }
  567. func newService() {
  568. sec := File.Section("service")
  569. Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
  570. Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
  571. Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
  572. Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
  573. Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
  574. Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
  575. Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
  576. Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
  577. }
  578. func newCacheService() {
  579. CacheAdapter = File.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
  580. switch CacheAdapter {
  581. case "memory":
  582. CacheInterval = File.Section("cache").Key("INTERVAL").MustInt(60)
  583. case "redis", "memcache":
  584. CacheConn = strings.Trim(File.Section("cache").Key("HOST").String(), "\" ")
  585. default:
  586. log.Fatal("Unrecognized cache adapter %q", CacheAdapter)
  587. return
  588. }
  589. log.Trace("Cache service is enabled")
  590. }
  591. func newSessionService() {
  592. SessionConfig.Provider = File.Section("session").Key("PROVIDER").In("memory",
  593. []string{"memory", "file", "redis", "mysql"})
  594. SessionConfig.ProviderConfig = strings.Trim(File.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
  595. SessionConfig.CookieName = File.Section("session").Key("COOKIE_NAME").MustString("i_like_gogs")
  596. SessionConfig.CookiePath = Server.Subpath
  597. SessionConfig.Secure = File.Section("session").Key("COOKIE_SECURE").MustBool()
  598. SessionConfig.Gclifetime = File.Section("session").Key("GC_INTERVAL_TIME").MustInt64(3600)
  599. SessionConfig.Maxlifetime = File.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
  600. CSRFCookieName = File.Section("session").Key("CSRF_COOKIE_NAME").MustString("_csrf")
  601. log.Trace("Session service is enabled")
  602. }
  603. // Mailer represents mail service.
  604. type Mailer struct {
  605. QueueLength int
  606. SubjectPrefix string
  607. Host string
  608. From string
  609. FromEmail string
  610. User, Passwd string
  611. DisableHelo bool
  612. HeloHostname string
  613. SkipVerify bool
  614. UseCertificate bool
  615. CertFile, KeyFile string
  616. UsePlainText bool
  617. AddPlainTextAlt bool
  618. }
  619. var (
  620. MailService *Mailer
  621. )
  622. // newMailService initializes mail service options from configuration.
  623. // No non-error log will be printed in hook mode.
  624. func newMailService() {
  625. sec := File.Section("mailer")
  626. if !sec.Key("ENABLED").MustBool() {
  627. return
  628. }
  629. MailService = &Mailer{
  630. QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
  631. SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString("[" + App.BrandName + "] "),
  632. Host: sec.Key("HOST").String(),
  633. User: sec.Key("USER").String(),
  634. Passwd: sec.Key("PASSWD").String(),
  635. DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
  636. HeloHostname: sec.Key("HELO_HOSTNAME").String(),
  637. SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
  638. UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
  639. CertFile: sec.Key("CERT_FILE").String(),
  640. KeyFile: sec.Key("KEY_FILE").String(),
  641. UsePlainText: sec.Key("USE_PLAIN_TEXT").MustBool(),
  642. AddPlainTextAlt: sec.Key("ADD_PLAIN_TEXT_ALT").MustBool(),
  643. }
  644. MailService.From = sec.Key("FROM").MustString(MailService.User)
  645. if len(MailService.From) > 0 {
  646. parsed, err := mail.ParseAddress(MailService.From)
  647. if err != nil {
  648. log.Fatal("Failed to parse value %q for '[mailer] FROM': %v", MailService.From, err)
  649. return
  650. }
  651. MailService.FromEmail = parsed.Address
  652. }
  653. if HookMode {
  654. return
  655. }
  656. log.Trace("Mail service is enabled")
  657. }
  658. func newRegisterMailService() {
  659. if !File.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  660. return
  661. } else if MailService == nil {
  662. log.Warn("Email confirmation is not enabled due to the mail service is not available")
  663. return
  664. }
  665. Service.RegisterEmailConfirm = true
  666. log.Trace("Email confirmation is enabled")
  667. }
  668. // newNotifyMailService initializes notification email service options from configuration.
  669. // No non-error log will be printed in hook mode.
  670. func newNotifyMailService() {
  671. if !File.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  672. return
  673. } else if MailService == nil {
  674. log.Warn("Email notification is not enabled due to the mail service is not available")
  675. return
  676. }
  677. Service.EnableNotifyMail = true
  678. if HookMode {
  679. return
  680. }
  681. log.Trace("Email notification is enabled")
  682. }
  683. func NewService() {
  684. newService()
  685. }
  686. func NewServices() {
  687. newService()
  688. newCacheService()
  689. newSessionService()
  690. newMailService()
  691. newRegisterMailService()
  692. newNotifyMailService()
  693. }
  694. // HookMode indicates whether program starts as Git server-side hook callback.
  695. var HookMode bool
  696. // NewPostReceiveHookServices initializes all services that are needed by
  697. // Git server-side post-receive hook callback.
  698. func NewPostReceiveHookServices() {
  699. HookMode = true
  700. newService()
  701. newMailService()
  702. newNotifyMailService()
  703. }