package settings import ( "encoding/binary" "encoding/json" "errors" "fmt" "os" "path/filepath" "reflect" "server/log" "server/web/api/utils" bolt "go.etcd.io/bbolt" ) var dbTorrentsName = []byte("Torrents") type torrentOldDB struct { Name string Magnet string InfoBytes []byte Hash string Size int64 Timestamp int64 } // Migrate from torrserver.db to config.db func Migrate1() { if _, err := os.Lstat(filepath.Join(Path, "torrserver.db")); os.IsNotExist(err) { return } db, err := bolt.Open(filepath.Join(Path, "torrserver.db"), 0o666, nil) if err != nil { return } torrs := make([]*torrentOldDB, 0) err = db.View(func(tx *bolt.Tx) error { tdb := tx.Bucket(dbTorrentsName) if tdb == nil { return nil } c := tdb.Cursor() for h, _ := c.First(); h != nil; h, _ = c.Next() { hdb := tdb.Bucket(h) if hdb != nil { torr := new(torrentOldDB) torr.Hash = string(h) tmp := hdb.Get([]byte("Name")) if tmp == nil { return fmt.Errorf("error load torrent") } torr.Name = string(tmp) tmp = hdb.Get([]byte("Link")) if tmp == nil { return fmt.Errorf("error load torrent") } torr.Magnet = string(tmp) tmp = hdb.Get([]byte("Size")) if tmp == nil { return fmt.Errorf("error load torrent") } torr.Size = b2i(tmp) tmp = hdb.Get([]byte("Timestamp")) if tmp == nil { return fmt.Errorf("error load torrent") } torr.Timestamp = b2i(tmp) torrs = append(torrs, torr) } } return nil }) db.Close() if err == nil && len(torrs) > 0 { for _, torr := range torrs { spec, err := utils.ParseLink(torr.Magnet) if err != nil { continue } title := torr.Name if len(spec.DisplayName) > len(title) { title = spec.DisplayName } log.TLogln("Migrate torrent", torr.Name, torr.Hash, torr.Size) AddTorrent(&TorrentDB{ TorrentSpec: spec, Title: title, Timestamp: torr.Timestamp, Size: torr.Size, }) } } os.Remove(filepath.Join(Path, "torrserver.db")) } func b2i(v []byte) int64 { return int64(binary.BigEndian.Uint64(v)) } /* === Migrate 2 === Migrate 'Settings' and 'Viewed' buckets from BBolt ('config.db') to separate JSON files ('settings.json' and 'viewed.json') 'Torrents' data continues to remain in the BBolt database ('config.db') due to the fact that BLOBs are stored there To make user be able to roll settings back, no data is deleted from 'config.db' file. */ func Migrate2(bboltDB, jsonDB TorrServerDB) error { var err error = nil const XPATH_SETTINGS = "Settings" const NAME_BITTORR = "BitTorr" const XPATH_VIEWED = "Viewed" if BTsets != nil { msg := "Migrate0002 MUST be called before initializing BTSets" log.TLogln(msg) os.Exit(1) } isByteArraysEqualJson := func(a, b []byte) (bool, error) { var objectA interface{} var objectB interface{} var err error = nil if err = json.Unmarshal(a, &objectA); err == nil { if err = json.Unmarshal(b, &objectB); err == nil { return reflect.DeepEqual(objectA, objectB), nil } else { err = fmt.Errorf("error unmashalling B: %s", err.Error()) } } else { err = fmt.Errorf("error unmashalling A: %s", err.Error()) } return false, err } migrateXPath := func(xPath, name string) error { if jsonDB.Get(xPath, name) == nil { bboltDBBlob := bboltDB.Get(xPath, name) if bboltDBBlob != nil { log.TLogln(fmt.Sprintf("Attempting to migrate %s->%s from TDB to JsonDB", xPath, name)) jsonDB.Set(xPath, name, bboltDBBlob) jsonDBBlob := jsonDB.Get(xPath, name) if isEqual, err := isByteArraysEqualJson(bboltDBBlob, jsonDBBlob); err == nil { if isEqual { log.TLogln(fmt.Sprintf("Migrated %s->%s successful", xPath, name)) } else { msg := fmt.Sprintf("Failed to migrate %s->%s TDB to JsonDB: equality check failed", xPath, name) log.TLogln(msg) return errors.New(msg) } } else { msg := fmt.Sprintf("Failed to migrate %s->%s TDB to JsonDB: %s", xPath, name, err) log.TLogln(msg) return errors.New(msg) } } } return nil } if err = migrateXPath(XPATH_SETTINGS, NAME_BITTORR); err != nil { return err } jsonDBViewedNames := jsonDB.List(XPATH_VIEWED) if len(jsonDBViewedNames) <= 0 { bboltDBViewedNames := bboltDB.List(XPATH_VIEWED) if len(bboltDBViewedNames) > 0 { for _, name := range bboltDBViewedNames { err = migrateXPath(XPATH_VIEWED, name) if err != nil { break } } } } return err }