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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575
  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. "net"
  10. "net/mail"
  11. "net/url"
  12. "os"
  13. "os/exec"
  14. "path"
  15. "path/filepath"
  16. "regexp"
  17. "runtime"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "code.gitea.io/git"
  22. "code.gitea.io/gitea/modules/generate"
  23. "code.gitea.io/gitea/modules/log"
  24. _ "code.gitea.io/gitea/modules/minwinsvc" // import minwinsvc for windows services
  25. "code.gitea.io/gitea/modules/user"
  26. "github.com/Unknwon/com"
  27. _ "github.com/go-macaron/cache/memcache" // memcache plugin for cache
  28. _ "github.com/go-macaron/cache/redis"
  29. "github.com/go-macaron/session"
  30. _ "github.com/go-macaron/session/redis" // redis plugin for store session
  31. "github.com/go-xorm/core"
  32. "github.com/kballard/go-shellquote"
  33. "gopkg.in/ini.v1"
  34. "strk.kbt.io/projects/go/libravatar"
  35. )
  36. // Scheme describes protocol types
  37. type Scheme string
  38. // enumerates all the scheme types
  39. const (
  40. HTTP Scheme = "http"
  41. HTTPS Scheme = "https"
  42. FCGI Scheme = "fcgi"
  43. UnixSocket Scheme = "unix"
  44. )
  45. // LandingPage describes the default page
  46. type LandingPage string
  47. // enumerates all the landing page types
  48. const (
  49. LandingPageHome LandingPage = "/"
  50. LandingPageExplore LandingPage = "/explore"
  51. LandingPageOrganizations LandingPage = "/explore/organizations"
  52. )
  53. // MarkupParser defines the external parser configured in ini
  54. type MarkupParser struct {
  55. Enabled bool
  56. MarkupName string
  57. Command string
  58. FileExtensions []string
  59. IsInputFile bool
  60. }
  61. // enumerates all the policy repository creating
  62. const (
  63. RepoCreatingLastUserVisibility = "last"
  64. RepoCreatingPrivate = "private"
  65. RepoCreatingPublic = "public"
  66. )
  67. // enumerates all the types of captchas
  68. const (
  69. ImageCaptcha = "image"
  70. ReCaptcha = "recaptcha"
  71. )
  72. // settings
  73. var (
  74. // AppVer settings
  75. AppVer string
  76. AppBuiltWith string
  77. AppName string
  78. AppURL string
  79. AppSubURL string
  80. AppSubURLDepth int // Number of slashes
  81. AppPath string
  82. AppDataPath string
  83. AppWorkPath string
  84. // Server settings
  85. Protocol Scheme
  86. Domain string
  87. HTTPAddr string
  88. HTTPPort string
  89. LocalURL string
  90. RedirectOtherPort bool
  91. PortToRedirect string
  92. OfflineMode bool
  93. DisableRouterLog bool
  94. CertFile string
  95. KeyFile string
  96. StaticRootPath string
  97. EnableGzip bool
  98. LandingPageURL LandingPage
  99. UnixSocketPermission uint32
  100. EnablePprof bool
  101. SSH = struct {
  102. Disabled bool `ini:"DISABLE_SSH"`
  103. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  104. BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
  105. Domain string `ini:"SSH_DOMAIN"`
  106. Port int `ini:"SSH_PORT"`
  107. ListenHost string `ini:"SSH_LISTEN_HOST"`
  108. ListenPort int `ini:"SSH_LISTEN_PORT"`
  109. RootPath string `ini:"SSH_ROOT_PATH"`
  110. ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
  111. ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
  112. ServerMACs []string `ini:"SSH_SERVER_MACS"`
  113. KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
  114. KeygenPath string `ini:"SSH_KEYGEN_PATH"`
  115. AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
  116. MinimumKeySizeCheck bool `ini:"-"`
  117. MinimumKeySizes map[string]int `ini:"-"`
  118. ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
  119. }{
  120. Disabled: false,
  121. StartBuiltinServer: false,
  122. Domain: "",
  123. Port: 22,
  124. ServerCiphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"},
  125. ServerKeyExchanges: []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"},
  126. ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"},
  127. KeygenPath: "ssh-keygen",
  128. }
  129. LFS struct {
  130. StartServer bool `ini:"LFS_START_SERVER"`
  131. ContentPath string `ini:"LFS_CONTENT_PATH"`
  132. JWTSecretBase64 string `ini:"LFS_JWT_SECRET"`
  133. JWTSecretBytes []byte `ini:"-"`
  134. HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
  135. }
  136. // Security settings
  137. InstallLock bool
  138. SecretKey string
  139. LogInRememberDays int
  140. CookieUserName string
  141. CookieRememberName string
  142. ReverseProxyAuthUser string
  143. MinPasswordLength int
  144. ImportLocalPaths bool
  145. DisableGitHooks bool
  146. // Database settings
  147. UseSQLite3 bool
  148. UseMySQL bool
  149. UseMSSQL bool
  150. UsePostgreSQL bool
  151. UseTiDB bool
  152. LogSQL bool
  153. // Indexer settings
  154. Indexer struct {
  155. IssuePath string
  156. RepoIndexerEnabled bool
  157. RepoPath string
  158. UpdateQueueLength int
  159. MaxIndexerFileSize int64
  160. }
  161. // Webhook settings
  162. Webhook = struct {
  163. QueueLength int
  164. DeliverTimeout int
  165. SkipTLSVerify bool
  166. Types []string
  167. PagingNum int
  168. }{
  169. QueueLength: 1000,
  170. DeliverTimeout: 5,
  171. SkipTLSVerify: false,
  172. PagingNum: 10,
  173. }
  174. // Repository settings
  175. Repository = struct {
  176. AnsiCharset string
  177. ForcePrivate bool
  178. DefaultPrivate string
  179. MaxCreationLimit int
  180. MirrorQueueLength int
  181. PullRequestQueueLength int
  182. PreferredLicenses []string
  183. DisableHTTPGit bool
  184. UseCompatSSHURI bool
  185. // Repository editor settings
  186. Editor struct {
  187. LineWrapExtensions []string
  188. PreviewableFileModes []string
  189. } `ini:"-"`
  190. // Repository upload settings
  191. Upload struct {
  192. Enabled bool
  193. TempPath string
  194. AllowedTypes []string `delim:"|"`
  195. FileMaxSize int64
  196. MaxFiles int
  197. } `ini:"-"`
  198. // Repository local settings
  199. Local struct {
  200. LocalCopyPath string
  201. LocalWikiPath string
  202. } `ini:"-"`
  203. }{
  204. AnsiCharset: "",
  205. ForcePrivate: false,
  206. DefaultPrivate: RepoCreatingLastUserVisibility,
  207. MaxCreationLimit: -1,
  208. MirrorQueueLength: 1000,
  209. PullRequestQueueLength: 1000,
  210. PreferredLicenses: []string{"Apache License 2.0,MIT License"},
  211. DisableHTTPGit: false,
  212. UseCompatSSHURI: false,
  213. // Repository editor settings
  214. Editor: struct {
  215. LineWrapExtensions []string
  216. PreviewableFileModes []string
  217. }{
  218. LineWrapExtensions: strings.Split(".txt,.md,.markdown,.mdown,.mkd,", ","),
  219. PreviewableFileModes: []string{"markdown"},
  220. },
  221. // Repository upload settings
  222. Upload: struct {
  223. Enabled bool
  224. TempPath string
  225. AllowedTypes []string `delim:"|"`
  226. FileMaxSize int64
  227. MaxFiles int
  228. }{
  229. Enabled: true,
  230. TempPath: "data/tmp/uploads",
  231. AllowedTypes: []string{},
  232. FileMaxSize: 3,
  233. MaxFiles: 5,
  234. },
  235. // Repository local settings
  236. Local: struct {
  237. LocalCopyPath string
  238. LocalWikiPath string
  239. }{
  240. LocalCopyPath: "tmp/local-repo",
  241. LocalWikiPath: "tmp/local-wiki",
  242. },
  243. }
  244. RepoRootPath string
  245. ScriptType = "bash"
  246. // UI settings
  247. UI = struct {
  248. ExplorePagingNum int
  249. IssuePagingNum int
  250. RepoSearchPagingNum int
  251. FeedMaxCommitNum int
  252. ReactionMaxUserNum int
  253. ThemeColorMetaTag string
  254. MaxDisplayFileSize int64
  255. ShowUserEmail bool
  256. DefaultTheme string
  257. Admin struct {
  258. UserPagingNum int
  259. RepoPagingNum int
  260. NoticePagingNum int
  261. OrgPagingNum int
  262. } `ini:"ui.admin"`
  263. User struct {
  264. RepoPagingNum int
  265. } `ini:"ui.user"`
  266. Meta struct {
  267. Author string
  268. Description string
  269. Keywords string
  270. } `ini:"ui.meta"`
  271. }{
  272. ExplorePagingNum: 20,
  273. IssuePagingNum: 10,
  274. RepoSearchPagingNum: 10,
  275. FeedMaxCommitNum: 5,
  276. ReactionMaxUserNum: 10,
  277. ThemeColorMetaTag: `#6cc644`,
  278. MaxDisplayFileSize: 8388608,
  279. DefaultTheme: `gitea`,
  280. Admin: struct {
  281. UserPagingNum int
  282. RepoPagingNum int
  283. NoticePagingNum int
  284. OrgPagingNum int
  285. }{
  286. UserPagingNum: 50,
  287. RepoPagingNum: 50,
  288. NoticePagingNum: 25,
  289. OrgPagingNum: 50,
  290. },
  291. User: struct {
  292. RepoPagingNum int
  293. }{
  294. RepoPagingNum: 15,
  295. },
  296. Meta: struct {
  297. Author string
  298. Description string
  299. Keywords string
  300. }{
  301. Author: "Gitea - Git with a cup of tea",
  302. Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go",
  303. Keywords: "go,git,self-hosted,gitea",
  304. },
  305. }
  306. // Markdown settings
  307. Markdown = struct {
  308. EnableHardLineBreak bool
  309. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  310. FileExtensions []string
  311. }{
  312. EnableHardLineBreak: false,
  313. FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
  314. }
  315. // Admin settings
  316. Admin struct {
  317. DisableRegularOrgCreation bool
  318. }
  319. // Picture settings
  320. AvatarUploadPath string
  321. AvatarMaxWidth int
  322. AvatarMaxHeight int
  323. GravatarSource string
  324. GravatarSourceURL *url.URL
  325. DisableGravatar bool
  326. EnableFederatedAvatar bool
  327. LibravatarService *libravatar.Libravatar
  328. // Log settings
  329. LogLevel string
  330. LogRootPath string
  331. LogModes []string
  332. LogConfigs []string
  333. // Attachment settings
  334. AttachmentPath string
  335. AttachmentAllowedTypes string
  336. AttachmentMaxSize int64
  337. AttachmentMaxFiles int
  338. AttachmentEnabled bool
  339. // Time settings
  340. TimeFormat string
  341. // Session settings
  342. SessionConfig session.Options
  343. CSRFCookieName = "_csrf"
  344. // Cron tasks
  345. Cron = struct {
  346. UpdateMirror struct {
  347. Enabled bool
  348. RunAtStart bool
  349. Schedule string
  350. } `ini:"cron.update_mirrors"`
  351. RepoHealthCheck struct {
  352. Enabled bool
  353. RunAtStart bool
  354. Schedule string
  355. Timeout time.Duration
  356. Args []string `delim:" "`
  357. } `ini:"cron.repo_health_check"`
  358. CheckRepoStats struct {
  359. Enabled bool
  360. RunAtStart bool
  361. Schedule string
  362. } `ini:"cron.check_repo_stats"`
  363. ArchiveCleanup struct {
  364. Enabled bool
  365. RunAtStart bool
  366. Schedule string
  367. OlderThan time.Duration
  368. } `ini:"cron.archive_cleanup"`
  369. SyncExternalUsers struct {
  370. Enabled bool
  371. RunAtStart bool
  372. Schedule string
  373. UpdateExisting bool
  374. } `ini:"cron.sync_external_users"`
  375. DeletedBranchesCleanup struct {
  376. Enabled bool
  377. RunAtStart bool
  378. Schedule string
  379. OlderThan time.Duration
  380. } `ini:"cron.deleted_branches_cleanup"`
  381. }{
  382. UpdateMirror: struct {
  383. Enabled bool
  384. RunAtStart bool
  385. Schedule string
  386. }{
  387. Enabled: true,
  388. RunAtStart: false,
  389. Schedule: "@every 10m",
  390. },
  391. RepoHealthCheck: struct {
  392. Enabled bool
  393. RunAtStart bool
  394. Schedule string
  395. Timeout time.Duration
  396. Args []string `delim:" "`
  397. }{
  398. Enabled: true,
  399. RunAtStart: false,
  400. Schedule: "@every 24h",
  401. Timeout: 60 * time.Second,
  402. Args: []string{},
  403. },
  404. CheckRepoStats: struct {
  405. Enabled bool
  406. RunAtStart bool
  407. Schedule string
  408. }{
  409. Enabled: true,
  410. RunAtStart: true,
  411. Schedule: "@every 24h",
  412. },
  413. ArchiveCleanup: struct {
  414. Enabled bool
  415. RunAtStart bool
  416. Schedule string
  417. OlderThan time.Duration
  418. }{
  419. Enabled: true,
  420. RunAtStart: true,
  421. Schedule: "@every 24h",
  422. OlderThan: 24 * time.Hour,
  423. },
  424. SyncExternalUsers: struct {
  425. Enabled bool
  426. RunAtStart bool
  427. Schedule string
  428. UpdateExisting bool
  429. }{
  430. Enabled: true,
  431. RunAtStart: false,
  432. Schedule: "@every 24h",
  433. UpdateExisting: true,
  434. },
  435. DeletedBranchesCleanup: struct {
  436. Enabled bool
  437. RunAtStart bool
  438. Schedule string
  439. OlderThan time.Duration
  440. }{
  441. Enabled: true,
  442. RunAtStart: true,
  443. Schedule: "@every 24h",
  444. OlderThan: 24 * time.Hour,
  445. },
  446. }
  447. // Git settings
  448. Git = struct {
  449. Version string `ini:"-"`
  450. DisableDiffHighlight bool
  451. MaxGitDiffLines int
  452. MaxGitDiffLineCharacters int
  453. MaxGitDiffFiles int
  454. GCArgs []string `delim:" "`
  455. Timeout struct {
  456. Migrate int
  457. Mirror int
  458. Clone int
  459. Pull int
  460. GC int `ini:"GC"`
  461. } `ini:"git.timeout"`
  462. }{
  463. DisableDiffHighlight: false,
  464. MaxGitDiffLines: 1000,
  465. MaxGitDiffLineCharacters: 5000,
  466. MaxGitDiffFiles: 100,
  467. GCArgs: []string{},
  468. Timeout: struct {
  469. Migrate int
  470. Mirror int
  471. Clone int
  472. Pull int
  473. GC int `ini:"GC"`
  474. }{
  475. Migrate: 600,
  476. Mirror: 300,
  477. Clone: 300,
  478. Pull: 300,
  479. GC: 60,
  480. },
  481. }
  482. // Mirror settings
  483. Mirror struct {
  484. DefaultInterval time.Duration
  485. MinInterval time.Duration
  486. }
  487. // API settings
  488. API = struct {
  489. EnableSwaggerEndpoint bool
  490. MaxResponseItems int
  491. }{
  492. EnableSwaggerEndpoint: true,
  493. MaxResponseItems: 50,
  494. }
  495. U2F = struct {
  496. AppID string
  497. TrustedFacets []string
  498. }{}
  499. // I18n settings
  500. Langs []string
  501. Names []string
  502. dateLangs map[string]string
  503. // Highlight settings are loaded in modules/template/highlight.go
  504. // Other settings
  505. ShowFooterBranding bool
  506. ShowFooterVersion bool
  507. ShowFooterTemplateLoadTime bool
  508. // Global setting objects
  509. Cfg *ini.File
  510. CustomPath string // Custom directory path
  511. CustomConf string
  512. CustomPID string
  513. ProdMode bool
  514. RunUser string
  515. IsWindows bool
  516. HasRobotsTxt bool
  517. InternalToken string // internal access token
  518. IterateBufferSize int
  519. ExternalMarkupParsers []MarkupParser
  520. // UILocation is the location on the UI, so that we can display the time on UI.
  521. // Currently only show the default time.Local, it could be added to app.ini after UI is ready
  522. UILocation = time.Local
  523. )
  524. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  525. func DateLang(lang string) string {
  526. name, ok := dateLangs[lang]
  527. if ok {
  528. return name
  529. }
  530. return "en"
  531. }
  532. func getAppPath() (string, error) {
  533. var appPath string
  534. var err error
  535. if IsWindows && filepath.IsAbs(os.Args[0]) {
  536. appPath = filepath.Clean(os.Args[0])
  537. } else {
  538. appPath, err = exec.LookPath(os.Args[0])
  539. }
  540. if err != nil {
  541. return "", err
  542. }
  543. appPath, err = filepath.Abs(appPath)
  544. if err != nil {
  545. return "", err
  546. }
  547. // Note: we don't use path.Dir here because it does not handle case
  548. // which path starts with two "/" in Windows: "//psf/Home/..."
  549. return strings.Replace(appPath, "\\", "/", -1), err
  550. }
  551. func getWorkPath(appPath string) string {
  552. workPath := ""
  553. giteaWorkPath := os.Getenv("GITEA_WORK_DIR")
  554. if len(giteaWorkPath) > 0 {
  555. workPath = giteaWorkPath
  556. } else {
  557. i := strings.LastIndex(appPath, "/")
  558. if i == -1 {
  559. workPath = appPath
  560. } else {
  561. workPath = appPath[:i]
  562. }
  563. }
  564. return strings.Replace(workPath, "\\", "/", -1)
  565. }
  566. func init() {
  567. IsWindows = runtime.GOOS == "windows"
  568. log.NewLogger(0, "console", `{"level": 0}`)
  569. var err error
  570. if AppPath, err = getAppPath(); err != nil {
  571. log.Fatal(4, "Failed to get app path: %v", err)
  572. }
  573. AppWorkPath = getWorkPath(AppPath)
  574. }
  575. func forcePathSeparator(path string) {
  576. if strings.Contains(path, "\\") {
  577. log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
  578. }
  579. }
  580. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  581. // actual user that runs the app. The first return value is the actual user name.
  582. // This check is ignored under Windows since SSH remote login is not the main
  583. // method to login on Windows.
  584. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  585. if IsWindows {
  586. return "", true
  587. }
  588. currentUser := user.CurrentUsername()
  589. return currentUser, runUser == currentUser
  590. }
  591. func createPIDFile(pidPath string) {
  592. currentPid := os.Getpid()
  593. if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil {
  594. log.Fatal(4, "Failed to create PID folder: %v", err)
  595. }
  596. file, err := os.Create(pidPath)
  597. if err != nil {
  598. log.Fatal(4, "Failed to create PID file: %v", err)
  599. }
  600. defer file.Close()
  601. if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil {
  602. log.Fatal(4, "Failed to write PID information: %v", err)
  603. }
  604. }
  605. // NewContext initializes configuration context.
  606. // NOTE: do not print any log except error.
  607. func NewContext() {
  608. Cfg = ini.Empty()
  609. CustomPath = os.Getenv("GITEA_CUSTOM")
  610. if len(CustomPath) == 0 {
  611. CustomPath = path.Join(AppWorkPath, "custom")
  612. } else if !filepath.IsAbs(CustomPath) {
  613. CustomPath = path.Join(AppWorkPath, CustomPath)
  614. }
  615. if len(CustomPID) > 0 {
  616. createPIDFile(CustomPID)
  617. }
  618. if len(CustomConf) == 0 {
  619. CustomConf = path.Join(CustomPath, "conf/app.ini")
  620. } else if !filepath.IsAbs(CustomConf) {
  621. CustomConf = path.Join(CustomPath, CustomConf)
  622. }
  623. if com.IsFile(CustomConf) {
  624. if err := Cfg.Append(CustomConf); err != nil {
  625. log.Fatal(4, "Failed to load custom conf '%s': %v", CustomConf, err)
  626. }
  627. } else {
  628. log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
  629. }
  630. Cfg.NameMapper = ini.AllCapsUnderscore
  631. homeDir, err := com.HomeDir()
  632. if err != nil {
  633. log.Fatal(4, "Failed to get home directory: %v", err)
  634. }
  635. homeDir = strings.Replace(homeDir, "\\", "/", -1)
  636. LogLevel = getLogLevel("log", "LEVEL", "Info")
  637. LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
  638. forcePathSeparator(LogRootPath)
  639. sec := Cfg.Section("server")
  640. AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
  641. Protocol = HTTP
  642. if sec.Key("PROTOCOL").String() == "https" {
  643. Protocol = HTTPS
  644. CertFile = sec.Key("CERT_FILE").String()
  645. KeyFile = sec.Key("KEY_FILE").String()
  646. } else if sec.Key("PROTOCOL").String() == "fcgi" {
  647. Protocol = FCGI
  648. } else if sec.Key("PROTOCOL").String() == "unix" {
  649. Protocol = UnixSocket
  650. UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
  651. UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
  652. if err != nil || UnixSocketPermissionParsed > 0777 {
  653. log.Fatal(4, "Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
  654. }
  655. UnixSocketPermission = uint32(UnixSocketPermissionParsed)
  656. }
  657. Domain = sec.Key("DOMAIN").MustString("localhost")
  658. HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
  659. HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
  660. defaultAppURL := string(Protocol) + "://" + Domain
  661. if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
  662. defaultAppURL += ":" + HTTPPort
  663. }
  664. AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
  665. AppURL = strings.TrimRight(AppURL, "/") + "/"
  666. // Check if has app suburl.
  667. url, err := url.Parse(AppURL)
  668. if err != nil {
  669. log.Fatal(4, "Invalid ROOT_URL '%s': %s", AppURL, err)
  670. }
  671. // Suburl should start with '/' and end without '/', such as '/{subpath}'.
  672. // This value is empty if site does not have sub-url.
  673. AppSubURL = strings.TrimSuffix(url.Path, "/")
  674. AppSubURLDepth = strings.Count(AppSubURL, "/")
  675. // Check if Domain differs from AppURL domain than update it to AppURL's domain
  676. // TODO: Can be replaced with url.Hostname() when minimal GoLang version is 1.8
  677. urlHostname := strings.SplitN(url.Host, ":", 2)[0]
  678. if urlHostname != Domain && net.ParseIP(urlHostname) == nil {
  679. Domain = urlHostname
  680. }
  681. var defaultLocalURL string
  682. switch Protocol {
  683. case UnixSocket:
  684. defaultLocalURL = "http://unix/"
  685. case FCGI:
  686. defaultLocalURL = AppURL
  687. default:
  688. defaultLocalURL = string(Protocol) + "://"
  689. if HTTPAddr == "0.0.0.0" {
  690. defaultLocalURL += "localhost"
  691. } else {
  692. defaultLocalURL += HTTPAddr
  693. }
  694. defaultLocalURL += ":" + HTTPPort + "/"
  695. }
  696. LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
  697. RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
  698. PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
  699. OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
  700. DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
  701. StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
  702. AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
  703. EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
  704. EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
  705. switch sec.Key("LANDING_PAGE").MustString("home") {
  706. case "explore":
  707. LandingPageURL = LandingPageExplore
  708. case "organizations":
  709. LandingPageURL = LandingPageOrganizations
  710. default:
  711. LandingPageURL = LandingPageHome
  712. }
  713. if len(SSH.Domain) == 0 {
  714. SSH.Domain = Domain
  715. }
  716. SSH.RootPath = path.Join(homeDir, ".ssh")
  717. serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
  718. if len(serverCiphers) > 0 {
  719. SSH.ServerCiphers = serverCiphers
  720. }
  721. serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
  722. if len(serverKeyExchanges) > 0 {
  723. SSH.ServerKeyExchanges = serverKeyExchanges
  724. }
  725. serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
  726. if len(serverMACs) > 0 {
  727. SSH.ServerMACs = serverMACs
  728. }
  729. SSH.KeyTestPath = os.TempDir()
  730. if err = Cfg.Section("server").MapTo(&SSH); err != nil {
  731. log.Fatal(4, "Failed to map SSH settings: %v", err)
  732. }
  733. SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
  734. SSH.Port = sec.Key("SSH_PORT").MustInt(22)
  735. SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
  736. // When disable SSH, start builtin server value is ignored.
  737. if SSH.Disabled {
  738. SSH.StartBuiltinServer = false
  739. }
  740. if !SSH.Disabled && !SSH.StartBuiltinServer {
  741. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  742. log.Fatal(4, "Failed to create '%s': %v", SSH.RootPath, err)
  743. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  744. log.Fatal(4, "Failed to create '%s': %v", SSH.KeyTestPath, err)
  745. }
  746. }
  747. SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool()
  748. SSH.MinimumKeySizes = map[string]int{}
  749. minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
  750. for _, key := range minimumKeySizes {
  751. if key.MustInt() != -1 {
  752. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  753. }
  754. }
  755. SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
  756. SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
  757. sec = Cfg.Section("server")
  758. if err = sec.MapTo(&LFS); err != nil {
  759. log.Fatal(4, "Failed to map LFS settings: %v", err)
  760. }
  761. LFS.ContentPath = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs"))
  762. if !filepath.IsAbs(LFS.ContentPath) {
  763. LFS.ContentPath = filepath.Join(AppWorkPath, LFS.ContentPath)
  764. }
  765. LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(20 * time.Minute)
  766. if LFS.StartServer {
  767. if err := os.MkdirAll(LFS.ContentPath, 0700); err != nil {
  768. log.Fatal(4, "Failed to create '%s': %v", LFS.ContentPath, err)
  769. }
  770. LFS.JWTSecretBytes = make([]byte, 32)
  771. n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
  772. if err != nil || n != 32 {
  773. LFS.JWTSecretBase64, err = generate.NewLfsJwtSecret()
  774. if err != nil {
  775. log.Fatal(4, "Error generating JWT Secret for custom config: %v", err)
  776. return
  777. }
  778. // Save secret
  779. cfg := ini.Empty()
  780. if com.IsFile(CustomConf) {
  781. // Keeps custom settings if there is already something.
  782. if err := cfg.Append(CustomConf); err != nil {
  783. log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err)
  784. }
  785. }
  786. cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
  787. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  788. log.Fatal(4, "Failed to create '%s': %v", CustomConf, err)
  789. }
  790. if err := cfg.SaveTo(CustomConf); err != nil {
  791. log.Fatal(4, "Error saving generated JWT Secret to custom config: %v", err)
  792. return
  793. }
  794. }
  795. //Disable LFS client hooks if installed for the current OS user
  796. //Needs at least git v2.1.2
  797. binVersion, err := git.BinVersion()
  798. if err != nil {
  799. log.Fatal(4, "Error retrieving git version: %v", err)
  800. }
  801. splitVersion := strings.SplitN(binVersion, ".", 4)
  802. majorVersion, err := strconv.ParseUint(splitVersion[0], 10, 64)
  803. if err != nil {
  804. log.Fatal(4, "Error parsing git major version: %v", err)
  805. }
  806. minorVersion, err := strconv.ParseUint(splitVersion[1], 10, 64)
  807. if err != nil {
  808. log.Fatal(4, "Error parsing git minor version: %v", err)
  809. }
  810. revisionVersion, err := strconv.ParseUint(splitVersion[2], 10, 64)
  811. if err != nil {
  812. log.Fatal(4, "Error parsing git revision version: %v", err)
  813. }
  814. if !((majorVersion > 2) || (majorVersion == 2 && minorVersion > 1) ||
  815. (majorVersion == 2 && minorVersion == 1 && revisionVersion >= 2)) {
  816. LFS.StartServer = false
  817. log.Error(4, "LFS server support needs at least Git v2.1.2")
  818. } else {
  819. git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=",
  820. "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
  821. }
  822. }
  823. sec = Cfg.Section("security")
  824. InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
  825. SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
  826. LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
  827. CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
  828. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
  829. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  830. MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
  831. ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
  832. DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(false)
  833. InternalToken = sec.Key("INTERNAL_TOKEN").String()
  834. if len(InternalToken) == 0 {
  835. InternalToken, err = generate.NewInternalToken()
  836. if err != nil {
  837. log.Fatal(4, "Error generate internal token: %v", err)
  838. }
  839. // Save secret
  840. cfgSave := ini.Empty()
  841. if com.IsFile(CustomConf) {
  842. // Keeps custom settings if there is already something.
  843. if err := cfgSave.Append(CustomConf); err != nil {
  844. log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err)
  845. }
  846. }
  847. cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(InternalToken)
  848. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  849. log.Fatal(4, "Failed to create '%s': %v", CustomConf, err)
  850. }
  851. if err := cfgSave.SaveTo(CustomConf); err != nil {
  852. log.Fatal(4, "Error saving generated JWT Secret to custom config: %v", err)
  853. }
  854. }
  855. IterateBufferSize = Cfg.Section("database").Key("ITERATE_BUFFER_SIZE").MustInt(50)
  856. LogSQL = Cfg.Section("database").Key("LOG_SQL").MustBool(true)
  857. sec = Cfg.Section("attachment")
  858. AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
  859. if !filepath.IsAbs(AttachmentPath) {
  860. AttachmentPath = path.Join(AppWorkPath, AttachmentPath)
  861. }
  862. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)
  863. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  864. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  865. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  866. TimeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("RFC1123")
  867. TimeFormat = map[string]string{
  868. "ANSIC": time.ANSIC,
  869. "UnixDate": time.UnixDate,
  870. "RubyDate": time.RubyDate,
  871. "RFC822": time.RFC822,
  872. "RFC822Z": time.RFC822Z,
  873. "RFC850": time.RFC850,
  874. "RFC1123": time.RFC1123,
  875. "RFC1123Z": time.RFC1123Z,
  876. "RFC3339": time.RFC3339,
  877. "RFC3339Nano": time.RFC3339Nano,
  878. "Kitchen": time.Kitchen,
  879. "Stamp": time.Stamp,
  880. "StampMilli": time.StampMilli,
  881. "StampMicro": time.StampMicro,
  882. "StampNano": time.StampNano,
  883. }[TimeFormatKey]
  884. // When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
  885. if len(TimeFormat) == 0 {
  886. TimeFormat = TimeFormatKey
  887. TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat)
  888. if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" {
  889. log.Fatal(4, "Can't create time properly, please check your time format has 2006, 01, 02, 15, 04 and 05")
  890. }
  891. log.Trace("Custom TimeFormat: %s", TimeFormat)
  892. }
  893. RunUser = Cfg.Section("").Key("RUN_USER").MustString(user.CurrentUsername())
  894. // Does not check run user when the install lock is off.
  895. if InstallLock {
  896. currentUser, match := IsRunUserMatchCurrentUser(RunUser)
  897. if !match {
  898. log.Fatal(4, "Expect user '%s' but current user is: %s", RunUser, currentUser)
  899. }
  900. }
  901. SSH.BuiltinServerUser = Cfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
  902. // Determine and create root git repository path.
  903. sec = Cfg.Section("repository")
  904. Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool()
  905. Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool()
  906. Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
  907. RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gitea-repositories"))
  908. forcePathSeparator(RepoRootPath)
  909. if !filepath.IsAbs(RepoRootPath) {
  910. RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath)
  911. } else {
  912. RepoRootPath = filepath.Clean(RepoRootPath)
  913. }
  914. ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
  915. if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
  916. log.Fatal(4, "Failed to map Repository settings: %v", err)
  917. } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
  918. log.Fatal(4, "Failed to map Repository.Editor settings: %v", err)
  919. } else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
  920. log.Fatal(4, "Failed to map Repository.Upload settings: %v", err)
  921. } else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
  922. log.Fatal(4, "Failed to map Repository.Local settings: %v", err)
  923. }
  924. if !filepath.IsAbs(Repository.Upload.TempPath) {
  925. Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
  926. }
  927. sec = Cfg.Section("picture")
  928. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))
  929. forcePathSeparator(AvatarUploadPath)
  930. if !filepath.IsAbs(AvatarUploadPath) {
  931. AvatarUploadPath = path.Join(AppWorkPath, AvatarUploadPath)
  932. }
  933. AvatarMaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
  934. AvatarMaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
  935. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  936. case "duoshuo":
  937. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  938. case "gravatar":
  939. GravatarSource = "https://secure.gravatar.com/avatar/"
  940. case "libravatar":
  941. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  942. default:
  943. GravatarSource = source
  944. }
  945. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  946. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock)
  947. if OfflineMode {
  948. DisableGravatar = true
  949. EnableFederatedAvatar = false
  950. }
  951. if DisableGravatar {
  952. EnableFederatedAvatar = false
  953. }
  954. if EnableFederatedAvatar || !DisableGravatar {
  955. GravatarSourceURL, err = url.Parse(GravatarSource)
  956. if err != nil {
  957. log.Fatal(4, "Failed to parse Gravatar URL(%s): %v",
  958. GravatarSource, err)
  959. }
  960. }
  961. if EnableFederatedAvatar {
  962. LibravatarService = libravatar.New()
  963. if GravatarSourceURL.Scheme == "https" {
  964. LibravatarService.SetUseHTTPS(true)
  965. LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
  966. } else {
  967. LibravatarService.SetUseHTTPS(false)
  968. LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
  969. }
  970. }
  971. if err = Cfg.Section("ui").MapTo(&UI); err != nil {
  972. log.Fatal(4, "Failed to map UI settings: %v", err)
  973. } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
  974. log.Fatal(4, "Failed to map Markdown settings: %v", err)
  975. } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
  976. log.Fatal(4, "Fail to map Admin settings: %v", err)
  977. } else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {
  978. log.Fatal(4, "Failed to map Cron settings: %v", err)
  979. } else if err = Cfg.Section("git").MapTo(&Git); err != nil {
  980. log.Fatal(4, "Failed to map Git settings: %v", err)
  981. } else if err = Cfg.Section("api").MapTo(&API); err != nil {
  982. log.Fatal(4, "Failed to map API settings: %v", err)
  983. }
  984. sec = Cfg.Section("mirror")
  985. Mirror.MinInterval = sec.Key("MIN_INTERVAL").MustDuration(10 * time.Minute)
  986. Mirror.DefaultInterval = sec.Key("DEFAULT_INTERVAL").MustDuration(8 * time.Hour)
  987. if Mirror.MinInterval.Minutes() < 1 {
  988. log.Warn("Mirror.MinInterval is too low")
  989. Mirror.MinInterval = 1 * time.Minute
  990. }
  991. if Mirror.DefaultInterval < Mirror.MinInterval {
  992. log.Warn("Mirror.DefaultInterval is less than Mirror.MinInterval")
  993. Mirror.DefaultInterval = time.Hour * 8
  994. }
  995. Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
  996. if len(Langs) == 0 {
  997. Langs = defaultLangs
  998. }
  999. Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
  1000. if len(Names) == 0 {
  1001. Names = defaultLangNames
  1002. }
  1003. dateLangs = Cfg.Section("i18n.datelang").KeysHash()
  1004. ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
  1005. ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true)
  1006. ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
  1007. UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
  1008. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  1009. extensionReg := regexp.MustCompile(`\.\w`)
  1010. for _, sec := range Cfg.Section("markup").ChildSections() {
  1011. name := strings.TrimLeft(sec.Name(), "markup.")
  1012. if name == "" {
  1013. log.Warn("name is empty, markup " + sec.Name() + "ignored")
  1014. continue
  1015. }
  1016. extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
  1017. var exts = make([]string, 0, len(extensions))
  1018. for _, extension := range extensions {
  1019. if !extensionReg.MatchString(extension) {
  1020. log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored")
  1021. } else {
  1022. exts = append(exts, extension)
  1023. }
  1024. }
  1025. if len(exts) == 0 {
  1026. log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored")
  1027. continue
  1028. }
  1029. command := sec.Key("RENDER_COMMAND").MustString("")
  1030. if command == "" {
  1031. log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored")
  1032. continue
  1033. }
  1034. ExternalMarkupParsers = append(ExternalMarkupParsers, MarkupParser{
  1035. Enabled: sec.Key("ENABLED").MustBool(false),
  1036. MarkupName: name,
  1037. FileExtensions: exts,
  1038. Command: command,
  1039. IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false),
  1040. })
  1041. }
  1042. sec = Cfg.Section("U2F")
  1043. U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimRight(AppURL, "/")))
  1044. U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/"))
  1045. }
  1046. // Service settings
  1047. var Service struct {
  1048. ActiveCodeLives int
  1049. ResetPwdCodeLives int
  1050. RegisterEmailConfirm bool
  1051. DisableRegistration bool
  1052. AllowOnlyExternalRegistration bool
  1053. ShowRegistrationButton bool
  1054. RequireSignInView bool
  1055. EnableNotifyMail bool
  1056. EnableReverseProxyAuth bool
  1057. EnableReverseProxyAutoRegister bool
  1058. EnableCaptcha bool
  1059. CaptchaType string
  1060. RecaptchaSecret string
  1061. RecaptchaSitekey string
  1062. DefaultKeepEmailPrivate bool
  1063. DefaultAllowCreateOrganization bool
  1064. EnableTimetracking bool
  1065. DefaultEnableTimetracking bool
  1066. DefaultAllowOnlyContributorsToTrackTime bool
  1067. NoReplyAddress string
  1068. // OpenID settings
  1069. EnableOpenIDSignIn bool
  1070. EnableOpenIDSignUp bool
  1071. OpenIDWhitelist []*regexp.Regexp
  1072. OpenIDBlacklist []*regexp.Regexp
  1073. }
  1074. func newService() {
  1075. sec := Cfg.Section("service")
  1076. Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
  1077. Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
  1078. Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
  1079. Service.AllowOnlyExternalRegistration = sec.Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").MustBool()
  1080. Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
  1081. Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
  1082. Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
  1083. Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
  1084. Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
  1085. Service.CaptchaType = sec.Key("CAPTCHA_TYPE").MustString(ImageCaptcha)
  1086. Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("")
  1087. Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("")
  1088. Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
  1089. Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
  1090. Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
  1091. if Service.EnableTimetracking {
  1092. Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true)
  1093. }
  1094. Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
  1095. Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
  1096. sec = Cfg.Section("openid")
  1097. Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
  1098. Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn)
  1099. pats := sec.Key("WHITELISTED_URIS").Strings(" ")
  1100. if len(pats) != 0 {
  1101. Service.OpenIDWhitelist = make([]*regexp.Regexp, len(pats))
  1102. for i, p := range pats {
  1103. Service.OpenIDWhitelist[i] = regexp.MustCompilePOSIX(p)
  1104. }
  1105. }
  1106. pats = sec.Key("BLACKLISTED_URIS").Strings(" ")
  1107. if len(pats) != 0 {
  1108. Service.OpenIDBlacklist = make([]*regexp.Regexp, len(pats))
  1109. for i, p := range pats {
  1110. Service.OpenIDBlacklist[i] = regexp.MustCompilePOSIX(p)
  1111. }
  1112. }
  1113. }
  1114. var logLevels = map[string]string{
  1115. "Trace": "0",
  1116. "Debug": "1",
  1117. "Info": "2",
  1118. "Warn": "3",
  1119. "Error": "4",
  1120. "Critical": "5",
  1121. }
  1122. func getLogLevel(section string, key string, defaultValue string) string {
  1123. validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
  1124. return Cfg.Section(section).Key(key).In(defaultValue, validLevels)
  1125. }
  1126. func newLogService() {
  1127. log.Info("Gitea v%s%s", AppVer, AppBuiltWith)
  1128. LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
  1129. LogConfigs = make([]string, len(LogModes))
  1130. useConsole := false
  1131. for i := 0; i < len(LogModes); i++ {
  1132. LogModes[i] = strings.TrimSpace(LogModes[i])
  1133. if LogModes[i] == "console" {
  1134. useConsole = true
  1135. }
  1136. }
  1137. if !useConsole {
  1138. log.DelLogger("console")
  1139. }
  1140. for i, mode := range LogModes {
  1141. sec, err := Cfg.GetSection("log." + mode)
  1142. if err != nil {
  1143. sec, _ = Cfg.NewSection("log." + mode)
  1144. }
  1145. // Log level.
  1146. levelName := getLogLevel("log."+mode, "LEVEL", LogLevel)
  1147. level, ok := logLevels[levelName]
  1148. if !ok {
  1149. log.Fatal(4, "Unknown log level: %s", levelName)
  1150. }
  1151. // Generate log configuration.
  1152. switch mode {
  1153. case "console":
  1154. LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
  1155. case "file":
  1156. logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gitea.log"))
  1157. if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  1158. panic(err.Error())
  1159. }
  1160. LogConfigs[i] = fmt.Sprintf(
  1161. `{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
  1162. logPath,
  1163. sec.Key("LOG_ROTATE").MustBool(true),
  1164. sec.Key("MAX_LINES").MustInt(1000000),
  1165. 1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  1166. sec.Key("DAILY_ROTATE").MustBool(true),
  1167. sec.Key("MAX_DAYS").MustInt(7))
  1168. case "conn":
  1169. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
  1170. sec.Key("RECONNECT_ON_MSG").MustBool(),
  1171. sec.Key("RECONNECT").MustBool(),
  1172. sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
  1173. sec.Key("ADDR").MustString(":7020"))
  1174. case "smtp":
  1175. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":["%s"],"subject":"%s"}`, level,
  1176. sec.Key("USER").MustString("example@example.com"),
  1177. sec.Key("PASSWD").MustString("******"),
  1178. sec.Key("HOST").MustString("127.0.0.1:25"),
  1179. strings.Replace(sec.Key("RECEIVERS").MustString("example@example.com"), ",", "\",\"", -1),
  1180. sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
  1181. case "database":
  1182. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
  1183. sec.Key("DRIVER").String(),
  1184. sec.Key("CONN").String())
  1185. }
  1186. log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
  1187. log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
  1188. }
  1189. }
  1190. // NewXORMLogService initializes xorm logger service
  1191. func NewXORMLogService(disableConsole bool) {
  1192. logModes := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
  1193. var logConfigs string
  1194. for _, mode := range logModes {
  1195. mode = strings.TrimSpace(mode)
  1196. if disableConsole && mode == "console" {
  1197. continue
  1198. }
  1199. sec, err := Cfg.GetSection("log." + mode)
  1200. if err != nil {
  1201. sec, _ = Cfg.NewSection("log." + mode)
  1202. }
  1203. // Log level.
  1204. levelName := getLogLevel("log."+mode, "LEVEL", LogLevel)
  1205. level, ok := logLevels[levelName]
  1206. if !ok {
  1207. log.Fatal(4, "Unknown log level: %s", levelName)
  1208. }
  1209. // Generate log configuration.
  1210. switch mode {
  1211. case "console":
  1212. logConfigs = fmt.Sprintf(`{"level":%s}`, level)
  1213. case "file":
  1214. logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "xorm.log"))
  1215. if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  1216. panic(err.Error())
  1217. }
  1218. logPath = path.Join(filepath.Dir(logPath), "xorm.log")
  1219. logConfigs = fmt.Sprintf(
  1220. `{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
  1221. logPath,
  1222. sec.Key("LOG_ROTATE").MustBool(true),
  1223. sec.Key("MAX_LINES").MustInt(1000000),
  1224. 1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  1225. sec.Key("DAILY_ROTATE").MustBool(true),
  1226. sec.Key("MAX_DAYS").MustInt(7))
  1227. case "conn":
  1228. logConfigs = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
  1229. sec.Key("RECONNECT_ON_MSG").MustBool(),
  1230. sec.Key("RECONNECT").MustBool(),
  1231. sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
  1232. sec.Key("ADDR").MustString(":7020"))
  1233. case "smtp":
  1234. logConfigs = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
  1235. sec.Key("USER").MustString("example@example.com"),
  1236. sec.Key("PASSWD").MustString("******"),
  1237. sec.Key("HOST").MustString("127.0.0.1:25"),
  1238. sec.Key("RECEIVERS").MustString("[]"),
  1239. sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
  1240. case "database":
  1241. logConfigs = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
  1242. sec.Key("DRIVER").String(),
  1243. sec.Key("CONN").String())
  1244. }
  1245. log.NewXORMLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, logConfigs)
  1246. if !disableConsole {
  1247. log.Info("XORM Log Mode: %s(%s)", strings.Title(mode), levelName)
  1248. }
  1249. var lvl core.LogLevel
  1250. switch levelName {
  1251. case "Trace", "Debug":
  1252. lvl = core.LOG_DEBUG
  1253. case "Info":
  1254. lvl = core.LOG_INFO
  1255. case "Warn":
  1256. lvl = core.LOG_WARNING
  1257. case "Error", "Critical":
  1258. lvl = core.LOG_ERR
  1259. }
  1260. log.XORMLogger.SetLevel(lvl)
  1261. }
  1262. if len(logConfigs) == 0 {
  1263. log.DiscardXORMLogger()
  1264. }
  1265. }
  1266. // Cache represents cache settings
  1267. type Cache struct {
  1268. Adapter string
  1269. Interval int
  1270. Conn string
  1271. TTL time.Duration
  1272. }
  1273. var (
  1274. // CacheService the global cache
  1275. CacheService *Cache
  1276. )
  1277. func newCacheService() {
  1278. sec := Cfg.Section("cache")
  1279. CacheService = &Cache{
  1280. Adapter: sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"}),
  1281. }
  1282. switch CacheService.Adapter {
  1283. case "memory":
  1284. CacheService.Interval = sec.Key("INTERVAL").MustInt(60)
  1285. case "redis", "memcache":
  1286. CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ")
  1287. default:
  1288. log.Fatal(4, "Unknown cache adapter: %s", CacheService.Adapter)
  1289. }
  1290. CacheService.TTL = sec.Key("ITEM_TTL").MustDuration(16 * time.Hour)
  1291. log.Info("Cache Service Enabled")
  1292. }
  1293. func newSessionService() {
  1294. SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
  1295. []string{"memory", "file", "redis", "mysql"})
  1296. SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
  1297. if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
  1298. SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
  1299. }
  1300. SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gitea")
  1301. SessionConfig.CookiePath = AppSubURL
  1302. SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool(false)
  1303. SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
  1304. SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
  1305. log.Info("Session Service Enabled")
  1306. }
  1307. // Mailer represents mail service.
  1308. type Mailer struct {
  1309. // Mailer
  1310. QueueLength int
  1311. Name string
  1312. From string
  1313. FromName string
  1314. FromEmail string
  1315. SendAsPlainText bool
  1316. // SMTP sender
  1317. Host string
  1318. User, Passwd string
  1319. DisableHelo bool
  1320. HeloHostname string
  1321. SkipVerify bool
  1322. UseCertificate bool
  1323. CertFile, KeyFile string
  1324. // Sendmail sender
  1325. UseSendmail bool
  1326. SendmailPath string
  1327. SendmailArgs []string
  1328. }
  1329. var (
  1330. // MailService the global mailer
  1331. MailService *Mailer
  1332. )
  1333. func newMailService() {
  1334. sec := Cfg.Section("mailer")
  1335. // Check mailer setting.
  1336. if !sec.Key("ENABLED").MustBool() {
  1337. return
  1338. }
  1339. MailService = &Mailer{
  1340. QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
  1341. Name: sec.Key("NAME").MustString(AppName),
  1342. SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
  1343. Host: sec.Key("HOST").String(),
  1344. User: sec.Key("USER").String(),
  1345. Passwd: sec.Key("PASSWD").String(),
  1346. DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
  1347. HeloHostname: sec.Key("HELO_HOSTNAME").String(),
  1348. SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
  1349. UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
  1350. CertFile: sec.Key("CERT_FILE").String(),
  1351. KeyFile: sec.Key("KEY_FILE").String(),
  1352. UseSendmail: sec.Key("USE_SENDMAIL").MustBool(),
  1353. SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"),
  1354. }
  1355. MailService.From = sec.Key("FROM").MustString(MailService.User)
  1356. if sec.HasKey("ENABLE_HTML_ALTERNATIVE") {
  1357. log.Warn("ENABLE_HTML_ALTERNATIVE is deprecated, use SEND_AS_PLAIN_TEXT")
  1358. MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
  1359. }
  1360. parsed, err := mail.ParseAddress(MailService.From)
  1361. if err != nil {
  1362. log.Fatal(4, "Invalid mailer.FROM (%s): %v", MailService.From, err)
  1363. }
  1364. MailService.FromName = parsed.Name
  1365. MailService.FromEmail = parsed.Address
  1366. if MailService.UseSendmail {
  1367. MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
  1368. if err != nil {
  1369. log.Error(4, "Failed to parse Sendmail args: %v", CustomConf, err)
  1370. }
  1371. }
  1372. log.Info("Mail Service Enabled")
  1373. }
  1374. func newRegisterMailService() {
  1375. if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  1376. return
  1377. } else if MailService == nil {
  1378. log.Warn("Register Mail Service: Mail Service is not enabled")
  1379. return
  1380. }
  1381. Service.RegisterEmailConfirm = true
  1382. log.Info("Register Mail Service Enabled")
  1383. }
  1384. func newNotifyMailService() {
  1385. if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  1386. return
  1387. } else if MailService == nil {
  1388. log.Warn("Notify Mail Service: Mail Service is not enabled")
  1389. return
  1390. }
  1391. Service.EnableNotifyMail = true
  1392. log.Info("Notify Mail Service Enabled")
  1393. }
  1394. func newWebhookService() {
  1395. sec := Cfg.Section("webhook")
  1396. Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
  1397. Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
  1398. Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
  1399. Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk"}
  1400. Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
  1401. }
  1402. // NewServices initializes the services
  1403. func NewServices() {
  1404. newService()
  1405. newLogService()
  1406. NewXORMLogService(false)
  1407. newCacheService()
  1408. newSessionService()
  1409. newMailService()
  1410. newRegisterMailService()
  1411. newNotifyMailService()
  1412. newWebhookService()
  1413. }