選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

import.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "strconv"
  10. "github.com/tidwall/buntdb"
  11. "github.com/oragono/oragono/irc/utils"
  12. )
  13. type userImport struct {
  14. Name string
  15. Hash string
  16. Email string
  17. RegisteredAt int64 `json:"registeredAt"`
  18. Vhost string
  19. AdditionalNicks []string `json:"additionalNicks"`
  20. }
  21. type channelImport struct {
  22. Name string
  23. Founder string
  24. RegisteredAt int64 `json:"registeredAt"`
  25. Topic string
  26. TopicSetBy string `json:"topicSetBy"`
  27. TopicSetAt int64 `json:"topicSetAt"`
  28. Amode map[string]string
  29. Modes string
  30. Key string
  31. Limit int
  32. }
  33. type databaseImport struct {
  34. Version int
  35. Source string
  36. Users map[string]userImport
  37. Channels map[string]channelImport
  38. }
  39. func serializeAmodes(raw map[string]string) (result []byte, err error) {
  40. processed := make(map[string]int, len(raw))
  41. for accountName, mode := range raw {
  42. if len(mode) != 1 {
  43. return nil, fmt.Errorf("invalid mode %s for account %s", mode, accountName)
  44. }
  45. cfname, err := CasefoldName(accountName)
  46. if err != nil {
  47. return nil, fmt.Errorf("invalid amode recipient %s: %w", accountName, err)
  48. }
  49. processed[cfname] = int(mode[0])
  50. }
  51. result, err = json.Marshal(processed)
  52. return
  53. }
  54. func doImportDBGeneric(config *Config, dbImport databaseImport, credsType CredentialsVersion, tx *buntdb.Tx) (err error) {
  55. requiredVersion := 1
  56. if dbImport.Version != requiredVersion {
  57. return fmt.Errorf("unsupported version of the db for import: version %d is required", requiredVersion)
  58. }
  59. // produce a hardcoded version of the database schema
  60. // XXX instead of referencing, e.g., keyAccountExists, we should write in the string literal
  61. // (to ensure that no matter what code changes happen elsewhere, we're still producing a
  62. // version 14 db)
  63. tx.Set(keySchemaVersion, "14", nil)
  64. tx.Set(keyCloakSecret, utils.GenerateSecretKey(), nil)
  65. for username, userInfo := range dbImport.Users {
  66. cfUsername, err := CasefoldName(username)
  67. if err != nil {
  68. log.Printf("invalid username %s: %v", username, err)
  69. continue
  70. }
  71. credentials := AccountCredentials{
  72. Version: credsType,
  73. PassphraseHash: []byte(userInfo.Hash),
  74. }
  75. marshaledCredentials, err := json.Marshal(&credentials)
  76. if err != nil {
  77. log.Printf("invalid credentials for %s: %v", username, err)
  78. continue
  79. }
  80. tx.Set(fmt.Sprintf(keyAccountExists, cfUsername), "1", nil)
  81. tx.Set(fmt.Sprintf(keyAccountVerified, cfUsername), "1", nil)
  82. tx.Set(fmt.Sprintf(keyAccountName, cfUsername), userInfo.Name, nil)
  83. tx.Set(fmt.Sprintf(keyAccountCallback, cfUsername), "mailto:"+userInfo.Email, nil)
  84. tx.Set(fmt.Sprintf(keyAccountCredentials, cfUsername), string(marshaledCredentials), nil)
  85. tx.Set(fmt.Sprintf(keyAccountRegTime, cfUsername), strconv.FormatInt(userInfo.RegisteredAt, 10), nil)
  86. if userInfo.Vhost != "" {
  87. tx.Set(fmt.Sprintf(keyAccountVHost, cfUsername), userInfo.Vhost, nil)
  88. }
  89. if len(userInfo.AdditionalNicks) != 0 {
  90. tx.Set(fmt.Sprintf(keyAccountAdditionalNicks, cfUsername), marshalReservedNicks(userInfo.AdditionalNicks), nil)
  91. }
  92. }
  93. for chname, chInfo := range dbImport.Channels {
  94. cfchname, err := CasefoldChannel(chname)
  95. if err != nil {
  96. log.Printf("invalid channel name %s: %v", chname, err)
  97. continue
  98. }
  99. cffounder, err := CasefoldName(chInfo.Founder)
  100. if err != nil {
  101. log.Printf("invalid founder %s for channel %s: %v", chInfo.Founder, chname, err)
  102. continue
  103. }
  104. tx.Set(fmt.Sprintf(keyChannelExists, cfchname), "1", nil)
  105. tx.Set(fmt.Sprintf(keyChannelName, cfchname), chname, nil)
  106. tx.Set(fmt.Sprintf(keyChannelRegTime, cfchname), strconv.FormatInt(chInfo.RegisteredAt, 10), nil)
  107. tx.Set(fmt.Sprintf(keyChannelFounder, cfchname), cffounder, nil)
  108. accountChannelsKey := fmt.Sprintf(keyAccountChannels, cffounder)
  109. founderChannels, fcErr := tx.Get(accountChannelsKey)
  110. if fcErr != nil || founderChannels == "" {
  111. founderChannels = cfchname
  112. } else {
  113. founderChannels = fmt.Sprintf("%s,%s", founderChannels, cfchname)
  114. }
  115. tx.Set(accountChannelsKey, founderChannels, nil)
  116. if chInfo.Topic != "" {
  117. tx.Set(fmt.Sprintf(keyChannelTopic, cfchname), chInfo.Topic, nil)
  118. tx.Set(fmt.Sprintf(keyChannelTopicSetTime, cfchname), strconv.FormatInt(chInfo.TopicSetAt, 10), nil)
  119. tx.Set(fmt.Sprintf(keyChannelTopicSetBy, cfchname), chInfo.TopicSetBy, nil)
  120. }
  121. if len(chInfo.Amode) != 0 {
  122. m, err := serializeAmodes(chInfo.Amode)
  123. if err == nil {
  124. tx.Set(fmt.Sprintf(keyChannelAccountToUMode, cfchname), string(m), nil)
  125. } else {
  126. log.Printf("couldn't serialize amodes for %s: %v", chname, err)
  127. }
  128. }
  129. tx.Set(fmt.Sprintf(keyChannelModes, cfchname), chInfo.Modes, nil)
  130. if chInfo.Key != "" {
  131. tx.Set(fmt.Sprintf(keyChannelPassword, cfchname), chInfo.Key, nil)
  132. }
  133. if chInfo.Limit > 0 {
  134. tx.Set(fmt.Sprintf(keyChannelUserLimit, cfchname), strconv.Itoa(chInfo.Limit), nil)
  135. }
  136. }
  137. return nil
  138. }
  139. func doImportDB(config *Config, dbImport databaseImport, tx *buntdb.Tx) (err error) {
  140. switch dbImport.Source {
  141. case "atheme":
  142. return doImportDBGeneric(config, dbImport, CredentialsAtheme, tx)
  143. case "anope":
  144. return doImportDBGeneric(config, dbImport, CredentialsAnope, tx)
  145. default:
  146. return fmt.Errorf("unsupported import source: %s", dbImport.Source)
  147. }
  148. }
  149. func ImportDB(config *Config, infile string) (err error) {
  150. data, err := ioutil.ReadFile(infile)
  151. if err != nil {
  152. return
  153. }
  154. var dbImport databaseImport
  155. err = json.Unmarshal(data, &dbImport)
  156. if err != nil {
  157. return err
  158. }
  159. err = checkDBReadyForInit(config.Datastore.Path)
  160. if err != nil {
  161. return err
  162. }
  163. db, err := buntdb.Open(config.Datastore.Path)
  164. if err != nil {
  165. return err
  166. }
  167. performImport := func(tx *buntdb.Tx) (err error) {
  168. return doImportDB(config, dbImport, tx)
  169. }
  170. return db.Update(performImport)
  171. }