123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- // Copyright (C) 2020 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package db
- import (
- "fmt"
- "strings"
- "time"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/types/known/timestamppb"
- "github.com/syncthing/syncthing/internal/gen/dbproto"
- "github.com/syncthing/syncthing/lib/protocol"
- )
- type ObservedDB struct {
- kv KV
- }
- func NewObservedDB(kv KV) *ObservedDB {
- return &ObservedDB{kv: kv}
- }
- type ObservedFolder struct {
- Time time.Time `json:"time"`
- Label string `json:"label"`
- ReceiveEncrypted bool `json:"receiveEncrypted"`
- RemoteEncrypted bool `json:"remoteEncrypted"`
- }
- func (o *ObservedFolder) toWire() *dbproto.ObservedFolder {
- return &dbproto.ObservedFolder{
- Time: timestamppb.New(o.Time),
- Label: o.Label,
- ReceiveEncrypted: o.ReceiveEncrypted,
- RemoteEncrypted: o.RemoteEncrypted,
- }
- }
- func (o *ObservedFolder) fromWire(w *dbproto.ObservedFolder) {
- o.Time = w.GetTime().AsTime()
- o.Label = w.GetLabel()
- o.ReceiveEncrypted = w.GetReceiveEncrypted()
- o.RemoteEncrypted = w.GetRemoteEncrypted()
- }
- type ObservedDevice struct {
- Time time.Time `json:"time"`
- Name string `json:"name"`
- Address string `json:"address"`
- }
- func (o *ObservedDevice) fromWire(w *dbproto.ObservedDevice) {
- o.Time = w.GetTime().AsTime()
- o.Name = w.GetName()
- o.Address = w.GetAddress()
- }
- func (db *ObservedDB) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) error {
- key := "device/" + device.String()
- od := &dbproto.ObservedDevice{
- Time: timestamppb.New(time.Now().Truncate(time.Second)),
- Name: name,
- Address: address,
- }
- return db.kv.PutKV(key, mustMarshal(od))
- }
- func (db *ObservedDB) RemovePendingDevice(device protocol.DeviceID) error {
- key := "device/" + device.String()
- return db.kv.DeleteKV(key)
- }
- // PendingDevices enumerates all entries. Invalid ones are dropped from the database
- // after a warning log message, as a side-effect.
- func (db *ObservedDB) PendingDevices() (map[protocol.DeviceID]ObservedDevice, error) {
- res := make(map[protocol.DeviceID]ObservedDevice)
- it, errFn := db.kv.PrefixKV("device/")
- for kv := range it {
- _, keyDev, ok := strings.Cut(kv.Key, "/")
- if !ok {
- if err := db.kv.DeleteKV(kv.Key); err != nil {
- return nil, fmt.Errorf("delete invalid pending device: %w", err)
- }
- continue
- }
- deviceID, err := protocol.DeviceIDFromString(keyDev)
- var protoD dbproto.ObservedDevice
- var od ObservedDevice
- if err != nil {
- goto deleteKey
- }
- if err = proto.Unmarshal(kv.Value, &protoD); err != nil {
- goto deleteKey
- }
- od.fromWire(&protoD)
- res[deviceID] = od
- continue
- deleteKey:
- // Deleting invalid entries is the only possible "repair" measure and
- // appropriate for the importance of pending entries. They will come back
- // soon if still relevant.
- if err := db.kv.DeleteKV(kv.Key); err != nil {
- return nil, fmt.Errorf("delete invalid pending device: %w", err)
- }
- }
- return res, errFn()
- }
- func (db *ObservedDB) AddOrUpdatePendingFolder(id string, of ObservedFolder, device protocol.DeviceID) error {
- key := "folder/" + device.String() + "/" + id
- return db.kv.PutKV(key, mustMarshal(of.toWire()))
- }
- // RemovePendingFolderForDevice removes entries for specific folder / device combinations.
- func (db *ObservedDB) RemovePendingFolderForDevice(id string, device protocol.DeviceID) error {
- key := "folder/" + device.String() + "/" + id
- return db.kv.DeleteKV(key)
- }
- // RemovePendingFolder removes all entries matching a specific folder ID.
- func (db *ObservedDB) RemovePendingFolder(id string) error {
- it, errFn := db.kv.PrefixKV("folder/")
- for kv := range it {
- parts := strings.Split(kv.Key, "/")
- if len(parts) != 3 || parts[2] != id {
- continue
- }
- if err := db.kv.DeleteKV(kv.Key); err != nil {
- return fmt.Errorf("delete pending folder: %w", err)
- }
- }
- return errFn()
- }
- // Consolidated information about a pending folder
- type PendingFolder struct {
- OfferedBy map[protocol.DeviceID]ObservedFolder `json:"offeredBy"`
- }
- func (db *ObservedDB) PendingFolders() (map[string]PendingFolder, error) {
- return db.PendingFoldersForDevice(protocol.EmptyDeviceID)
- }
- // PendingFoldersForDevice enumerates only entries matching the given device ID, unless it
- // is EmptyDeviceID. Invalid ones are dropped from the database after a info log
- // message, as a side-effect.
- func (db *ObservedDB) PendingFoldersForDevice(device protocol.DeviceID) (map[string]PendingFolder, error) {
- prefix := "folder/"
- if device != protocol.EmptyDeviceID {
- prefix += device.String() + "/"
- }
- res := make(map[string]PendingFolder)
- it, errFn := db.kv.PrefixKV(prefix)
- for kv := range it {
- parts := strings.Split(kv.Key, "/")
- if len(parts) != 3 {
- continue
- }
- keyDev := parts[1]
- deviceID, err := protocol.DeviceIDFromString(keyDev)
- var protoF dbproto.ObservedFolder
- var of ObservedFolder
- var folderID string
- if err != nil {
- goto deleteKey
- }
- if folderID = parts[2]; len(folderID) < 1 {
- goto deleteKey
- }
- if err = proto.Unmarshal(kv.Value, &protoF); err != nil {
- goto deleteKey
- }
- if _, ok := res[folderID]; !ok {
- res[folderID] = PendingFolder{
- OfferedBy: map[protocol.DeviceID]ObservedFolder{},
- }
- }
- of.fromWire(&protoF)
- res[folderID].OfferedBy[deviceID] = of
- continue
- deleteKey:
- // Deleting invalid entries is the only possible "repair" measure and
- // appropriate for the importance of pending entries. They will come back
- // soon if still relevant.
- if err := db.kv.DeleteKV(kv.Key); err != nil {
- return nil, fmt.Errorf("delete invalid pending folder: %w", err)
- }
- }
- return res, errFn()
- }
- func mustMarshal(m proto.Message) []byte {
- bs, err := proto.Marshal(m)
- if err != nil {
- panic(err)
- }
- return bs
- }
|