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.

database.go 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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/base64"
  7. "fmt"
  8. "log"
  9. "os"
  10. "strings"
  11. "github.com/oragono/oragono/irc/passwd"
  12. "github.com/tidwall/buntdb"
  13. )
  14. const (
  15. // 'version' of the database schema
  16. keySchemaVersion = "db.version"
  17. // latest schema of the db
  18. latestDbSchema = "2"
  19. // key for the primary salt used by the ircd
  20. keySalt = "crypto.salt"
  21. )
  22. // InitDB creates the database.
  23. func InitDB(path string) {
  24. // prepare kvstore db
  25. //TODO(dan): fail if already exists instead? don't want to overwrite good data
  26. os.Remove(path)
  27. store, err := buntdb.Open(path)
  28. if err != nil {
  29. log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
  30. }
  31. defer store.Close()
  32. err = store.Update(func(tx *buntdb.Tx) error {
  33. // set base db salt
  34. salt, err := passwd.NewSalt()
  35. encodedSalt := base64.StdEncoding.EncodeToString(salt)
  36. if err != nil {
  37. log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
  38. }
  39. tx.Set(keySalt, encodedSalt, nil)
  40. // set schema version
  41. tx.Set(keySchemaVersion, "2", nil)
  42. return nil
  43. })
  44. if err != nil {
  45. log.Fatal("Could not save datastore:", err.Error())
  46. }
  47. }
  48. // OpenDatabase returns an existing database, performing a schema version check.
  49. func OpenDatabase(path string) (*buntdb.DB, error) {
  50. // open data store
  51. db, err := buntdb.Open(path)
  52. if err != nil {
  53. return nil, err
  54. }
  55. // check db version
  56. err = db.View(func(tx *buntdb.Tx) error {
  57. version, _ := tx.Get(keySchemaVersion)
  58. if version != latestDbSchema {
  59. return fmt.Errorf("Database must be updated. Expected schema v%s, got v%s", latestDbSchema, version)
  60. }
  61. return nil
  62. })
  63. if err != nil {
  64. // close the db
  65. db.Close()
  66. return nil, err
  67. }
  68. return db, nil
  69. }
  70. // UpgradeDB upgrades the datastore to the latest schema.
  71. func UpgradeDB(path string) {
  72. store, err := buntdb.Open(path)
  73. if err != nil {
  74. log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
  75. }
  76. defer store.Close()
  77. err = store.Update(func(tx *buntdb.Tx) error {
  78. version, _ := tx.Get(keySchemaVersion)
  79. // == version 1 -> 2 ==
  80. // account key changes and account.verified key bugfix.
  81. if version == "1" {
  82. log.Println("Updating store v1 to v2")
  83. var keysToRemove []string
  84. newKeys := make(map[string]string)
  85. tx.AscendKeys("account *", func(key, value string) bool {
  86. keysToRemove = append(keysToRemove, key)
  87. splitkey := strings.Split(key, " ")
  88. // work around bug
  89. if splitkey[2] == "exists" {
  90. // manually create new verified key
  91. newVerifiedKey := fmt.Sprintf("%s.verified %s", splitkey[0], splitkey[1])
  92. newKeys[newVerifiedKey] = "1"
  93. } else if splitkey[1] == "%s" {
  94. return true
  95. }
  96. newKey := fmt.Sprintf("%s.%s %s", splitkey[0], splitkey[2], splitkey[1])
  97. newKeys[newKey] = value
  98. return true
  99. })
  100. for _, key := range keysToRemove {
  101. tx.Delete(key)
  102. }
  103. for key, value := range newKeys {
  104. tx.Set(key, value, nil)
  105. }
  106. tx.Set(keySchemaVersion, "2", nil)
  107. }
  108. return nil
  109. })
  110. if err != nil {
  111. log.Fatal("Could not update datastore:", err.Error())
  112. }
  113. return
  114. }