setting.go 27 KB

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