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

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