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 36KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package setting
  6. import (
  7. "encoding/base64"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "math"
  12. "net"
  13. "net/url"
  14. "os"
  15. "os/exec"
  16. "path"
  17. "path/filepath"
  18. "runtime"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "code.gitea.io/gitea/modules/generate"
  23. "code.gitea.io/gitea/modules/git"
  24. "code.gitea.io/gitea/modules/log"
  25. "code.gitea.io/gitea/modules/user"
  26. shellquote "github.com/kballard/go-shellquote"
  27. version "github.com/mcuadros/go-version"
  28. "github.com/unknwon/com"
  29. ini "gopkg.in/ini.v1"
  30. "strk.kbt.io/projects/go/libravatar"
  31. )
  32. // Scheme describes protocol types
  33. type Scheme string
  34. // enumerates all the scheme types
  35. const (
  36. HTTP Scheme = "http"
  37. HTTPS Scheme = "https"
  38. FCGI Scheme = "fcgi"
  39. FCGIUnix Scheme = "fcgi+unix"
  40. UnixSocket Scheme = "unix"
  41. )
  42. // LandingPage describes the default page
  43. type LandingPage string
  44. // enumerates all the landing page types
  45. const (
  46. LandingPageHome LandingPage = "/"
  47. LandingPageExplore LandingPage = "/explore"
  48. LandingPageOrganizations LandingPage = "/explore/organizations"
  49. LandingPageLogin LandingPage = "/user/login"
  50. )
  51. // enumerates all the types of captchas
  52. const (
  53. ImageCaptcha = "image"
  54. ReCaptcha = "recaptcha"
  55. )
  56. // settings
  57. var (
  58. // AppVer settings
  59. AppVer string
  60. AppBuiltWith string
  61. AppName string
  62. AppURL string
  63. AppSubURL string
  64. AppSubURLDepth int // Number of slashes
  65. AppPath string
  66. AppDataPath string
  67. AppWorkPath string
  68. // Server settings
  69. Protocol Scheme
  70. Domain string
  71. HTTPAddr string
  72. HTTPPort string
  73. LocalURL string
  74. RedirectOtherPort bool
  75. PortToRedirect string
  76. OfflineMode bool
  77. CertFile string
  78. KeyFile string
  79. StaticRootPath string
  80. StaticCacheTime time.Duration
  81. EnableGzip bool
  82. LandingPageURL LandingPage
  83. UnixSocketPermission uint32
  84. EnablePprof bool
  85. PprofDataPath string
  86. EnableLetsEncrypt bool
  87. LetsEncryptTOS bool
  88. LetsEncryptDirectory string
  89. LetsEncryptEmail string
  90. GracefulRestartable bool
  91. GracefulHammerTime time.Duration
  92. StartupTimeout time.Duration
  93. StaticURLPrefix string
  94. SSH = struct {
  95. Disabled bool `ini:"DISABLE_SSH"`
  96. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  97. BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
  98. Domain string `ini:"SSH_DOMAIN"`
  99. Port int `ini:"SSH_PORT"`
  100. ListenHost string `ini:"SSH_LISTEN_HOST"`
  101. ListenPort int `ini:"SSH_LISTEN_PORT"`
  102. RootPath string `ini:"SSH_ROOT_PATH"`
  103. ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
  104. ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
  105. ServerMACs []string `ini:"SSH_SERVER_MACS"`
  106. KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
  107. KeygenPath string `ini:"SSH_KEYGEN_PATH"`
  108. AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
  109. MinimumKeySizeCheck bool `ini:"-"`
  110. MinimumKeySizes map[string]int `ini:"-"`
  111. CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
  112. ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
  113. }{
  114. Disabled: false,
  115. StartBuiltinServer: false,
  116. Domain: "",
  117. Port: 22,
  118. ServerCiphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"},
  119. ServerKeyExchanges: []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"},
  120. ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"},
  121. KeygenPath: "ssh-keygen",
  122. MinimumKeySizes: map[string]int{"ed25519": 256, "ecdsa": 256, "rsa": 2048, "dsa": 1024},
  123. }
  124. LFS struct {
  125. StartServer bool `ini:"LFS_START_SERVER"`
  126. ContentPath string `ini:"LFS_CONTENT_PATH"`
  127. JWTSecretBase64 string `ini:"LFS_JWT_SECRET"`
  128. JWTSecretBytes []byte `ini:"-"`
  129. HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
  130. MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
  131. LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
  132. }
  133. // Security settings
  134. InstallLock bool
  135. SecretKey string
  136. LogInRememberDays int
  137. CookieUserName string
  138. CookieRememberName string
  139. ReverseProxyAuthUser string
  140. ReverseProxyAuthEmail string
  141. MinPasswordLength int
  142. ImportLocalPaths bool
  143. DisableGitHooks bool
  144. OnlyAllowPushIfGiteaEnvironmentSet bool
  145. PasswordComplexity []string
  146. PasswordHashAlgo string
  147. // UI settings
  148. UI = struct {
  149. ExplorePagingNum int
  150. IssuePagingNum int
  151. RepoSearchPagingNum int
  152. MembersPagingNum int
  153. FeedMaxCommitNum int
  154. GraphMaxCommitNum int
  155. CodeCommentLines int
  156. ReactionMaxUserNum int
  157. ThemeColorMetaTag string
  158. MaxDisplayFileSize int64
  159. ShowUserEmail bool
  160. DefaultShowFullName bool
  161. DefaultTheme string
  162. Themes []string
  163. Reactions []string
  164. ReactionsMap map[string]bool
  165. SearchRepoDescription bool
  166. UseServiceWorker bool
  167. Notification struct {
  168. MinTimeout time.Duration
  169. TimeoutStep time.Duration
  170. MaxTimeout time.Duration
  171. EventSourceUpdateTime time.Duration
  172. } `ini:"ui.notification"`
  173. Admin struct {
  174. UserPagingNum int
  175. RepoPagingNum int
  176. NoticePagingNum int
  177. OrgPagingNum int
  178. } `ini:"ui.admin"`
  179. User struct {
  180. RepoPagingNum int
  181. } `ini:"ui.user"`
  182. Meta struct {
  183. Author string
  184. Description string
  185. Keywords string
  186. } `ini:"ui.meta"`
  187. }{
  188. ExplorePagingNum: 20,
  189. IssuePagingNum: 10,
  190. RepoSearchPagingNum: 10,
  191. MembersPagingNum: 20,
  192. FeedMaxCommitNum: 5,
  193. GraphMaxCommitNum: 100,
  194. CodeCommentLines: 4,
  195. ReactionMaxUserNum: 10,
  196. ThemeColorMetaTag: `#6cc644`,
  197. MaxDisplayFileSize: 8388608,
  198. DefaultTheme: `gitea`,
  199. Themes: []string{`gitea`, `arc-green`},
  200. Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
  201. Notification: struct {
  202. MinTimeout time.Duration
  203. TimeoutStep time.Duration
  204. MaxTimeout time.Duration
  205. EventSourceUpdateTime time.Duration
  206. }{
  207. MinTimeout: 10 * time.Second,
  208. TimeoutStep: 10 * time.Second,
  209. MaxTimeout: 60 * time.Second,
  210. EventSourceUpdateTime: 10 * time.Second,
  211. },
  212. Admin: struct {
  213. UserPagingNum int
  214. RepoPagingNum int
  215. NoticePagingNum int
  216. OrgPagingNum int
  217. }{
  218. UserPagingNum: 50,
  219. RepoPagingNum: 50,
  220. NoticePagingNum: 25,
  221. OrgPagingNum: 50,
  222. },
  223. User: struct {
  224. RepoPagingNum int
  225. }{
  226. RepoPagingNum: 15,
  227. },
  228. Meta: struct {
  229. Author string
  230. Description string
  231. Keywords string
  232. }{
  233. Author: "Gitea - Git with a cup of tea",
  234. Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go",
  235. Keywords: "go,git,self-hosted,gitea",
  236. },
  237. }
  238. // Markdown settings
  239. Markdown = struct {
  240. EnableHardLineBreakInComments bool
  241. EnableHardLineBreakInDocuments bool
  242. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  243. FileExtensions []string
  244. }{
  245. EnableHardLineBreakInComments: true,
  246. EnableHardLineBreakInDocuments: false,
  247. FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
  248. }
  249. // Admin settings
  250. Admin struct {
  251. DisableRegularOrgCreation bool
  252. DefaultEmailNotification string
  253. }
  254. // Picture settings
  255. AvatarUploadPath string
  256. AvatarMaxWidth int
  257. AvatarMaxHeight int
  258. GravatarSource string
  259. GravatarSourceURL *url.URL
  260. DisableGravatar bool
  261. EnableFederatedAvatar bool
  262. LibravatarService *libravatar.Libravatar
  263. AvatarMaxFileSize int64
  264. RepositoryAvatarUploadPath string
  265. RepositoryAvatarFallback string
  266. RepositoryAvatarFallbackImage string
  267. // Log settings
  268. LogLevel string
  269. StacktraceLogLevel string
  270. LogRootPath string
  271. RedirectMacaronLog bool
  272. DisableRouterLog bool
  273. RouterLogLevel log.Level
  274. RouterLogMode string
  275. EnableAccessLog bool
  276. AccessLogTemplate string
  277. EnableXORMLog bool
  278. // Attachment settings
  279. AttachmentPath string
  280. AttachmentAllowedTypes string
  281. AttachmentMaxSize int64
  282. AttachmentMaxFiles int
  283. AttachmentEnabled bool
  284. // Time settings
  285. TimeFormat string
  286. // UILocation is the location on the UI, so that we can display the time on UI.
  287. DefaultUILocation = time.Local
  288. CSRFCookieName = "_csrf"
  289. CSRFCookieHTTPOnly = true
  290. // Mirror settings
  291. Mirror struct {
  292. DefaultInterval time.Duration
  293. MinInterval time.Duration
  294. }
  295. // API settings
  296. API = struct {
  297. EnableSwagger bool
  298. SwaggerURL string
  299. MaxResponseItems int
  300. DefaultPagingNum int
  301. DefaultGitTreesPerPage int
  302. DefaultMaxBlobSize int64
  303. }{
  304. EnableSwagger: true,
  305. SwaggerURL: "",
  306. MaxResponseItems: 50,
  307. DefaultPagingNum: 30,
  308. DefaultGitTreesPerPage: 1000,
  309. DefaultMaxBlobSize: 10485760,
  310. }
  311. OAuth2 = struct {
  312. Enable bool
  313. AccessTokenExpirationTime int64
  314. RefreshTokenExpirationTime int64
  315. InvalidateRefreshTokens bool
  316. JWTSecretBytes []byte `ini:"-"`
  317. JWTSecretBase64 string `ini:"JWT_SECRET"`
  318. MaxTokenLength int
  319. }{
  320. Enable: true,
  321. AccessTokenExpirationTime: 3600,
  322. RefreshTokenExpirationTime: 730,
  323. InvalidateRefreshTokens: false,
  324. MaxTokenLength: math.MaxInt16,
  325. }
  326. U2F = struct {
  327. AppID string
  328. TrustedFacets []string
  329. }{}
  330. // Metrics settings
  331. Metrics = struct {
  332. Enabled bool
  333. Token string
  334. }{
  335. Enabled: false,
  336. Token: "",
  337. }
  338. // I18n settings
  339. Langs []string
  340. Names []string
  341. // Highlight settings are loaded in modules/template/highlight.go
  342. // Other settings
  343. ShowFooterBranding bool
  344. ShowFooterVersion bool
  345. ShowFooterTemplateLoadTime bool
  346. // Global setting objects
  347. Cfg *ini.File
  348. CustomPath string // Custom directory path
  349. CustomConf string
  350. CustomPID string
  351. ProdMode bool
  352. RunUser string
  353. IsWindows bool
  354. HasRobotsTxt bool
  355. InternalToken string // internal access token
  356. // UILocation is the location on the UI, so that we can display the time on UI.
  357. // Currently only show the default time.Local, it could be added to app.ini after UI is ready
  358. UILocation = time.Local
  359. )
  360. func getAppPath() (string, error) {
  361. var appPath string
  362. var err error
  363. if IsWindows && filepath.IsAbs(os.Args[0]) {
  364. appPath = filepath.Clean(os.Args[0])
  365. } else {
  366. appPath, err = exec.LookPath(os.Args[0])
  367. }
  368. if err != nil {
  369. return "", err
  370. }
  371. appPath, err = filepath.Abs(appPath)
  372. if err != nil {
  373. return "", err
  374. }
  375. // Note: we don't use path.Dir here because it does not handle case
  376. // which path starts with two "/" in Windows: "//psf/Home/..."
  377. return strings.Replace(appPath, "\\", "/", -1), err
  378. }
  379. func getWorkPath(appPath string) string {
  380. workPath := AppWorkPath
  381. if giteaWorkPath, ok := os.LookupEnv("GITEA_WORK_DIR"); ok {
  382. workPath = giteaWorkPath
  383. }
  384. if len(workPath) == 0 {
  385. i := strings.LastIndex(appPath, "/")
  386. if i == -1 {
  387. workPath = appPath
  388. } else {
  389. workPath = appPath[:i]
  390. }
  391. }
  392. return strings.Replace(workPath, "\\", "/", -1)
  393. }
  394. func init() {
  395. IsWindows = runtime.GOOS == "windows"
  396. // We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
  397. log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
  398. var err error
  399. if AppPath, err = getAppPath(); err != nil {
  400. log.Fatal("Failed to get app path: %v", err)
  401. }
  402. AppWorkPath = getWorkPath(AppPath)
  403. }
  404. func forcePathSeparator(path string) {
  405. if strings.Contains(path, "\\") {
  406. log.Fatal("Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
  407. }
  408. }
  409. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  410. // actual user that runs the app. The first return value is the actual user name.
  411. // This check is ignored under Windows since SSH remote login is not the main
  412. // method to login on Windows.
  413. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  414. if IsWindows || SSH.StartBuiltinServer {
  415. return "", true
  416. }
  417. currentUser := user.CurrentUsername()
  418. return currentUser, runUser == currentUser
  419. }
  420. func createPIDFile(pidPath string) {
  421. currentPid := os.Getpid()
  422. if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil {
  423. log.Fatal("Failed to create PID folder: %v", err)
  424. }
  425. file, err := os.Create(pidPath)
  426. if err != nil {
  427. log.Fatal("Failed to create PID file: %v", err)
  428. }
  429. defer file.Close()
  430. if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil {
  431. log.Fatal("Failed to write PID information: %v", err)
  432. }
  433. }
  434. // CheckLFSVersion will check lfs version, if not satisfied, then disable it.
  435. func CheckLFSVersion() {
  436. if LFS.StartServer {
  437. //Disable LFS client hooks if installed for the current OS user
  438. //Needs at least git v2.1.2
  439. binVersion, err := git.BinVersion()
  440. if err != nil {
  441. log.Fatal("Error retrieving git version: %v", err)
  442. }
  443. if !version.Compare(binVersion, "2.1.2", ">=") {
  444. LFS.StartServer = false
  445. log.Error("LFS server support needs at least Git v2.1.2")
  446. } else {
  447. git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=",
  448. "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
  449. }
  450. }
  451. }
  452. // SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
  453. // GITEA_CUSTOM environment variable and with provided overrides before stepping
  454. // back to the default
  455. func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string) {
  456. if len(providedWorkPath) != 0 {
  457. AppWorkPath = filepath.ToSlash(providedWorkPath)
  458. }
  459. if giteaCustom, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
  460. CustomPath = giteaCustom
  461. }
  462. if len(providedCustom) != 0 {
  463. CustomPath = providedCustom
  464. }
  465. if len(CustomPath) == 0 {
  466. CustomPath = path.Join(AppWorkPath, "custom")
  467. } else if !filepath.IsAbs(CustomPath) {
  468. CustomPath = path.Join(AppWorkPath, CustomPath)
  469. }
  470. if len(providedConf) != 0 {
  471. CustomConf = providedConf
  472. }
  473. if len(CustomConf) == 0 {
  474. CustomConf = path.Join(CustomPath, "conf/app.ini")
  475. } else if !filepath.IsAbs(CustomConf) {
  476. CustomConf = path.Join(CustomPath, CustomConf)
  477. log.Warn("Using 'custom' directory as relative origin for configuration file: '%s'", CustomConf)
  478. }
  479. }
  480. // NewContext initializes configuration context.
  481. // NOTE: do not print any log except error.
  482. func NewContext() {
  483. Cfg = ini.Empty()
  484. if len(CustomPID) > 0 {
  485. createPIDFile(CustomPID)
  486. }
  487. if com.IsFile(CustomConf) {
  488. if err := Cfg.Append(CustomConf); err != nil {
  489. log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err)
  490. }
  491. } else {
  492. log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
  493. }
  494. Cfg.NameMapper = ini.SnackCase
  495. homeDir, err := com.HomeDir()
  496. if err != nil {
  497. log.Fatal("Failed to get home directory: %v", err)
  498. }
  499. homeDir = strings.Replace(homeDir, "\\", "/", -1)
  500. LogLevel = getLogLevel(Cfg.Section("log"), "LEVEL", "Info")
  501. StacktraceLogLevel = getStacktraceLogLevel(Cfg.Section("log"), "STACKTRACE_LEVEL", "None")
  502. LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
  503. forcePathSeparator(LogRootPath)
  504. RedirectMacaronLog = Cfg.Section("log").Key("REDIRECT_MACARON_LOG").MustBool(false)
  505. RouterLogLevel = log.FromString(Cfg.Section("log").Key("ROUTER_LOG_LEVEL").MustString("Info"))
  506. sec := Cfg.Section("server")
  507. AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
  508. Protocol = HTTP
  509. switch sec.Key("PROTOCOL").String() {
  510. case "https":
  511. Protocol = HTTPS
  512. CertFile = sec.Key("CERT_FILE").String()
  513. KeyFile = sec.Key("KEY_FILE").String()
  514. if !filepath.IsAbs(CertFile) && len(CertFile) > 0 {
  515. CertFile = filepath.Join(CustomPath, CertFile)
  516. }
  517. if !filepath.IsAbs(KeyFile) && len(KeyFile) > 0 {
  518. KeyFile = filepath.Join(CustomPath, KeyFile)
  519. }
  520. case "fcgi":
  521. Protocol = FCGI
  522. case "fcgi+unix":
  523. Protocol = FCGIUnix
  524. UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
  525. UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
  526. if err != nil || UnixSocketPermissionParsed > 0777 {
  527. log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
  528. }
  529. UnixSocketPermission = uint32(UnixSocketPermissionParsed)
  530. case "unix":
  531. Protocol = UnixSocket
  532. UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
  533. UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
  534. if err != nil || UnixSocketPermissionParsed > 0777 {
  535. log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
  536. }
  537. UnixSocketPermission = uint32(UnixSocketPermissionParsed)
  538. }
  539. EnableLetsEncrypt = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
  540. LetsEncryptTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false)
  541. if !LetsEncryptTOS && EnableLetsEncrypt {
  542. log.Warn("Failed to enable Let's Encrypt due to Let's Encrypt TOS not being accepted")
  543. EnableLetsEncrypt = false
  544. }
  545. LetsEncryptDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https")
  546. LetsEncryptEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
  547. Domain = sec.Key("DOMAIN").MustString("localhost")
  548. HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
  549. HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
  550. GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
  551. GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
  552. StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
  553. defaultAppURL := string(Protocol) + "://" + Domain
  554. if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
  555. defaultAppURL += ":" + HTTPPort
  556. }
  557. AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
  558. AppURL = strings.TrimSuffix(AppURL, "/") + "/"
  559. // Check if has app suburl.
  560. appURL, err := url.Parse(AppURL)
  561. if err != nil {
  562. log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
  563. }
  564. // Suburl should start with '/' and end without '/', such as '/{subpath}'.
  565. // This value is empty if site does not have sub-url.
  566. AppSubURL = strings.TrimSuffix(appURL.Path, "/")
  567. StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
  568. AppSubURLDepth = strings.Count(AppSubURL, "/")
  569. // Check if Domain differs from AppURL domain than update it to AppURL's domain
  570. // TODO: Can be replaced with url.Hostname() when minimal GoLang version is 1.8
  571. urlHostname := strings.SplitN(appURL.Host, ":", 2)[0]
  572. if urlHostname != Domain && net.ParseIP(urlHostname) == nil {
  573. Domain = urlHostname
  574. }
  575. var defaultLocalURL string
  576. switch Protocol {
  577. case UnixSocket:
  578. defaultLocalURL = "http://unix/"
  579. case FCGI:
  580. defaultLocalURL = AppURL
  581. case FCGIUnix:
  582. defaultLocalURL = AppURL
  583. default:
  584. defaultLocalURL = string(Protocol) + "://"
  585. if HTTPAddr == "0.0.0.0" {
  586. defaultLocalURL += "localhost"
  587. } else {
  588. defaultLocalURL += HTTPAddr
  589. }
  590. defaultLocalURL += ":" + HTTPPort + "/"
  591. }
  592. LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
  593. RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
  594. PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
  595. OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
  596. DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
  597. StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
  598. StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
  599. AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
  600. EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
  601. EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
  602. PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
  603. if !filepath.IsAbs(PprofDataPath) {
  604. PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
  605. }
  606. switch sec.Key("LANDING_PAGE").MustString("home") {
  607. case "explore":
  608. LandingPageURL = LandingPageExplore
  609. case "organizations":
  610. LandingPageURL = LandingPageOrganizations
  611. case "login":
  612. LandingPageURL = LandingPageLogin
  613. default:
  614. LandingPageURL = LandingPageHome
  615. }
  616. if len(SSH.Domain) == 0 {
  617. SSH.Domain = Domain
  618. }
  619. SSH.RootPath = path.Join(homeDir, ".ssh")
  620. serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
  621. if len(serverCiphers) > 0 {
  622. SSH.ServerCiphers = serverCiphers
  623. }
  624. serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
  625. if len(serverKeyExchanges) > 0 {
  626. SSH.ServerKeyExchanges = serverKeyExchanges
  627. }
  628. serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
  629. if len(serverMACs) > 0 {
  630. SSH.ServerMACs = serverMACs
  631. }
  632. SSH.KeyTestPath = os.TempDir()
  633. if err = Cfg.Section("server").MapTo(&SSH); err != nil {
  634. log.Fatal("Failed to map SSH settings: %v", err)
  635. }
  636. SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
  637. SSH.Port = sec.Key("SSH_PORT").MustInt(22)
  638. SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
  639. // When disable SSH, start builtin server value is ignored.
  640. if SSH.Disabled {
  641. SSH.StartBuiltinServer = false
  642. }
  643. if !SSH.Disabled && !SSH.StartBuiltinServer {
  644. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  645. log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
  646. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  647. log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
  648. }
  649. }
  650. SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool()
  651. minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
  652. for _, key := range minimumKeySizes {
  653. if key.MustInt() != -1 {
  654. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  655. } else {
  656. delete(SSH.MinimumKeySizes, strings.ToLower(key.Name()))
  657. }
  658. }
  659. SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
  660. SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
  661. SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
  662. sec = Cfg.Section("server")
  663. if err = sec.MapTo(&LFS); err != nil {
  664. log.Fatal("Failed to map LFS settings: %v", err)
  665. }
  666. LFS.ContentPath = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs"))
  667. if !filepath.IsAbs(LFS.ContentPath) {
  668. LFS.ContentPath = filepath.Join(AppWorkPath, LFS.ContentPath)
  669. }
  670. if LFS.LocksPagingNum == 0 {
  671. LFS.LocksPagingNum = 50
  672. }
  673. LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(20 * time.Minute)
  674. if LFS.StartServer {
  675. LFS.JWTSecretBytes = make([]byte, 32)
  676. n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
  677. if err != nil || n != 32 {
  678. LFS.JWTSecretBase64, err = generate.NewJwtSecret()
  679. if err != nil {
  680. log.Fatal("Error generating JWT Secret for custom config: %v", err)
  681. return
  682. }
  683. // Save secret
  684. cfg := ini.Empty()
  685. if com.IsFile(CustomConf) {
  686. // Keeps custom settings if there is already something.
  687. if err := cfg.Append(CustomConf); err != nil {
  688. log.Error("Failed to load custom conf '%s': %v", CustomConf, err)
  689. }
  690. }
  691. cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
  692. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  693. log.Fatal("Failed to create '%s': %v", CustomConf, err)
  694. }
  695. if err := cfg.SaveTo(CustomConf); err != nil {
  696. log.Fatal("Error saving generated JWT Secret to custom config: %v", err)
  697. return
  698. }
  699. }
  700. }
  701. if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil {
  702. log.Fatal("Failed to OAuth2 settings: %v", err)
  703. return
  704. }
  705. if OAuth2.Enable {
  706. OAuth2.JWTSecretBytes = make([]byte, 32)
  707. n, err := base64.RawURLEncoding.Decode(OAuth2.JWTSecretBytes, []byte(OAuth2.JWTSecretBase64))
  708. if err != nil || n != 32 {
  709. OAuth2.JWTSecretBase64, err = generate.NewJwtSecret()
  710. if err != nil {
  711. log.Fatal("error generating JWT secret: %v", err)
  712. return
  713. }
  714. cfg := ini.Empty()
  715. if com.IsFile(CustomConf) {
  716. if err := cfg.Append(CustomConf); err != nil {
  717. log.Error("failed to load custom conf %s: %v", CustomConf, err)
  718. return
  719. }
  720. }
  721. cfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64)
  722. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  723. log.Fatal("failed to create '%s': %v", CustomConf, err)
  724. return
  725. }
  726. if err := cfg.SaveTo(CustomConf); err != nil {
  727. log.Fatal("error saving generating JWT secret to custom config: %v", err)
  728. return
  729. }
  730. }
  731. }
  732. sec = Cfg.Section("admin")
  733. Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled")
  734. sec = Cfg.Section("security")
  735. InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
  736. SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
  737. LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
  738. CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
  739. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
  740. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  741. ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
  742. MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
  743. ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
  744. DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(false)
  745. OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
  746. PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("pbkdf2")
  747. CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
  748. InternalToken = loadInternalToken(sec)
  749. cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
  750. PasswordComplexity = make([]string, 0, len(cfgdata))
  751. for _, name := range cfgdata {
  752. name := strings.ToLower(strings.Trim(name, `"`))
  753. if name != "" {
  754. PasswordComplexity = append(PasswordComplexity, name)
  755. }
  756. }
  757. sec = Cfg.Section("attachment")
  758. AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
  759. if !filepath.IsAbs(AttachmentPath) {
  760. AttachmentPath = path.Join(AppWorkPath, AttachmentPath)
  761. }
  762. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)
  763. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  764. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  765. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  766. timeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("")
  767. if timeFormatKey != "" {
  768. TimeFormat = map[string]string{
  769. "ANSIC": time.ANSIC,
  770. "UnixDate": time.UnixDate,
  771. "RubyDate": time.RubyDate,
  772. "RFC822": time.RFC822,
  773. "RFC822Z": time.RFC822Z,
  774. "RFC850": time.RFC850,
  775. "RFC1123": time.RFC1123,
  776. "RFC1123Z": time.RFC1123Z,
  777. "RFC3339": time.RFC3339,
  778. "RFC3339Nano": time.RFC3339Nano,
  779. "Kitchen": time.Kitchen,
  780. "Stamp": time.Stamp,
  781. "StampMilli": time.StampMilli,
  782. "StampMicro": time.StampMicro,
  783. "StampNano": time.StampNano,
  784. }[timeFormatKey]
  785. // When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
  786. if len(TimeFormat) == 0 {
  787. TimeFormat = timeFormatKey
  788. TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat)
  789. if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" {
  790. log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat)
  791. log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05")
  792. }
  793. log.Trace("Custom TimeFormat: %s", TimeFormat)
  794. }
  795. }
  796. zone := Cfg.Section("time").Key("DEFAULT_UI_LOCATION").String()
  797. if zone != "" {
  798. DefaultUILocation, err = time.LoadLocation(zone)
  799. if err != nil {
  800. log.Fatal("Load time zone failed: %v", err)
  801. } else {
  802. log.Info("Default UI Location is %v", zone)
  803. }
  804. }
  805. if DefaultUILocation == nil {
  806. DefaultUILocation = time.Local
  807. }
  808. RunUser = Cfg.Section("").Key("RUN_USER").MustString(user.CurrentUsername())
  809. // Does not check run user when the install lock is off.
  810. if InstallLock {
  811. currentUser, match := IsRunUserMatchCurrentUser(RunUser)
  812. if !match {
  813. log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser)
  814. }
  815. }
  816. SSH.BuiltinServerUser = Cfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
  817. newRepository()
  818. sec = Cfg.Section("picture")
  819. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))
  820. forcePathSeparator(AvatarUploadPath)
  821. if !filepath.IsAbs(AvatarUploadPath) {
  822. AvatarUploadPath = path.Join(AppWorkPath, AvatarUploadPath)
  823. }
  824. RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "repo-avatars"))
  825. forcePathSeparator(RepositoryAvatarUploadPath)
  826. if !filepath.IsAbs(RepositoryAvatarUploadPath) {
  827. RepositoryAvatarUploadPath = path.Join(AppWorkPath, RepositoryAvatarUploadPath)
  828. }
  829. RepositoryAvatarFallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
  830. RepositoryAvatarFallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/img/repo_default.png")
  831. AvatarMaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
  832. AvatarMaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
  833. AvatarMaxFileSize = sec.Key("AVATAR_MAX_FILE_SIZE").MustInt64(1048576)
  834. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  835. case "duoshuo":
  836. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  837. case "gravatar":
  838. GravatarSource = "https://secure.gravatar.com/avatar/"
  839. case "libravatar":
  840. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  841. default:
  842. GravatarSource = source
  843. }
  844. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  845. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock)
  846. if OfflineMode {
  847. DisableGravatar = true
  848. EnableFederatedAvatar = false
  849. }
  850. if DisableGravatar {
  851. EnableFederatedAvatar = false
  852. }
  853. if EnableFederatedAvatar || !DisableGravatar {
  854. GravatarSourceURL, err = url.Parse(GravatarSource)
  855. if err != nil {
  856. log.Fatal("Failed to parse Gravatar URL(%s): %v",
  857. GravatarSource, err)
  858. }
  859. }
  860. if EnableFederatedAvatar {
  861. LibravatarService = libravatar.New()
  862. if GravatarSourceURL.Scheme == "https" {
  863. LibravatarService.SetUseHTTPS(true)
  864. LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
  865. } else {
  866. LibravatarService.SetUseHTTPS(false)
  867. LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
  868. }
  869. }
  870. if err = Cfg.Section("ui").MapTo(&UI); err != nil {
  871. log.Fatal("Failed to map UI settings: %v", err)
  872. } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
  873. log.Fatal("Failed to map Markdown settings: %v", err)
  874. } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
  875. log.Fatal("Fail to map Admin settings: %v", err)
  876. } else if err = Cfg.Section("api").MapTo(&API); err != nil {
  877. log.Fatal("Failed to map API settings: %v", err)
  878. } else if err = Cfg.Section("metrics").MapTo(&Metrics); err != nil {
  879. log.Fatal("Failed to map Metrics settings: %v", err)
  880. }
  881. u := *appURL
  882. u.Path = path.Join(u.Path, "api", "swagger")
  883. API.SwaggerURL = u.String()
  884. newGit()
  885. sec = Cfg.Section("mirror")
  886. Mirror.MinInterval = sec.Key("MIN_INTERVAL").MustDuration(10 * time.Minute)
  887. Mirror.DefaultInterval = sec.Key("DEFAULT_INTERVAL").MustDuration(8 * time.Hour)
  888. if Mirror.MinInterval.Minutes() < 1 {
  889. log.Warn("Mirror.MinInterval is too low")
  890. Mirror.MinInterval = 1 * time.Minute
  891. }
  892. if Mirror.DefaultInterval < Mirror.MinInterval {
  893. log.Warn("Mirror.DefaultInterval is less than Mirror.MinInterval")
  894. Mirror.DefaultInterval = time.Hour * 8
  895. }
  896. Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
  897. if len(Langs) == 0 {
  898. Langs = []string{
  899. "en-US", "zh-CN", "zh-HK", "zh-TW", "de-DE", "fr-FR", "nl-NL", "lv-LV",
  900. "ru-RU", "uk-UA", "ja-JP", "es-ES", "pt-BR", "pt-PT", "pl-PL", "bg-BG",
  901. "it-IT", "fi-FI", "tr-TR", "cs-CZ", "sr-SP", "sv-SE", "ko-KR"}
  902. }
  903. Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
  904. if len(Names) == 0 {
  905. Names = []string{"English", "简体中文", "繁體中文(香港)", "繁體中文(台灣)", "Deutsch",
  906. "français", "Nederlands", "latviešu", "русский", "Українська", "日本語",
  907. "español", "português do Brasil", "Português de Portugal", "polski", "български",
  908. "italiano", "suomi", "Türkçe", "čeština", "српски", "svenska", "한국어"}
  909. }
  910. ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
  911. ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true)
  912. ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
  913. UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
  914. UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
  915. UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
  916. UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(true)
  917. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  918. newMarkup()
  919. sec = Cfg.Section("U2F")
  920. U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimRight(AppURL, "/")))
  921. U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/"))
  922. UI.ReactionsMap = make(map[string]bool)
  923. for _, reaction := range UI.Reactions {
  924. UI.ReactionsMap[reaction] = true
  925. }
  926. }
  927. func loadInternalToken(sec *ini.Section) string {
  928. uri := sec.Key("INTERNAL_TOKEN_URI").String()
  929. if len(uri) == 0 {
  930. return loadOrGenerateInternalToken(sec)
  931. }
  932. tempURI, err := url.Parse(uri)
  933. if err != nil {
  934. log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err)
  935. }
  936. switch tempURI.Scheme {
  937. case "file":
  938. fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600)
  939. if err != nil {
  940. log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
  941. }
  942. defer fp.Close()
  943. buf, err := ioutil.ReadAll(fp)
  944. if err != nil {
  945. log.Fatal("Failed to read InternalTokenURI (%s): %v", uri, err)
  946. }
  947. // No token in the file, generate one and store it.
  948. if len(buf) == 0 {
  949. token, err := generate.NewInternalToken()
  950. if err != nil {
  951. log.Fatal("Error generate internal token: %v", err)
  952. }
  953. if _, err := io.WriteString(fp, token); err != nil {
  954. log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
  955. }
  956. return token
  957. }
  958. return strings.TrimSpace(string(buf))
  959. default:
  960. log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
  961. }
  962. return ""
  963. }
  964. func loadOrGenerateInternalToken(sec *ini.Section) string {
  965. var err error
  966. token := sec.Key("INTERNAL_TOKEN").String()
  967. if len(token) == 0 {
  968. token, err = generate.NewInternalToken()
  969. if err != nil {
  970. log.Fatal("Error generate internal token: %v", err)
  971. }
  972. // Save secret
  973. cfgSave := ini.Empty()
  974. if com.IsFile(CustomConf) {
  975. // Keeps custom settings if there is already something.
  976. if err := cfgSave.Append(CustomConf); err != nil {
  977. log.Error("Failed to load custom conf '%s': %v", CustomConf, err)
  978. }
  979. }
  980. cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
  981. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  982. log.Fatal("Failed to create '%s': %v", CustomConf, err)
  983. }
  984. if err := cfgSave.SaveTo(CustomConf); err != nil {
  985. log.Fatal("Error saving generated INTERNAL_TOKEN to custom config: %v", err)
  986. }
  987. }
  988. return token
  989. }
  990. func ensureLFSDirectory() {
  991. if LFS.StartServer {
  992. if err := os.MkdirAll(LFS.ContentPath, 0700); err != nil {
  993. log.Fatal("Failed to create '%s': %v", LFS.ContentPath, err)
  994. }
  995. }
  996. }
  997. // NewServices initializes the services
  998. func NewServices() {
  999. InitDBConfig()
  1000. newService()
  1001. NewLogServices(false)
  1002. ensureLFSDirectory()
  1003. newCacheService()
  1004. newSessionService()
  1005. newCORSService()
  1006. newMailService()
  1007. newRegisterMailService()
  1008. newNotifyMailService()
  1009. newWebhookService()
  1010. newMigrationsService()
  1011. newIndexerService()
  1012. newTaskService()
  1013. NewQueueService()
  1014. }