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

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