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

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