Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

database.go 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
  3. // released under the MIT license
  4. package irc
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "log"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/oragono/oragono/irc/modes"
  14. "github.com/oragono/oragono/irc/utils"
  15. "github.com/tidwall/buntdb"
  16. )
  17. const (
  18. // 'version' of the database schema
  19. keySchemaVersion = "db.version"
  20. // latest schema of the db
  21. latestDbSchema = "12"
  22. keyCloakSecret = "crypto.cloak_secret"
  23. )
  24. type SchemaChanger func(*Config, *buntdb.Tx) error
  25. type SchemaChange struct {
  26. InitialVersion string // the change will take this version
  27. TargetVersion string // and transform it into this version
  28. Changer SchemaChanger
  29. }
  30. // maps an initial version to a schema change capable of upgrading it
  31. var schemaChanges map[string]SchemaChange
  32. // InitDB creates the database, implementing the `oragono initdb` command.
  33. func InitDB(path string) {
  34. _, err := os.Stat(path)
  35. if err == nil {
  36. log.Fatal("Datastore already exists (delete it manually to continue): ", path)
  37. } else if !os.IsNotExist(err) {
  38. log.Fatal("Datastore path is inaccessible: ", err.Error())
  39. }
  40. err = initializeDB(path)
  41. if err != nil {
  42. log.Fatal("Could not save datastore: ", err.Error())
  43. }
  44. }
  45. // internal database initialization code
  46. func initializeDB(path string) error {
  47. store, err := buntdb.Open(path)
  48. if err != nil {
  49. return err
  50. }
  51. defer store.Close()
  52. err = store.Update(func(tx *buntdb.Tx) error {
  53. // set schema version
  54. tx.Set(keySchemaVersion, latestDbSchema, nil)
  55. tx.Set(keyCloakSecret, utils.GenerateSecretKey(), nil)
  56. return nil
  57. })
  58. return err
  59. }
  60. // OpenDatabase returns an existing database, performing a schema version check.
  61. func OpenDatabase(config *Config) (*buntdb.DB, error) {
  62. return openDatabaseInternal(config, config.Datastore.AutoUpgrade)
  63. }
  64. // open the database, giving it at most one chance to auto-upgrade the schema
  65. func openDatabaseInternal(config *Config, allowAutoupgrade bool) (db *buntdb.DB, err error) {
  66. db, err = buntdb.Open(config.Datastore.Path)
  67. if err != nil {
  68. return
  69. }
  70. defer func() {
  71. if err != nil && db != nil {
  72. db.Close()
  73. db = nil
  74. }
  75. }()
  76. // read the current version string
  77. var version string
  78. err = db.View(func(tx *buntdb.Tx) error {
  79. version, err = tx.Get(keySchemaVersion)
  80. return err
  81. })
  82. if err != nil {
  83. return
  84. }
  85. if version == latestDbSchema {
  86. // success
  87. return
  88. }
  89. // XXX quiesce the DB so we can be sure it's safe to make a backup copy
  90. db.Close()
  91. db = nil
  92. if allowAutoupgrade {
  93. err = performAutoUpgrade(version, config)
  94. if err != nil {
  95. return
  96. }
  97. // successful autoupgrade, let's try this again:
  98. return openDatabaseInternal(config, false)
  99. } else {
  100. err = &utils.IncompatibleSchemaError{CurrentVersion: version, RequiredVersion: latestDbSchema}
  101. return
  102. }
  103. }
  104. func performAutoUpgrade(currentVersion string, config *Config) (err error) {
  105. path := config.Datastore.Path
  106. log.Printf("attempting to auto-upgrade schema from version %s to %s\n", currentVersion, latestDbSchema)
  107. timestamp := time.Now().UTC().Format("2006-01-02-15:04:05.000Z")
  108. backupPath := fmt.Sprintf("%s.v%s.%s.bak", path, currentVersion, timestamp)
  109. log.Printf("making a backup of current database at %s\n", backupPath)
  110. err = utils.CopyFile(path, backupPath)
  111. if err != nil {
  112. return err
  113. }
  114. err = UpgradeDB(config)
  115. if err != nil {
  116. // database upgrade is a single transaction, so we don't need to restore the backup;
  117. // we can just delete it
  118. os.Remove(backupPath)
  119. }
  120. return err
  121. }
  122. // UpgradeDB upgrades the datastore to the latest schema.
  123. func UpgradeDB(config *Config) (err error) {
  124. // #715: test that the database exists
  125. _, err = os.Stat(config.Datastore.Path)
  126. if err != nil {
  127. return err
  128. }
  129. store, err := buntdb.Open(config.Datastore.Path)
  130. if err != nil {
  131. return err
  132. }
  133. defer store.Close()
  134. var version string
  135. err = store.Update(func(tx *buntdb.Tx) error {
  136. for {
  137. version, _ = tx.Get(keySchemaVersion)
  138. change, schemaNeedsChange := schemaChanges[version]
  139. if !schemaNeedsChange {
  140. if version == latestDbSchema {
  141. // success!
  142. break
  143. }
  144. // unable to upgrade to the desired version, roll back
  145. return &utils.IncompatibleSchemaError{CurrentVersion: version, RequiredVersion: latestDbSchema}
  146. }
  147. log.Println("attempting to update schema from version " + version)
  148. err := change.Changer(config, tx)
  149. if err != nil {
  150. return err
  151. }
  152. _, _, err = tx.Set(keySchemaVersion, change.TargetVersion, nil)
  153. if err != nil {
  154. return err
  155. }
  156. log.Println("successfully updated schema to version " + change.TargetVersion)
  157. }
  158. return nil
  159. })
  160. if err != nil {
  161. log.Printf("database upgrade failed and was rolled back: %v\n", err)
  162. }
  163. return err
  164. }
  165. func LoadCloakSecret(db *buntdb.DB) (result string) {
  166. db.View(func(tx *buntdb.Tx) error {
  167. result, _ = tx.Get(keyCloakSecret)
  168. return nil
  169. })
  170. return
  171. }
  172. func StoreCloakSecret(db *buntdb.DB, secret string) {
  173. db.Update(func(tx *buntdb.Tx) error {
  174. tx.Set(keyCloakSecret, secret, nil)
  175. return nil
  176. })
  177. }
  178. func schemaChangeV1toV2(config *Config, tx *buntdb.Tx) error {
  179. // == version 1 -> 2 ==
  180. // account key changes and account.verified key bugfix.
  181. var keysToRemove []string
  182. newKeys := make(map[string]string)
  183. tx.AscendKeys("account *", func(key, value string) bool {
  184. keysToRemove = append(keysToRemove, key)
  185. splitkey := strings.Split(key, " ")
  186. // work around bug
  187. if splitkey[2] == "exists" {
  188. // manually create new verified key
  189. newVerifiedKey := fmt.Sprintf("%s.verified %s", splitkey[0], splitkey[1])
  190. newKeys[newVerifiedKey] = "1"
  191. } else if splitkey[1] == "%s" {
  192. return true
  193. }
  194. newKey := fmt.Sprintf("%s.%s %s", splitkey[0], splitkey[2], splitkey[1])
  195. newKeys[newKey] = value
  196. return true
  197. })
  198. for _, key := range keysToRemove {
  199. tx.Delete(key)
  200. }
  201. for key, value := range newKeys {
  202. tx.Set(key, value, nil)
  203. }
  204. return nil
  205. }
  206. // 1. channel founder names should be casefolded
  207. // 2. founder should be explicitly granted the ChannelFounder user mode
  208. // 3. explicitly initialize stored channel modes to the server default values
  209. func schemaChangeV2ToV3(config *Config, tx *buntdb.Tx) error {
  210. var channels []string
  211. prefix := "channel.exists "
  212. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  213. if !strings.HasPrefix(key, prefix) {
  214. return false
  215. }
  216. chname := strings.TrimPrefix(key, prefix)
  217. channels = append(channels, chname)
  218. return true
  219. })
  220. // founder names should be casefolded
  221. // founder should be explicitly granted the ChannelFounder user mode
  222. for _, channel := range channels {
  223. founderKey := "channel.founder " + channel
  224. founder, _ := tx.Get(founderKey)
  225. if founder != "" {
  226. founder, err := CasefoldName(founder)
  227. if err == nil {
  228. tx.Set(founderKey, founder, nil)
  229. accountToUmode := map[string]modes.Mode{
  230. founder: modes.ChannelFounder,
  231. }
  232. atustr, _ := json.Marshal(accountToUmode)
  233. tx.Set("channel.accounttoumode "+channel, string(atustr), nil)
  234. }
  235. }
  236. }
  237. // explicitly store the channel modes
  238. defaultModes := config.Channels.defaultModes
  239. modeStrings := make([]string, len(defaultModes))
  240. for i, mode := range defaultModes {
  241. modeStrings[i] = string(mode)
  242. }
  243. defaultModeString := strings.Join(modeStrings, "")
  244. for _, channel := range channels {
  245. tx.Set("channel.modes "+channel, defaultModeString, nil)
  246. }
  247. return nil
  248. }
  249. // 1. ban info format changed (from `legacyBanInfo` below to `IPBanInfo`)
  250. // 2. dlines against individual IPs are normalized into dlines against the appropriate /128 network
  251. func schemaChangeV3ToV4(config *Config, tx *buntdb.Tx) error {
  252. type ipRestrictTime struct {
  253. Duration time.Duration
  254. Expires time.Time
  255. }
  256. type legacyBanInfo struct {
  257. Reason string `json:"reason"`
  258. OperReason string `json:"oper_reason"`
  259. OperName string `json:"oper_name"`
  260. Time *ipRestrictTime `json:"time"`
  261. }
  262. now := time.Now()
  263. legacyToNewInfo := func(old legacyBanInfo) (new_ IPBanInfo) {
  264. new_.Reason = old.Reason
  265. new_.OperReason = old.OperReason
  266. new_.OperName = old.OperName
  267. if old.Time == nil {
  268. new_.TimeCreated = now
  269. new_.Duration = 0
  270. } else {
  271. new_.TimeCreated = old.Time.Expires.Add(-1 * old.Time.Duration)
  272. new_.Duration = old.Time.Duration
  273. }
  274. return
  275. }
  276. var keysToDelete []string
  277. prefix := "bans.dline "
  278. dlines := make(map[string]IPBanInfo)
  279. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  280. if !strings.HasPrefix(key, prefix) {
  281. return false
  282. }
  283. keysToDelete = append(keysToDelete, key)
  284. var lbinfo legacyBanInfo
  285. id := strings.TrimPrefix(key, prefix)
  286. err := json.Unmarshal([]byte(value), &lbinfo)
  287. if err != nil {
  288. log.Printf("error unmarshaling legacy dline: %v\n", err)
  289. return true
  290. }
  291. // legacy keys can be either an IP or a CIDR
  292. hostNet, err := utils.NormalizedNetFromString(id)
  293. if err != nil {
  294. log.Printf("error unmarshaling legacy dline network: %v\n", err)
  295. return true
  296. }
  297. dlines[utils.NetToNormalizedString(hostNet)] = legacyToNewInfo(lbinfo)
  298. return true
  299. })
  300. setOptions := func(info IPBanInfo) *buntdb.SetOptions {
  301. if info.Duration == 0 {
  302. return nil
  303. }
  304. ttl := info.TimeCreated.Add(info.Duration).Sub(now)
  305. return &buntdb.SetOptions{Expires: true, TTL: ttl}
  306. }
  307. // store the new dlines
  308. for id, info := range dlines {
  309. b, err := json.Marshal(info)
  310. if err != nil {
  311. log.Printf("error marshaling migrated dline: %v\n", err)
  312. continue
  313. }
  314. tx.Set(fmt.Sprintf("bans.dlinev2 %s", id), string(b), setOptions(info))
  315. }
  316. // same operations against klines
  317. prefix = "bans.kline "
  318. klines := make(map[string]IPBanInfo)
  319. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  320. if !strings.HasPrefix(key, prefix) {
  321. return false
  322. }
  323. keysToDelete = append(keysToDelete, key)
  324. mask := strings.TrimPrefix(key, prefix)
  325. var lbinfo legacyBanInfo
  326. err := json.Unmarshal([]byte(value), &lbinfo)
  327. if err != nil {
  328. log.Printf("error unmarshaling legacy kline: %v\n", err)
  329. return true
  330. }
  331. klines[mask] = legacyToNewInfo(lbinfo)
  332. return true
  333. })
  334. for mask, info := range klines {
  335. b, err := json.Marshal(info)
  336. if err != nil {
  337. log.Printf("error marshaling migrated kline: %v\n", err)
  338. continue
  339. }
  340. tx.Set(fmt.Sprintf("bans.klinev2 %s", mask), string(b), setOptions(info))
  341. }
  342. // clean up all the old entries
  343. for _, key := range keysToDelete {
  344. tx.Delete(key)
  345. }
  346. return nil
  347. }
  348. // create new key tracking channels that belong to an account
  349. func schemaChangeV4ToV5(config *Config, tx *buntdb.Tx) error {
  350. founderToChannels := make(map[string][]string)
  351. prefix := "channel.founder "
  352. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  353. if !strings.HasPrefix(key, prefix) {
  354. return false
  355. }
  356. channel := strings.TrimPrefix(key, prefix)
  357. founderToChannels[value] = append(founderToChannels[value], channel)
  358. return true
  359. })
  360. for founder, channels := range founderToChannels {
  361. tx.Set(fmt.Sprintf("account.channels %s", founder), strings.Join(channels, ","), nil)
  362. }
  363. return nil
  364. }
  365. // custom nick enforcement was a separate db key, now it's part of settings
  366. func schemaChangeV5ToV6(config *Config, tx *buntdb.Tx) error {
  367. accountToEnforcement := make(map[string]NickEnforcementMethod)
  368. prefix := "account.customenforcement "
  369. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  370. if !strings.HasPrefix(key, prefix) {
  371. return false
  372. }
  373. account := strings.TrimPrefix(key, prefix)
  374. method, err := nickReservationFromString(value)
  375. if err == nil {
  376. accountToEnforcement[account] = method
  377. } else {
  378. log.Printf("skipping corrupt custom enforcement value for %s\n", account)
  379. }
  380. return true
  381. })
  382. for account, method := range accountToEnforcement {
  383. var settings AccountSettings
  384. settings.NickEnforcement = method
  385. text, err := json.Marshal(settings)
  386. if err != nil {
  387. return err
  388. }
  389. tx.Delete(prefix + account)
  390. tx.Set(fmt.Sprintf("account.settings %s", account), string(text), nil)
  391. }
  392. return nil
  393. }
  394. type maskInfoV7 struct {
  395. TimeCreated time.Time
  396. CreatorNickmask string
  397. CreatorAccount string
  398. }
  399. func schemaChangeV6ToV7(config *Config, tx *buntdb.Tx) error {
  400. now := time.Now().UTC()
  401. var channels []string
  402. prefix := "channel.exists "
  403. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  404. if !strings.HasPrefix(key, prefix) {
  405. return false
  406. }
  407. channels = append(channels, strings.TrimPrefix(key, prefix))
  408. return true
  409. })
  410. converter := func(key string) {
  411. oldRawValue, err := tx.Get(key)
  412. if err != nil {
  413. return
  414. }
  415. var masks []string
  416. err = json.Unmarshal([]byte(oldRawValue), &masks)
  417. if err != nil {
  418. return
  419. }
  420. newCookedValue := make(map[string]maskInfoV7)
  421. for _, mask := range masks {
  422. normalizedMask, err := CanonicalizeMaskWildcard(mask)
  423. if err != nil {
  424. continue
  425. }
  426. newCookedValue[normalizedMask] = maskInfoV7{
  427. TimeCreated: now,
  428. CreatorNickmask: "*",
  429. CreatorAccount: "*",
  430. }
  431. }
  432. newRawValue, err := json.Marshal(newCookedValue)
  433. if err != nil {
  434. return
  435. }
  436. tx.Set(key, string(newRawValue), nil)
  437. }
  438. prefixes := []string{
  439. "channel.banlist %s",
  440. "channel.exceptlist %s",
  441. "channel.invitelist %s",
  442. }
  443. for _, channel := range channels {
  444. for _, prefix := range prefixes {
  445. converter(fmt.Sprintf(prefix, channel))
  446. }
  447. }
  448. return nil
  449. }
  450. type accountSettingsLegacyV7 struct {
  451. AutoreplayLines *int
  452. NickEnforcement NickEnforcementMethod
  453. AllowBouncer MulticlientAllowedSetting
  454. AutoreplayJoins bool
  455. }
  456. type accountSettingsLegacyV8 struct {
  457. AutoreplayLines *int
  458. NickEnforcement NickEnforcementMethod
  459. AllowBouncer MulticlientAllowedSetting
  460. ReplayJoins ReplayJoinsSetting
  461. }
  462. // #616: change autoreplay-joins to replay-joins
  463. func schemaChangeV7ToV8(config *Config, tx *buntdb.Tx) error {
  464. prefix := "account.settings "
  465. var accounts, blobs []string
  466. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  467. var legacy accountSettingsLegacyV7
  468. var current accountSettingsLegacyV8
  469. if !strings.HasPrefix(key, prefix) {
  470. return false
  471. }
  472. account := strings.TrimPrefix(key, prefix)
  473. err := json.Unmarshal([]byte(value), &legacy)
  474. if err != nil {
  475. log.Printf("corrupt record for %s: %v\n", account, err)
  476. return true
  477. }
  478. current.AutoreplayLines = legacy.AutoreplayLines
  479. current.NickEnforcement = legacy.NickEnforcement
  480. current.AllowBouncer = legacy.AllowBouncer
  481. if legacy.AutoreplayJoins {
  482. current.ReplayJoins = ReplayJoinsAlways
  483. } else {
  484. current.ReplayJoins = ReplayJoinsCommandsOnly
  485. }
  486. blob, err := json.Marshal(current)
  487. if err != nil {
  488. log.Printf("could not marshal record for %s: %v\n", account, err)
  489. return true
  490. }
  491. accounts = append(accounts, account)
  492. blobs = append(blobs, string(blob))
  493. return true
  494. })
  495. for i, account := range accounts {
  496. tx.Set(prefix+account, blobs[i], nil)
  497. }
  498. return nil
  499. }
  500. type accountCredsLegacyV8 struct {
  501. Version uint
  502. PassphraseSalt []byte // legacy field, not used by v1 and later
  503. PassphraseHash []byte
  504. Certificate string
  505. }
  506. type accountCredsLegacyV9 struct {
  507. Version uint
  508. PassphraseSalt []byte // legacy field, not used by v1 and later
  509. PassphraseHash []byte
  510. Certfps []string
  511. }
  512. // #530: support multiple client certificate fingerprints
  513. func schemaChangeV8ToV9(config *Config, tx *buntdb.Tx) error {
  514. prefix := "account.credentials "
  515. var accounts, blobs []string
  516. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  517. var legacy accountCredsLegacyV8
  518. var current accountCredsLegacyV9
  519. if !strings.HasPrefix(key, prefix) {
  520. return false
  521. }
  522. account := strings.TrimPrefix(key, prefix)
  523. err := json.Unmarshal([]byte(value), &legacy)
  524. if err != nil {
  525. log.Printf("corrupt record for %s: %v\n", account, err)
  526. return true
  527. }
  528. current.Version = legacy.Version
  529. current.PassphraseSalt = legacy.PassphraseSalt // ugh can't get rid of this
  530. current.PassphraseHash = legacy.PassphraseHash
  531. if legacy.Certificate != "" {
  532. current.Certfps = []string{legacy.Certificate}
  533. }
  534. blob, err := json.Marshal(current)
  535. if err != nil {
  536. log.Printf("could not marshal record for %s: %v\n", account, err)
  537. return true
  538. }
  539. accounts = append(accounts, account)
  540. blobs = append(blobs, string(blob))
  541. return true
  542. })
  543. for i, account := range accounts {
  544. tx.Set(prefix+account, blobs[i], nil)
  545. }
  546. return nil
  547. }
  548. // #836: account registration time at nanosecond resolution
  549. // (mostly to simplify testing)
  550. func schemaChangeV9ToV10(config *Config, tx *buntdb.Tx) error {
  551. prefix := "account.registered.time "
  552. var accounts, times []string
  553. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  554. if !strings.HasPrefix(key, prefix) {
  555. return false
  556. }
  557. account := strings.TrimPrefix(key, prefix)
  558. accounts = append(accounts, account)
  559. times = append(times, value)
  560. return true
  561. })
  562. for i, account := range accounts {
  563. time, err := strconv.ParseInt(times[i], 10, 64)
  564. if err != nil {
  565. log.Printf("corrupt registration time entry for %s: %v\n", account, err)
  566. continue
  567. }
  568. time = time * 1000000000
  569. tx.Set(prefix+account, strconv.FormatInt(time, 10), nil)
  570. }
  571. return nil
  572. }
  573. // #952: move the cloak secret into the database,
  574. // generate a new one if necessary
  575. func schemaChangeV10ToV11(config *Config, tx *buntdb.Tx) error {
  576. cloakSecret := config.Server.Cloaks.LegacySecretValue
  577. if cloakSecret == "" || cloakSecret == "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4" {
  578. cloakSecret = utils.GenerateSecretKey()
  579. }
  580. _, _, err := tx.Set(keyCloakSecret, cloakSecret, nil)
  581. return err
  582. }
  583. // #1027: NickEnforcementTimeout (2) was removed,
  584. // NickEnforcementStrict was 3 and is now 2
  585. func schemaChangeV11ToV12(config *Config, tx *buntdb.Tx) error {
  586. prefix := "account.settings "
  587. var accounts, rawSettings []string
  588. tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
  589. if !strings.HasPrefix(key, prefix) {
  590. return false
  591. }
  592. account := strings.TrimPrefix(key, prefix)
  593. accounts = append(accounts, account)
  594. rawSettings = append(rawSettings, value)
  595. return true
  596. })
  597. for i, account := range accounts {
  598. var settings AccountSettings
  599. err := json.Unmarshal([]byte(rawSettings[i]), &settings)
  600. if err != nil {
  601. log.Printf("corrupt account settings entry for %s: %v\n", account, err)
  602. continue
  603. }
  604. // upgrade NickEnforcementTimeout (which was 2) to NickEnforcementStrict (currently 2),
  605. // fix up the old value of NickEnforcementStrict (3) to the current value (2)
  606. if int(settings.NickEnforcement) == 3 {
  607. settings.NickEnforcement = NickEnforcementMethod(2)
  608. text, err := json.Marshal(settings)
  609. if err != nil {
  610. return err
  611. }
  612. tx.Set(prefix+account, string(text), nil)
  613. }
  614. }
  615. return nil
  616. }
  617. func init() {
  618. allChanges := []SchemaChange{
  619. {
  620. InitialVersion: "1",
  621. TargetVersion: "2",
  622. Changer: schemaChangeV1toV2,
  623. },
  624. {
  625. InitialVersion: "2",
  626. TargetVersion: "3",
  627. Changer: schemaChangeV2ToV3,
  628. },
  629. {
  630. InitialVersion: "3",
  631. TargetVersion: "4",
  632. Changer: schemaChangeV3ToV4,
  633. },
  634. {
  635. InitialVersion: "4",
  636. TargetVersion: "5",
  637. Changer: schemaChangeV4ToV5,
  638. },
  639. {
  640. InitialVersion: "5",
  641. TargetVersion: "6",
  642. Changer: schemaChangeV5ToV6,
  643. },
  644. {
  645. InitialVersion: "6",
  646. TargetVersion: "7",
  647. Changer: schemaChangeV6ToV7,
  648. },
  649. {
  650. InitialVersion: "7",
  651. TargetVersion: "8",
  652. Changer: schemaChangeV7ToV8,
  653. },
  654. {
  655. InitialVersion: "8",
  656. TargetVersion: "9",
  657. Changer: schemaChangeV8ToV9,
  658. },
  659. {
  660. InitialVersion: "9",
  661. TargetVersion: "10",
  662. Changer: schemaChangeV9ToV10,
  663. },
  664. {
  665. InitialVersion: "10",
  666. TargetVersion: "11",
  667. Changer: schemaChangeV10ToV11,
  668. },
  669. {
  670. InitialVersion: "11",
  671. TargetVersion: "12",
  672. Changer: schemaChangeV11ToV12,
  673. },
  674. }
  675. // build the index
  676. schemaChanges = make(map[string]SchemaChange)
  677. for _, change := range allChanges {
  678. schemaChanges[change.InitialVersion] = change
  679. }
  680. }