You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting.go 28KB

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