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.

config.go 36KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "crypto/tls"
  8. "errors"
  9. "fmt"
  10. "io/ioutil"
  11. "log"
  12. "net"
  13. "os"
  14. "regexp"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "code.cloudfoundry.org/bytefmt"
  20. "github.com/oragono/oragono/irc/caps"
  21. "github.com/oragono/oragono/irc/cloaks"
  22. "github.com/oragono/oragono/irc/connection_limits"
  23. "github.com/oragono/oragono/irc/custime"
  24. "github.com/oragono/oragono/irc/isupport"
  25. "github.com/oragono/oragono/irc/languages"
  26. "github.com/oragono/oragono/irc/ldap"
  27. "github.com/oragono/oragono/irc/logger"
  28. "github.com/oragono/oragono/irc/modes"
  29. "github.com/oragono/oragono/irc/mysql"
  30. "github.com/oragono/oragono/irc/passwd"
  31. "github.com/oragono/oragono/irc/utils"
  32. "gopkg.in/yaml.v2"
  33. )
  34. // here's how this works: exported (capitalized) members of the config structs
  35. // are defined in the YAML file and deserialized directly from there. They may
  36. // be postprocessed and overwritten by LoadConfig. Unexported (lowercase) members
  37. // are derived from the exported members in LoadConfig.
  38. // TLSListenConfig defines configuration options for listening on TLS.
  39. type TLSListenConfig struct {
  40. Cert string
  41. Key string
  42. Proxy bool
  43. }
  44. // This is the YAML-deserializable type of the value of the `Server.Listeners` map
  45. type listenerConfigBlock struct {
  46. TLS TLSListenConfig
  47. Tor bool
  48. STSOnly bool `yaml:"sts-only"`
  49. }
  50. // listenerConfig is the config governing a particular listener (bound address),
  51. // in particular whether it has TLS or Tor (or both) enabled.
  52. type listenerConfig struct {
  53. TLSConfig *tls.Config
  54. Tor bool
  55. STSOnly bool
  56. ProxyBeforeTLS bool
  57. }
  58. type PersistentStatus uint
  59. const (
  60. PersistentUnspecified PersistentStatus = iota
  61. PersistentDisabled
  62. PersistentOptIn
  63. PersistentOptOut
  64. PersistentMandatory
  65. )
  66. func persistentStatusToString(status PersistentStatus) string {
  67. switch status {
  68. case PersistentUnspecified:
  69. return "default"
  70. case PersistentDisabled:
  71. return "disabled"
  72. case PersistentOptIn:
  73. return "opt-in"
  74. case PersistentOptOut:
  75. return "opt-out"
  76. case PersistentMandatory:
  77. return "mandatory"
  78. default:
  79. return ""
  80. }
  81. }
  82. func persistentStatusFromString(status string) (PersistentStatus, error) {
  83. switch strings.ToLower(status) {
  84. case "default":
  85. return PersistentUnspecified, nil
  86. case "":
  87. return PersistentDisabled, nil
  88. case "opt-in":
  89. return PersistentOptIn, nil
  90. case "opt-out":
  91. return PersistentOptOut, nil
  92. case "mandatory":
  93. return PersistentMandatory, nil
  94. default:
  95. b, err := utils.StringToBool(status)
  96. if b {
  97. return PersistentMandatory, err
  98. } else {
  99. return PersistentDisabled, err
  100. }
  101. }
  102. }
  103. func (ps *PersistentStatus) UnmarshalYAML(unmarshal func(interface{}) error) error {
  104. var orig string
  105. var err error
  106. if err = unmarshal(&orig); err != nil {
  107. return err
  108. }
  109. result, err := persistentStatusFromString(orig)
  110. if err == nil {
  111. if result == PersistentUnspecified {
  112. result = PersistentDisabled
  113. }
  114. *ps = result
  115. }
  116. return err
  117. }
  118. func persistenceEnabled(serverSetting, clientSetting PersistentStatus) (enabled bool) {
  119. if serverSetting == PersistentDisabled {
  120. return false
  121. } else if serverSetting == PersistentMandatory {
  122. return true
  123. } else if clientSetting == PersistentDisabled {
  124. return false
  125. } else if clientSetting == PersistentMandatory {
  126. return true
  127. } else if serverSetting == PersistentOptOut {
  128. return true
  129. } else {
  130. return false
  131. }
  132. }
  133. type HistoryStatus uint
  134. const (
  135. HistoryDefault HistoryStatus = iota
  136. HistoryDisabled
  137. HistoryEphemeral
  138. HistoryPersistent
  139. )
  140. func historyStatusFromString(str string) (status HistoryStatus, err error) {
  141. switch strings.ToLower(str) {
  142. case "default":
  143. return HistoryDefault, nil
  144. case "ephemeral":
  145. return HistoryEphemeral, nil
  146. case "persistent":
  147. return HistoryPersistent, nil
  148. default:
  149. b, err := utils.StringToBool(str)
  150. if b {
  151. return HistoryPersistent, err
  152. } else {
  153. return HistoryDisabled, err
  154. }
  155. }
  156. }
  157. func historyStatusToString(status HistoryStatus) string {
  158. switch status {
  159. case HistoryDefault:
  160. return "default"
  161. case HistoryDisabled:
  162. return "disabled"
  163. case HistoryEphemeral:
  164. return "ephemeral"
  165. case HistoryPersistent:
  166. return "persistent"
  167. default:
  168. return ""
  169. }
  170. }
  171. // XXX you must have already checked History.Enabled before calling this
  172. func historyEnabled(serverSetting PersistentStatus, localSetting HistoryStatus) (result HistoryStatus) {
  173. switch serverSetting {
  174. case PersistentMandatory:
  175. return HistoryPersistent
  176. case PersistentOptOut:
  177. if localSetting == HistoryDefault {
  178. return HistoryPersistent
  179. } else {
  180. return localSetting
  181. }
  182. case PersistentOptIn:
  183. switch localSetting {
  184. case HistoryPersistent:
  185. return HistoryPersistent
  186. case HistoryEphemeral, HistoryDefault:
  187. return HistoryEphemeral
  188. default:
  189. return HistoryDisabled
  190. }
  191. case PersistentDisabled:
  192. if localSetting == HistoryDisabled {
  193. return HistoryDisabled
  194. } else {
  195. return HistoryEphemeral
  196. }
  197. default:
  198. // PersistentUnspecified: shouldn't happen because the deserializer converts it
  199. // to PersistentDisabled
  200. if localSetting == HistoryDefault {
  201. return HistoryEphemeral
  202. } else {
  203. return localSetting
  204. }
  205. }
  206. }
  207. type MulticlientConfig struct {
  208. Enabled bool
  209. AllowedByDefault bool `yaml:"allowed-by-default"`
  210. AlwaysOn PersistentStatus `yaml:"always-on"`
  211. }
  212. type AccountConfig struct {
  213. Registration AccountRegistrationConfig
  214. AuthenticationEnabled bool `yaml:"authentication-enabled"`
  215. RequireSasl struct {
  216. Enabled bool
  217. Exempted []string
  218. exemptedNets []net.IPNet
  219. } `yaml:"require-sasl"`
  220. LDAP ldap.ServerConfig
  221. LoginThrottling struct {
  222. Enabled bool
  223. Duration time.Duration
  224. MaxAttempts int `yaml:"max-attempts"`
  225. } `yaml:"login-throttling"`
  226. SkipServerPassword bool `yaml:"skip-server-password"`
  227. NickReservation struct {
  228. Enabled bool
  229. AdditionalNickLimit int `yaml:"additional-nick-limit"`
  230. Method NickEnforcementMethod
  231. AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
  232. RenameTimeout time.Duration `yaml:"rename-timeout"`
  233. // RenamePrefix is the legacy field, GuestFormat is the new version
  234. RenamePrefix string `yaml:"rename-prefix"`
  235. GuestFormat string `yaml:"guest-nickname-format"`
  236. guestRegexp *regexp.Regexp
  237. guestRegexpFolded *regexp.Regexp
  238. ForceGuestFormat bool `yaml:"force-guest-format"`
  239. ForceNickEqualsAccount bool `yaml:"force-nick-equals-account"`
  240. } `yaml:"nick-reservation"`
  241. Multiclient MulticlientConfig
  242. Bouncer *MulticlientConfig // # handle old name for 'multiclient'
  243. VHosts VHostConfig
  244. }
  245. // AccountRegistrationConfig controls account registration.
  246. type AccountRegistrationConfig struct {
  247. Enabled bool
  248. EnabledCallbacks []string `yaml:"enabled-callbacks"`
  249. EnabledCredentialTypes []string `yaml:"-"`
  250. VerifyTimeout custime.Duration `yaml:"verify-timeout"`
  251. Callbacks struct {
  252. Mailto struct {
  253. Server string
  254. Port int
  255. TLS struct {
  256. Enabled bool
  257. InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
  258. ServerName string `yaml:"servername"`
  259. }
  260. Username string
  261. Password string
  262. Sender string
  263. VerifyMessageSubject string `yaml:"verify-message-subject"`
  264. VerifyMessage string `yaml:"verify-message"`
  265. }
  266. }
  267. BcryptCost uint `yaml:"bcrypt-cost"`
  268. }
  269. type VHostConfig struct {
  270. Enabled bool
  271. MaxLength int `yaml:"max-length"`
  272. ValidRegexpRaw string `yaml:"valid-regexp"`
  273. ValidRegexp *regexp.Regexp
  274. UserRequests struct {
  275. Enabled bool
  276. Channel string
  277. Cooldown custime.Duration
  278. } `yaml:"user-requests"`
  279. OfferList []string `yaml:"offer-list"`
  280. }
  281. type NickEnforcementMethod int
  282. const (
  283. // NickEnforcementOptional is the zero value; it serializes to
  284. // "optional" in the yaml config, and "default" as an arg to `NS ENFORCE`.
  285. // in both cases, it means "defer to the other source of truth", i.e.,
  286. // in the config, defer to the user's custom setting, and as a custom setting,
  287. // defer to the default in the config. if both are NickEnforcementOptional then
  288. // there is no enforcement.
  289. // XXX: these are serialized as numbers in the database, so beware of collisions
  290. // when refactoring (any numbers currently in use must keep their meanings, or
  291. // else be fixed up by a schema change)
  292. NickEnforcementOptional NickEnforcementMethod = iota
  293. NickEnforcementNone
  294. NickEnforcementWithTimeout
  295. NickEnforcementStrict
  296. )
  297. func nickReservationToString(method NickEnforcementMethod) string {
  298. switch method {
  299. case NickEnforcementOptional:
  300. return "default"
  301. case NickEnforcementNone:
  302. return "none"
  303. case NickEnforcementWithTimeout:
  304. return "timeout"
  305. case NickEnforcementStrict:
  306. return "strict"
  307. default:
  308. return ""
  309. }
  310. }
  311. func nickReservationFromString(method string) (NickEnforcementMethod, error) {
  312. switch strings.ToLower(method) {
  313. case "default":
  314. return NickEnforcementOptional, nil
  315. case "optional":
  316. return NickEnforcementOptional, nil
  317. case "none":
  318. return NickEnforcementNone, nil
  319. case "timeout":
  320. return NickEnforcementWithTimeout, nil
  321. case "strict":
  322. return NickEnforcementStrict, nil
  323. default:
  324. return NickEnforcementOptional, fmt.Errorf("invalid nick-reservation.method value: %s", method)
  325. }
  326. }
  327. func (nr *NickEnforcementMethod) UnmarshalYAML(unmarshal func(interface{}) error) error {
  328. var orig string
  329. var err error
  330. if err = unmarshal(&orig); err != nil {
  331. return err
  332. }
  333. method, err := nickReservationFromString(orig)
  334. if err == nil {
  335. *nr = method
  336. }
  337. return err
  338. }
  339. func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err error) {
  340. var orig string
  341. if err = unmarshal(&orig); err != nil {
  342. return err
  343. }
  344. var result Casemapping
  345. switch strings.ToLower(orig) {
  346. case "ascii":
  347. result = CasemappingASCII
  348. case "precis", "rfc7613", "rfc8265":
  349. result = CasemappingPRECIS
  350. case "permissive", "fun":
  351. result = CasemappingPermissive
  352. default:
  353. return fmt.Errorf("invalid casemapping value: %s", orig)
  354. }
  355. *cm = result
  356. return nil
  357. }
  358. // OperClassConfig defines a specific operator class.
  359. type OperClassConfig struct {
  360. Title string
  361. WhoisLine string
  362. Extends string
  363. Capabilities []string
  364. }
  365. // OperConfig defines a specific operator's configuration.
  366. type OperConfig struct {
  367. Class string
  368. Vhost string
  369. WhoisLine string `yaml:"whois-line"`
  370. Password string
  371. Fingerprint string
  372. Auto bool
  373. Modes string
  374. }
  375. // Various server-enforced limits on data size.
  376. type Limits struct {
  377. AwayLen int `yaml:"awaylen"`
  378. ChanListModes int `yaml:"chan-list-modes"`
  379. ChannelLen int `yaml:"channellen"`
  380. IdentLen int `yaml:"identlen"`
  381. KickLen int `yaml:"kicklen"`
  382. MonitorEntries int `yaml:"monitor-entries"`
  383. NickLen int `yaml:"nicklen"`
  384. TopicLen int `yaml:"topiclen"`
  385. WhowasEntries int `yaml:"whowas-entries"`
  386. RegistrationMessages int `yaml:"registration-messages"`
  387. Multiline struct {
  388. MaxBytes int `yaml:"max-bytes"`
  389. MaxLines int `yaml:"max-lines"`
  390. }
  391. }
  392. // STSConfig controls the STS configuration/
  393. type STSConfig struct {
  394. Enabled bool
  395. Duration custime.Duration
  396. Port int
  397. Preload bool
  398. STSOnlyBanner string `yaml:"sts-only-banner"`
  399. bannerLines []string
  400. }
  401. // Value returns the STS value to advertise in CAP
  402. func (sts *STSConfig) Value() string {
  403. val := fmt.Sprintf("duration=%d", int(time.Duration(sts.Duration).Seconds()))
  404. if sts.Enabled && sts.Port > 0 {
  405. val += fmt.Sprintf(",port=%d", sts.Port)
  406. }
  407. if sts.Enabled && sts.Preload {
  408. val += ",preload"
  409. }
  410. return val
  411. }
  412. type FakelagConfig struct {
  413. Enabled bool
  414. Window time.Duration
  415. BurstLimit uint `yaml:"burst-limit"`
  416. MessagesPerWindow uint `yaml:"messages-per-window"`
  417. Cooldown time.Duration
  418. }
  419. type TorListenersConfig struct {
  420. Listeners []string // legacy only
  421. RequireSasl bool `yaml:"require-sasl"`
  422. Vhost string
  423. MaxConnections int `yaml:"max-connections"`
  424. ThrottleDuration time.Duration `yaml:"throttle-duration"`
  425. MaxConnectionsPerDuration int `yaml:"max-connections-per-duration"`
  426. }
  427. // Config defines the overall configuration.
  428. type Config struct {
  429. Network struct {
  430. Name string
  431. }
  432. Server struct {
  433. Password string
  434. passwordBytes []byte
  435. Name string
  436. nameCasefolded string
  437. // Listeners is the new style for configuring listeners:
  438. Listeners map[string]listenerConfigBlock
  439. UnixBindMode os.FileMode `yaml:"unix-bind-mode"`
  440. TorListeners TorListenersConfig `yaml:"tor-listeners"`
  441. // they get parsed into this internal representation:
  442. trueListeners map[string]listenerConfig
  443. STS STSConfig
  444. LookupHostnames *bool `yaml:"lookup-hostnames"`
  445. lookupHostnames bool
  446. ForwardConfirmHostnames bool `yaml:"forward-confirm-hostnames"`
  447. CheckIdent bool `yaml:"check-ident"`
  448. MOTD string
  449. motdLines []string
  450. MOTDFormatting bool `yaml:"motd-formatting"`
  451. ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
  452. proxyAllowedFromNets []net.IPNet
  453. WebIRC []webircConfig `yaml:"webirc"`
  454. MaxSendQString string `yaml:"max-sendq"`
  455. MaxSendQBytes int
  456. AllowPlaintextResume bool `yaml:"allow-plaintext-resume"`
  457. Compatibility struct {
  458. ForceTrailing *bool `yaml:"force-trailing"`
  459. forceTrailing bool
  460. SendUnprefixedSasl bool `yaml:"send-unprefixed-sasl"`
  461. }
  462. isupport isupport.List
  463. IPLimits connection_limits.LimiterConfig `yaml:"ip-limits"`
  464. Cloaks cloaks.CloakConfig `yaml:"ip-cloaking"`
  465. SecureNetDefs []string `yaml:"secure-nets"`
  466. secureNets []net.IPNet
  467. supportedCaps *caps.Set
  468. capValues caps.Values
  469. Casemapping Casemapping
  470. }
  471. Roleplay struct {
  472. Enabled *bool
  473. enabled bool
  474. RequireChanops bool `yaml:"require-chanops"`
  475. RequireOper bool `yaml:"require-oper"`
  476. AddSuffix *bool `yaml:"add-suffix"`
  477. addSuffix bool
  478. }
  479. Languages struct {
  480. Enabled bool
  481. Path string
  482. Default string
  483. }
  484. languageManager *languages.Manager
  485. Datastore struct {
  486. Path string
  487. AutoUpgrade bool
  488. MySQL mysql.Config
  489. }
  490. Accounts AccountConfig
  491. Channels struct {
  492. DefaultModes *string `yaml:"default-modes"`
  493. defaultModes modes.Modes
  494. MaxChannelsPerClient int `yaml:"max-channels-per-client"`
  495. OpOnlyCreation bool `yaml:"operator-only-creation"`
  496. Registration struct {
  497. Enabled bool
  498. OperatorOnly bool `yaml:"operator-only"`
  499. MaxChannelsPerAccount int `yaml:"max-channels-per-account"`
  500. }
  501. }
  502. OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
  503. Opers map[string]*OperConfig
  504. // parsed operator definitions, unexported so they can't be defined
  505. // directly in YAML:
  506. operators map[string]*Oper
  507. Logging []logger.LoggingConfig
  508. Debug struct {
  509. RecoverFromErrors *bool `yaml:"recover-from-errors"`
  510. recoverFromErrors bool
  511. PprofListener *string `yaml:"pprof-listener"`
  512. }
  513. Limits Limits
  514. Fakelag FakelagConfig
  515. History struct {
  516. Enabled bool
  517. ChannelLength int `yaml:"channel-length"`
  518. ClientLength int `yaml:"client-length"`
  519. AutoresizeWindow time.Duration `yaml:"autoresize-window"`
  520. AutoreplayOnJoin int `yaml:"autoreplay-on-join"`
  521. ChathistoryMax int `yaml:"chathistory-maxmessages"`
  522. ZNCMax int `yaml:"znc-maxmessages"`
  523. Restrictions struct {
  524. ExpireTime custime.Duration `yaml:"expire-time"`
  525. EnforceRegistrationDate bool `yaml:"enforce-registration-date"`
  526. GracePeriod custime.Duration `yaml:"grace-period"`
  527. }
  528. Persistent struct {
  529. Enabled bool
  530. UnregisteredChannels bool `yaml:"unregistered-channels"`
  531. RegisteredChannels PersistentStatus `yaml:"registered-channels"`
  532. DirectMessages PersistentStatus `yaml:"direct-messages"`
  533. }
  534. }
  535. Filename string
  536. }
  537. // OperClass defines an assembled operator class.
  538. type OperClass struct {
  539. Title string
  540. WhoisLine string `yaml:"whois-line"`
  541. Capabilities StringSet // map to make lookups much easier
  542. }
  543. // OperatorClasses returns a map of assembled operator classes from the given config.
  544. func (conf *Config) OperatorClasses() (map[string]*OperClass, error) {
  545. fixupCapability := func(capab string) string {
  546. return strings.TrimPrefix(capab, "oper:") // #868
  547. }
  548. ocs := make(map[string]*OperClass)
  549. // loop from no extends to most extended, breaking if we can't add any more
  550. lenOfLastOcs := -1
  551. for {
  552. if lenOfLastOcs == len(ocs) {
  553. return nil, ErrOperClassDependencies
  554. }
  555. lenOfLastOcs = len(ocs)
  556. var anyMissing bool
  557. for name, info := range conf.OperClasses {
  558. _, exists := ocs[name]
  559. _, extendsExists := ocs[info.Extends]
  560. if exists {
  561. // class already exists
  562. continue
  563. } else if len(info.Extends) > 0 && !extendsExists {
  564. // class we extend on doesn't exist
  565. _, exists := conf.OperClasses[info.Extends]
  566. if !exists {
  567. return nil, fmt.Errorf("Operclass [%s] extends [%s], which doesn't exist", name, info.Extends)
  568. }
  569. anyMissing = true
  570. continue
  571. }
  572. // create new operclass
  573. var oc OperClass
  574. oc.Capabilities = make(StringSet)
  575. // get inhereted info from other operclasses
  576. if len(info.Extends) > 0 {
  577. einfo := ocs[info.Extends]
  578. for capab := range einfo.Capabilities {
  579. oc.Capabilities.Add(fixupCapability(capab))
  580. }
  581. }
  582. // add our own info
  583. oc.Title = info.Title
  584. for _, capab := range info.Capabilities {
  585. oc.Capabilities.Add(fixupCapability(capab))
  586. }
  587. if len(info.WhoisLine) > 0 {
  588. oc.WhoisLine = info.WhoisLine
  589. } else {
  590. oc.WhoisLine = "is a"
  591. if strings.Contains(strings.ToLower(string(oc.Title[0])), "aeiou") {
  592. oc.WhoisLine += "n"
  593. }
  594. oc.WhoisLine += " "
  595. oc.WhoisLine += oc.Title
  596. }
  597. ocs[name] = &oc
  598. }
  599. if !anyMissing {
  600. // we've got every operclass!
  601. break
  602. }
  603. }
  604. return ocs, nil
  605. }
  606. // Oper represents a single assembled operator's config.
  607. type Oper struct {
  608. Name string
  609. Class *OperClass
  610. WhoisLine string
  611. Vhost string
  612. Pass []byte
  613. Fingerprint string
  614. Auto bool
  615. Modes []modes.ModeChange
  616. }
  617. // Operators returns a map of operator configs from the given OperClass and config.
  618. func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error) {
  619. operators := make(map[string]*Oper)
  620. for name, opConf := range conf.Opers {
  621. var oper Oper
  622. // oper name
  623. name, err := CasefoldName(name)
  624. if err != nil {
  625. return nil, fmt.Errorf("Could not casefold oper name: %s", err.Error())
  626. }
  627. oper.Name = name
  628. if opConf.Password != "" {
  629. oper.Pass, err = decodeLegacyPasswordHash(opConf.Password)
  630. if err != nil {
  631. return nil, fmt.Errorf("Oper %s has an invalid password hash: %s", oper.Name, err.Error())
  632. }
  633. }
  634. if opConf.Fingerprint != "" {
  635. oper.Fingerprint, err = utils.NormalizeCertfp(opConf.Fingerprint)
  636. if err != nil {
  637. return nil, fmt.Errorf("Oper %s has an invalid fingerprint: %s", oper.Name, err.Error())
  638. }
  639. }
  640. oper.Auto = opConf.Auto
  641. if oper.Pass == nil && oper.Fingerprint == "" {
  642. return nil, fmt.Errorf("Oper %s has neither a password nor a fingerprint", name)
  643. }
  644. oper.Vhost = opConf.Vhost
  645. class, exists := oc[opConf.Class]
  646. if !exists {
  647. return nil, fmt.Errorf("Could not load operator [%s] - they use operclass [%s] which does not exist", name, opConf.Class)
  648. }
  649. oper.Class = class
  650. if len(opConf.WhoisLine) > 0 {
  651. oper.WhoisLine = opConf.WhoisLine
  652. } else {
  653. oper.WhoisLine = class.WhoisLine
  654. }
  655. modeStr := strings.TrimSpace(opConf.Modes)
  656. modeChanges, unknownChanges := modes.ParseUserModeChanges(strings.Split(modeStr, " ")...)
  657. if len(unknownChanges) > 0 {
  658. return nil, fmt.Errorf("Could not load operator [%s] due to unknown modes %v", name, unknownChanges)
  659. }
  660. oper.Modes = modeChanges
  661. // successful, attach to list of opers
  662. operators[name] = &oper
  663. }
  664. return operators, nil
  665. }
  666. func loadTlsConfig(config TLSListenConfig) (tlsConfig *tls.Config, err error) {
  667. cert, err := tls.LoadX509KeyPair(config.Cert, config.Key)
  668. if err != nil {
  669. return nil, ErrInvalidCertKeyPair
  670. }
  671. result := tls.Config{
  672. Certificates: []tls.Certificate{cert},
  673. ClientAuth: tls.RequestClientCert,
  674. }
  675. return &result, nil
  676. }
  677. // prepareListeners populates Config.Server.trueListeners
  678. func (conf *Config) prepareListeners() (err error) {
  679. if len(conf.Server.Listeners) == 0 {
  680. return fmt.Errorf("No listeners were configured")
  681. }
  682. conf.Server.trueListeners = make(map[string]listenerConfig)
  683. for addr, block := range conf.Server.Listeners {
  684. var lconf listenerConfig
  685. lconf.Tor = block.Tor
  686. lconf.STSOnly = block.STSOnly
  687. if lconf.STSOnly && !conf.Server.STS.Enabled {
  688. return fmt.Errorf("%s is configured as a STS-only listener, but STS is disabled", addr)
  689. }
  690. if block.TLS.Cert != "" {
  691. tlsConfig, err := loadTlsConfig(block.TLS)
  692. if err != nil {
  693. return err
  694. }
  695. lconf.TLSConfig = tlsConfig
  696. lconf.ProxyBeforeTLS = block.TLS.Proxy
  697. }
  698. conf.Server.trueListeners[addr] = lconf
  699. }
  700. return nil
  701. }
  702. // LoadRawConfig loads the config without doing any consistency checks or postprocessing
  703. func LoadRawConfig(filename string) (config *Config, err error) {
  704. data, err := ioutil.ReadFile(filename)
  705. if err != nil {
  706. return nil, err
  707. }
  708. err = yaml.Unmarshal(data, &config)
  709. if err != nil {
  710. return nil, err
  711. }
  712. return
  713. }
  714. // LoadConfig loads the given YAML configuration file.
  715. func LoadConfig(filename string) (config *Config, err error) {
  716. config, err = LoadRawConfig(filename)
  717. if err != nil {
  718. return nil, err
  719. }
  720. config.Filename = filename
  721. if config.Network.Name == "" {
  722. return nil, ErrNetworkNameMissing
  723. }
  724. if config.Server.Name == "" {
  725. return nil, ErrServerNameMissing
  726. }
  727. if !utils.IsServerName(config.Server.Name) {
  728. return nil, ErrServerNameNotHostname
  729. }
  730. config.Server.nameCasefolded = strings.ToLower(config.Server.Name)
  731. if config.Datastore.Path == "" {
  732. return nil, ErrDatastorePathMissing
  733. }
  734. //dan: automagically fix identlen until a few releases in the future (from now, 0.12.0), being a newly-introduced limit
  735. if config.Limits.IdentLen < 1 {
  736. config.Limits.IdentLen = 20
  737. }
  738. if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
  739. return nil, ErrLimitsAreInsane
  740. }
  741. if config.Limits.RegistrationMessages == 0 {
  742. config.Limits.RegistrationMessages = 1024
  743. }
  744. if config.Datastore.MySQL.Enabled {
  745. if config.Limits.NickLen > mysql.MaxTargetLength || config.Limits.ChannelLen > mysql.MaxTargetLength {
  746. return nil, fmt.Errorf("to use MySQL, nick and channel length limits must be %d or lower", mysql.MaxTargetLength)
  747. }
  748. }
  749. config.Server.supportedCaps = caps.NewCompleteSet()
  750. config.Server.capValues = make(caps.Values)
  751. err = config.prepareListeners()
  752. if err != nil {
  753. return nil, fmt.Errorf("failed to prepare listeners: %v", err)
  754. }
  755. if config.Server.STS.Enabled {
  756. if config.Server.STS.Port < 0 || config.Server.STS.Port > 65535 {
  757. return nil, fmt.Errorf("STS port is incorrect, should be 0 if disabled: %d", config.Server.STS.Port)
  758. }
  759. if config.Server.STS.STSOnlyBanner != "" {
  760. for _, line := range strings.Split(config.Server.STS.STSOnlyBanner, "\n") {
  761. config.Server.STS.bannerLines = append(config.Server.STS.bannerLines, strings.TrimSpace(line))
  762. }
  763. } else {
  764. config.Server.STS.bannerLines = []string{fmt.Sprintf("This server is only accessible over TLS. Please reconnect using TLS on port %d.", config.Server.STS.Port)}
  765. }
  766. } else {
  767. config.Server.supportedCaps.Disable(caps.STS)
  768. config.Server.STS.Duration = 0
  769. }
  770. // set this even if STS is disabled
  771. config.Server.capValues[caps.STS] = config.Server.STS.Value()
  772. config.Server.lookupHostnames = utils.BoolDefaultTrue(config.Server.LookupHostnames)
  773. // process webirc blocks
  774. var newWebIRC []webircConfig
  775. for _, webirc := range config.Server.WebIRC {
  776. // skip webirc blocks with no hosts (such as the example one)
  777. if len(webirc.Hosts) == 0 {
  778. continue
  779. }
  780. err = webirc.Populate()
  781. if err != nil {
  782. return nil, fmt.Errorf("Could not parse WebIRC config: %s", err.Error())
  783. }
  784. newWebIRC = append(newWebIRC, webirc)
  785. }
  786. config.Server.WebIRC = newWebIRC
  787. if config.Limits.Multiline.MaxBytes <= 0 {
  788. config.Server.supportedCaps.Disable(caps.Multiline)
  789. } else {
  790. var multilineCapValue string
  791. if config.Limits.Multiline.MaxLines == 0 {
  792. multilineCapValue = fmt.Sprintf("max-bytes=%d", config.Limits.Multiline.MaxBytes)
  793. } else {
  794. multilineCapValue = fmt.Sprintf("max-bytes=%d,max-lines=%d", config.Limits.Multiline.MaxBytes, config.Limits.Multiline.MaxLines)
  795. }
  796. config.Server.capValues[caps.Multiline] = multilineCapValue
  797. }
  798. // handle legacy name 'bouncer' for 'multiclient' section:
  799. if config.Accounts.Bouncer != nil {
  800. config.Accounts.Multiclient = *config.Accounts.Bouncer
  801. }
  802. if !config.Accounts.Multiclient.Enabled {
  803. config.Accounts.Multiclient.AlwaysOn = PersistentDisabled
  804. } else if config.Accounts.Multiclient.AlwaysOn >= PersistentOptOut {
  805. config.Accounts.Multiclient.AllowedByDefault = true
  806. }
  807. // handle guest format, including the legacy key rename-prefix
  808. if config.Accounts.NickReservation.GuestFormat == "" {
  809. renamePrefix := config.Accounts.NickReservation.RenamePrefix
  810. if renamePrefix == "" {
  811. renamePrefix = "Guest-"
  812. }
  813. config.Accounts.NickReservation.GuestFormat = renamePrefix + "*"
  814. }
  815. config.Accounts.NickReservation.guestRegexp, config.Accounts.NickReservation.guestRegexpFolded, err = compileGuestRegexp(config.Accounts.NickReservation.GuestFormat, config.Server.Casemapping)
  816. if err != nil {
  817. return nil, err
  818. }
  819. var newLogConfigs []logger.LoggingConfig
  820. for _, logConfig := range config.Logging {
  821. // methods
  822. methods := make(map[string]bool)
  823. for _, method := range strings.Split(logConfig.Method, " ") {
  824. if len(method) > 0 {
  825. methods[strings.ToLower(method)] = true
  826. }
  827. }
  828. if methods["file"] && logConfig.Filename == "" {
  829. return nil, ErrLoggerFilenameMissing
  830. }
  831. logConfig.MethodFile = methods["file"]
  832. logConfig.MethodStdout = methods["stdout"]
  833. logConfig.MethodStderr = methods["stderr"]
  834. // levels
  835. level, exists := logger.LogLevelNames[strings.ToLower(logConfig.LevelString)]
  836. if !exists {
  837. return nil, fmt.Errorf("Could not translate log leve [%s]", logConfig.LevelString)
  838. }
  839. logConfig.Level = level
  840. // types
  841. for _, typeStr := range strings.Split(logConfig.TypeString, " ") {
  842. if len(typeStr) == 0 {
  843. continue
  844. }
  845. if typeStr == "-" {
  846. return nil, ErrLoggerExcludeEmpty
  847. }
  848. if typeStr[0] == '-' {
  849. typeStr = typeStr[1:]
  850. logConfig.ExcludedTypes = append(logConfig.ExcludedTypes, typeStr)
  851. } else {
  852. logConfig.Types = append(logConfig.Types, typeStr)
  853. }
  854. }
  855. if len(logConfig.Types) < 1 {
  856. return nil, ErrLoggerHasNoTypes
  857. }
  858. newLogConfigs = append(newLogConfigs, logConfig)
  859. }
  860. config.Logging = newLogConfigs
  861. // hardcode this for now
  862. config.Accounts.Registration.EnabledCredentialTypes = []string{"passphrase", "certfp"}
  863. for i, name := range config.Accounts.Registration.EnabledCallbacks {
  864. if name == "none" {
  865. // we store "none" as "*" internally
  866. config.Accounts.Registration.EnabledCallbacks[i] = "*"
  867. }
  868. }
  869. sort.Strings(config.Accounts.Registration.EnabledCallbacks)
  870. config.Accounts.RequireSasl.exemptedNets, err = utils.ParseNetList(config.Accounts.RequireSasl.Exempted)
  871. if err != nil {
  872. return nil, fmt.Errorf("Could not parse require-sasl exempted nets: %v", err.Error())
  873. }
  874. config.Server.proxyAllowedFromNets, err = utils.ParseNetList(config.Server.ProxyAllowedFrom)
  875. if err != nil {
  876. return nil, fmt.Errorf("Could not parse proxy-allowed-from nets: %v", err.Error())
  877. }
  878. config.Server.secureNets, err = utils.ParseNetList(config.Server.SecureNetDefs)
  879. if err != nil {
  880. return nil, fmt.Errorf("Could not parse secure-nets: %v\n", err.Error())
  881. }
  882. rawRegexp := config.Accounts.VHosts.ValidRegexpRaw
  883. if rawRegexp != "" {
  884. regexp, err := regexp.Compile(rawRegexp)
  885. if err == nil {
  886. config.Accounts.VHosts.ValidRegexp = regexp
  887. } else {
  888. log.Printf("invalid vhost regexp: %s\n", err.Error())
  889. }
  890. }
  891. if config.Accounts.VHosts.ValidRegexp == nil {
  892. config.Accounts.VHosts.ValidRegexp = defaultValidVhostRegex
  893. }
  894. for _, vhost := range config.Accounts.VHosts.OfferList {
  895. if !config.Accounts.VHosts.ValidRegexp.MatchString(vhost) {
  896. return nil, fmt.Errorf("invalid offered vhost: %s", vhost)
  897. }
  898. }
  899. if !config.Accounts.LoginThrottling.Enabled {
  900. config.Accounts.LoginThrottling.MaxAttempts = 0 // limit of 0 means disabled
  901. }
  902. config.Server.capValues[caps.SASL] = "PLAIN,EXTERNAL"
  903. if !config.Accounts.AuthenticationEnabled {
  904. config.Server.supportedCaps.Disable(caps.SASL)
  905. }
  906. maxSendQBytes, err := bytefmt.ToBytes(config.Server.MaxSendQString)
  907. if err != nil {
  908. return nil, fmt.Errorf("Could not parse maximum SendQ size (make sure it only contains whole numbers): %s", err.Error())
  909. }
  910. config.Server.MaxSendQBytes = int(maxSendQBytes)
  911. config.languageManager, err = languages.NewManager(config.Languages.Enabled, config.Languages.Path, config.Languages.Default)
  912. if err != nil {
  913. return nil, fmt.Errorf("Could not load languages: %s", err.Error())
  914. }
  915. config.Server.capValues[caps.Languages] = config.languageManager.CapValue()
  916. config.Debug.recoverFromErrors = utils.BoolDefaultTrue(config.Debug.RecoverFromErrors)
  917. // process operator definitions, store them to config.operators
  918. operclasses, err := config.OperatorClasses()
  919. if err != nil {
  920. return nil, err
  921. }
  922. opers, err := config.Operators(operclasses)
  923. if err != nil {
  924. return nil, err
  925. }
  926. config.operators = opers
  927. // parse default channel modes
  928. config.Channels.defaultModes = ParseDefaultChannelModes(config.Channels.DefaultModes)
  929. if config.Server.Password != "" {
  930. config.Server.passwordBytes, err = decodeLegacyPasswordHash(config.Server.Password)
  931. if err != nil {
  932. return nil, err
  933. }
  934. }
  935. if config.Accounts.Registration.BcryptCost == 0 {
  936. config.Accounts.Registration.BcryptCost = passwd.DefaultCost
  937. }
  938. if config.Channels.MaxChannelsPerClient == 0 {
  939. config.Channels.MaxChannelsPerClient = 100
  940. }
  941. if config.Channels.Registration.MaxChannelsPerAccount == 0 {
  942. config.Channels.Registration.MaxChannelsPerAccount = 15
  943. }
  944. config.Server.Compatibility.forceTrailing = utils.BoolDefaultTrue(config.Server.Compatibility.ForceTrailing)
  945. config.loadMOTD()
  946. // in the current implementation, we disable history by creating a history buffer
  947. // with zero capacity. but the `enabled` config option MUST be respected regardless
  948. // of this detail
  949. if !config.History.Enabled {
  950. config.History.ChannelLength = 0
  951. config.History.ClientLength = 0
  952. }
  953. if !config.History.Enabled || !config.History.Persistent.Enabled {
  954. config.History.Persistent.UnregisteredChannels = false
  955. config.History.Persistent.RegisteredChannels = PersistentDisabled
  956. config.History.Persistent.DirectMessages = PersistentDisabled
  957. }
  958. if config.History.ZNCMax == 0 {
  959. config.History.ZNCMax = config.History.ChathistoryMax
  960. }
  961. config.Roleplay.enabled = utils.BoolDefaultTrue(config.Roleplay.Enabled)
  962. config.Roleplay.addSuffix = utils.BoolDefaultTrue(config.Roleplay.AddSuffix)
  963. config.Datastore.MySQL.ExpireTime = time.Duration(config.History.Restrictions.ExpireTime)
  964. config.Server.Cloaks.Initialize()
  965. if config.Server.Cloaks.Enabled {
  966. if config.Server.Cloaks.Secret == "" || config.Server.Cloaks.Secret == "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4" {
  967. return nil, fmt.Errorf("You must generate a new value of server.ip-cloaking.secret to enable cloaking")
  968. }
  969. if !utils.IsHostname(config.Server.Cloaks.Netname) {
  970. return nil, fmt.Errorf("Invalid netname for cloaked hostnames: %s", config.Server.Cloaks.Netname)
  971. }
  972. }
  973. // now that all postprocessing is complete, regenerate ISUPPORT:
  974. err = config.generateISupport()
  975. if err != nil {
  976. return nil, err
  977. }
  978. err = config.prepareListeners()
  979. if err != nil {
  980. return nil, fmt.Errorf("failed to prepare listeners: %v", err)
  981. }
  982. return config, nil
  983. }
  984. // setISupport sets up our RPL_ISUPPORT reply.
  985. func (config *Config) generateISupport() (err error) {
  986. maxTargetsString := strconv.Itoa(maxTargets)
  987. // add RPL_ISUPPORT tokens
  988. isupport := &config.Server.isupport
  989. isupport.Initialize()
  990. isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen))
  991. isupport.Add("CASEMAPPING", "ascii")
  992. isupport.Add("CHANLIMIT", fmt.Sprintf("%s:%d", chanTypes, config.Channels.MaxChannelsPerClient))
  993. isupport.Add("CHANMODES", strings.Join([]string{modes.Modes{modes.BanMask, modes.ExceptMask, modes.InviteMask}.String(), modes.Modes{modes.Key}.String(), modes.Modes{modes.UserLimit}.String(), modes.Modes{modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.ChanRoleplaying, modes.Secret, modes.NoCTCP, modes.RegisteredOnly}.String()}, ","))
  994. if config.History.Enabled && config.History.ChathistoryMax > 0 {
  995. isupport.Add("draft/CHATHISTORY", strconv.Itoa(config.History.ChathistoryMax))
  996. }
  997. isupport.Add("CHANNELLEN", strconv.Itoa(config.Limits.ChannelLen))
  998. isupport.Add("CHANTYPES", chanTypes)
  999. isupport.Add("ELIST", "U")
  1000. isupport.Add("EXCEPTS", "")
  1001. isupport.Add("INVEX", "")
  1002. isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
  1003. isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
  1004. isupport.Add("MAXTARGETS", maxTargetsString)
  1005. isupport.Add("MODES", "")
  1006. isupport.Add("MONITOR", strconv.Itoa(config.Limits.MonitorEntries))
  1007. isupport.Add("NETWORK", config.Network.Name)
  1008. isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen))
  1009. isupport.Add("PREFIX", "(qaohv)~&@%+")
  1010. if config.Roleplay.enabled {
  1011. isupport.Add("RPCHAN", "E")
  1012. isupport.Add("RPUSER", "E")
  1013. }
  1014. isupport.Add("STATUSMSG", "~&@%+")
  1015. isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString))
  1016. isupport.Add("TOPICLEN", strconv.Itoa(config.Limits.TopicLen))
  1017. if config.Server.Casemapping == CasemappingPRECIS {
  1018. isupport.Add("UTF8MAPPING", precisUTF8MappingToken)
  1019. }
  1020. err = isupport.RegenerateCachedReply()
  1021. return
  1022. }
  1023. // Diff returns changes in supported caps across a rehash.
  1024. func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set) {
  1025. addedCaps = caps.NewSet()
  1026. removedCaps = caps.NewSet()
  1027. if oldConfig == nil {
  1028. return
  1029. }
  1030. if oldConfig.Server.capValues[caps.Languages] != config.Server.capValues[caps.Languages] {
  1031. // XXX updated caps get a DEL line and then a NEW line with the new value
  1032. addedCaps.Add(caps.Languages)
  1033. removedCaps.Add(caps.Languages)
  1034. }
  1035. if !oldConfig.Accounts.AuthenticationEnabled && config.Accounts.AuthenticationEnabled {
  1036. addedCaps.Add(caps.SASL)
  1037. } else if oldConfig.Accounts.AuthenticationEnabled && !config.Accounts.AuthenticationEnabled {
  1038. removedCaps.Add(caps.SASL)
  1039. }
  1040. if oldConfig.Limits.Multiline.MaxBytes != 0 && config.Limits.Multiline.MaxBytes == 0 {
  1041. removedCaps.Add(caps.Multiline)
  1042. } else if oldConfig.Limits.Multiline.MaxBytes == 0 && config.Limits.Multiline.MaxBytes != 0 {
  1043. addedCaps.Add(caps.Multiline)
  1044. } else if oldConfig.Limits.Multiline != config.Limits.Multiline {
  1045. removedCaps.Add(caps.Multiline)
  1046. addedCaps.Add(caps.Multiline)
  1047. }
  1048. if oldConfig.Server.STS.Enabled != config.Server.STS.Enabled || oldConfig.Server.capValues[caps.STS] != config.Server.capValues[caps.STS] {
  1049. // XXX: STS is always removed by CAP NEW sts=duration=0, not CAP DEL
  1050. // so the appropriate notify is always a CAP NEW; put it in addedCaps for any change
  1051. addedCaps.Add(caps.STS)
  1052. }
  1053. return
  1054. }
  1055. func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
  1056. starIndex := strings.IndexByte(guestFormat, '*')
  1057. if starIndex == -1 {
  1058. return nil, nil, errors.New("guest format must contain exactly one *")
  1059. }
  1060. initial := guestFormat[:starIndex]
  1061. final := guestFormat[starIndex+1:]
  1062. if strings.IndexByte(final, '*') != -1 {
  1063. return nil, nil, errors.New("guest format must contain exactly one *")
  1064. }
  1065. standard, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initial, final))
  1066. if err != nil {
  1067. return
  1068. }
  1069. initialFolded, err := casefoldWithSetting(initial, casemapping)
  1070. if err != nil {
  1071. return
  1072. }
  1073. finalFolded, err := casefoldWithSetting(final, casemapping)
  1074. if err != nil {
  1075. return
  1076. }
  1077. folded, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initialFolded, finalFolded))
  1078. return
  1079. }