| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 | 
							- // 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 contract
 
- import (
 
- 	"database/sql/driver"
 
- 	"encoding/json"
 
- 	"errors"
 
- 	"reflect"
 
- 	"strconv"
 
- 	"time"
 
- 	"github.com/syncthing/syncthing/lib/util"
 
- )
 
- type Report struct {
 
- 	// Generated
 
- 	Received time.Time `json:"-"` // Only from DB
 
- 	Date     string    `json:"date,omitempty"`
 
- 	Address  string    `json:"address,omitempty"`
 
- 	// v1 fields
 
- 	UniqueID       string  `json:"uniqueID,omitempty" since:"1"`
 
- 	Version        string  `json:"version,omitempty" since:"1"`
 
- 	LongVersion    string  `json:"longVersion,omitempty" since:"1"`
 
- 	Platform       string  `json:"platform,omitempty" since:"1"`
 
- 	NumFolders     int     `json:"numFolders,omitempty" since:"1"`
 
- 	NumDevices     int     `json:"numDevices,omitempty" since:"1"`
 
- 	TotFiles       int     `json:"totFiles,omitempty" since:"1"`
 
- 	FolderMaxFiles int     `json:"folderMaxFiles,omitempty" since:"1"`
 
- 	TotMiB         int     `json:"totMiB,omitempty" since:"1"`
 
- 	FolderMaxMiB   int     `json:"folderMaxMiB,omitempty" since:"1"`
 
- 	MemoryUsageMiB int     `json:"memoryUsageMiB,omitempty" since:"1"`
 
- 	SHA256Perf     float64 `json:"sha256Perf,omitempty" since:"1"`
 
- 	HashPerf       float64 `json:"hashPerf,omitempty" since:"1"` // Was previously not stored server-side
 
- 	MemorySize     int     `json:"memorySize,omitempty" since:"1"`
 
- 	// v2 fields
 
- 	URVersion  int `json:"urVersion,omitempty" since:"2"`
 
- 	NumCPU     int `json:"numCPU,omitempty" since:"2"`
 
- 	FolderUses struct {
 
- 		SendOnly            int `json:"sendonly,omitempty" since:"2"`
 
- 		SendReceive         int `json:"sendreceive,omitempty" since:"2"` // Was previously not stored server-side
 
- 		ReceiveOnly         int `json:"receiveonly,omitempty" since:"2"`
 
- 		IgnorePerms         int `json:"ignorePerms,omitempty" since:"2"`
 
- 		IgnoreDelete        int `json:"ignoreDelete,omitempty" since:"2"`
 
- 		AutoNormalize       int `json:"autoNormalize,omitempty" since:"2"`
 
- 		SimpleVersioning    int `json:"simpleVersioning,omitempty" since:"2"`
 
- 		ExternalVersioning  int `json:"externalVersioning,omitempty" since:"2"`
 
- 		StaggeredVersioning int `json:"staggeredVersioning,omitempty" since:"2"`
 
- 		TrashcanVersioning  int `json:"trashcanVersioning,omitempty" since:"2"`
 
- 	} `json:"folderUses,omitempty" since:"2"`
 
- 	DeviceUses struct {
 
- 		Introducer       int `json:"introducer,omitempty" since:"2"`
 
- 		CustomCertName   int `json:"customCertName,omitempty" since:"2"`
 
- 		CompressAlways   int `json:"compressAlways,omitempty" since:"2"`
 
- 		CompressMetadata int `json:"compressMetadata,omitempty" since:"2"`
 
- 		CompressNever    int `json:"compressNever,omitempty" since:"2"`
 
- 		DynamicAddr      int `json:"dynamicAddr,omitempty" since:"2"`
 
- 		StaticAddr       int `json:"staticAddr,omitempty" since:"2"`
 
- 	} `json:"deviceUses,omitempty" since:"2"`
 
- 	Announce struct {
 
- 		GlobalEnabled     bool `json:"globalEnabled,omitempty" since:"2"`
 
- 		LocalEnabled      bool `json:"localEnabled,omitempty" since:"2"`
 
- 		DefaultServersDNS int  `json:"defaultServersDNS,omitempty" since:"2"`
 
- 		DefaultServersIP  int  `json:"defaultServersIP,omitempty" since:"2"` // Deprecated and not provided client-side anymore
 
- 		OtherServers      int  `json:"otherServers,omitempty" since:"2"`
 
- 	} `json:"announce,omitempty" since:"2"`
 
- 	Relays struct {
 
- 		Enabled        bool `json:"enabled,omitempty" since:"2"`
 
- 		DefaultServers int  `json:"defaultServers,omitempty" since:"2"`
 
- 		OtherServers   int  `json:"otherServers,omitempty" since:"2"`
 
- 	} `json:"relays,omitempty" since:"2"`
 
- 	UsesRateLimit        bool `json:"usesRateLimit,omitempty" since:"2"`
 
- 	UpgradeAllowedManual bool `json:"upgradeAllowedManual,omitempty" since:"2"`
 
- 	UpgradeAllowedAuto   bool `json:"upgradeAllowedAuto,omitempty" since:"2"`
 
- 	// V2.5 fields (fields that were in v2 but never added to the database
 
- 	UpgradeAllowedPre bool  `json:"upgradeAllowedPre,omitempty" since:"2"`
 
- 	RescanIntvs       []int `json:"rescanIntvs,omitempty" since:"2"`
 
- 	// v3 fields
 
- 	Uptime                     int    `json:"uptime,omitempty" since:"3"`
 
- 	NATType                    string `json:"natType,omitempty" since:"3"`
 
- 	AlwaysLocalNets            bool   `json:"alwaysLocalNets,omitempty" since:"3"`
 
- 	CacheIgnoredFiles          bool   `json:"cacheIgnoredFiles,omitempty" since:"3"`
 
- 	OverwriteRemoteDeviceNames bool   `json:"overwriteRemoteDeviceNames,omitempty" since:"3"`
 
- 	ProgressEmitterEnabled     bool   `json:"progressEmitterEnabled,omitempty" since:"3"`
 
- 	CustomDefaultFolderPath    bool   `json:"customDefaultFolderPath,omitempty" since:"3"`
 
- 	WeakHashSelection          string `json:"weakHashSelection,omitempty" since:"3"` // Deprecated and not provided client-side anymore
 
- 	CustomTrafficClass         bool   `json:"customTrafficClass,omitempty" since:"3"`
 
- 	CustomTempIndexMinBlocks   bool   `json:"customTempIndexMinBlocks,omitempty" since:"3"`
 
- 	TemporariesDisabled        bool   `json:"temporariesDisabled,omitempty" since:"3"`
 
- 	TemporariesCustom          bool   `json:"temporariesCustom,omitempty" since:"3"`
 
- 	LimitBandwidthInLan        bool   `json:"limitBandwidthInLan,omitempty" since:"3"`
 
- 	CustomReleaseURL           bool   `json:"customReleaseURL,omitempty" since:"3"`
 
- 	RestartOnWakeup            bool   `json:"restartOnWakeup,omitempty" since:"3"`
 
- 	CustomStunServers          bool   `json:"customStunServers,omitempty" since:"3"`
 
- 	FolderUsesV3 struct {
 
- 		ScanProgressDisabled    int            `json:"scanProgressDisabled,omitempty" since:"3"`
 
- 		ConflictsDisabled       int            `json:"conflictsDisabled,omitempty" since:"3"`
 
- 		ConflictsUnlimited      int            `json:"conflictsUnlimited,omitempty" since:"3"`
 
- 		ConflictsOther          int            `json:"conflictsOther,omitempty" since:"3"`
 
- 		DisableSparseFiles      int            `json:"disableSparseFiles,omitempty" since:"3"`
 
- 		DisableTempIndexes      int            `json:"disableTempIndexes,omitempty" since:"3"`
 
- 		AlwaysWeakHash          int            `json:"alwaysWeakHash,omitempty" since:"3"`
 
- 		CustomWeakHashThreshold int            `json:"customWeakHashThreshold,omitempty" since:"3"`
 
- 		FsWatcherEnabled        int            `json:"fsWatcherEnabled,omitempty" since:"3"`
 
- 		PullOrder               map[string]int `json:"pullOrder,omitempty" since:"3"`
 
- 		FilesystemType          map[string]int `json:"filesystemType,omitempty" since:"3"`
 
- 		FsWatcherDelays         []int          `json:"fsWatcherDelays,omitempty" since:"3"`
 
- 		CustomMarkerName        int            `json:"customMarkerName,omitempty" since:"3"`
 
- 		CopyOwnershipFromParent int            `json:"copyOwnershipFromParent,omitempty" since:"3"`
 
- 		ModTimeWindowS          []int          `json:"modTimeWindowS,omitempty" since:"3"`
 
- 		MaxConcurrentWrites     []int          `json:"maxConcurrentWrites,omitempty" since:"3"`
 
- 		DisableFsync            int            `json:"disableFsync,omitempty" since:"3"`
 
- 		BlockPullOrder          map[string]int `json:"blockPullOrder,omitempty" since:"3"`
 
- 		CopyRangeMethod         map[string]int `json:"copyRangeMethod,omitempty" since:"3"`
 
- 	} `json:"folderUsesV3,omitempty" since:"3"`
 
- 	GUIStats struct {
 
- 		Enabled                   int            `json:"enabled,omitempty" since:"3"`
 
- 		UseTLS                    int            `json:"useTLS,omitempty" since:"3"`
 
- 		UseAuth                   int            `json:"useAuth,omitempty" since:"3"`
 
- 		InsecureAdminAccess       int            `json:"insecureAdminAccess,omitempty" since:"3"`
 
- 		Debugging                 int            `json:"debugging,omitempty" since:"3"`
 
- 		InsecureSkipHostCheck     int            `json:"insecureSkipHostCheck,omitempty" since:"3"`
 
- 		InsecureAllowFrameLoading int            `json:"insecureAllowFrameLoading,omitempty" since:"3"`
 
- 		ListenLocal               int            `json:"listenLocal,omitempty" since:"3"`
 
- 		ListenUnspecified         int            `json:"listenUnspecified,omitempty" since:"3"`
 
- 		Theme                     map[string]int `json:"theme,omitempty" since:"3"`
 
- 	} `json:"guiStats,omitempty" since:"3"`
 
- 	BlockStats struct {
 
- 		Total             int `json:"total,omitempty" since:"3"`
 
- 		Renamed           int `json:"renamed,omitempty" since:"3"`
 
- 		Reused            int `json:"reused,omitempty" since:"3"`
 
- 		Pulled            int `json:"pulled,omitempty" since:"3"`
 
- 		CopyOrigin        int `json:"copyOrigin,omitempty" since:"3"`
 
- 		CopyOriginShifted int `json:"copyOriginShifted,omitempty" since:"3"`
 
- 		CopyElsewhere     int `json:"copyElsewhere,omitempty" since:"3"`
 
- 	} `json:"blockStats,omitempty" since:"3"`
 
- 	TransportStats map[string]int `json:"transportStats,omitempty" since:"3"`
 
- 	IgnoreStats struct {
 
- 		Lines           int `json:"lines,omitempty" since:"3"`
 
- 		Inverts         int `json:"inverts,omitempty" since:"3"`
 
- 		Folded          int `json:"folded,omitempty" since:"3"`
 
- 		Deletable       int `json:"deletable,omitempty" since:"3"`
 
- 		Rooted          int `json:"rooted,omitempty" since:"3"`
 
- 		Includes        int `json:"includes,omitempty" since:"3"`
 
- 		EscapedIncludes int `json:"escapedIncludes,omitempty" since:"3"`
 
- 		DoubleStars     int `json:"doubleStars,omitempty" since:"3"`
 
- 		Stars           int `json:"stars,omitempty" since:"3"`
 
- 	} `json:"ignoreStats,omitempty" since:"3"`
 
- 	// V3 fields added late in the RC
 
- 	WeakHashEnabled bool `json:"weakHashEnabled,omitempty" since:"3"` // Deprecated and not provided client-side anymore
 
- }
 
- func New() *Report {
 
- 	r := &Report{}
 
- 	util.FillNil(r)
 
- 	return r
 
- }
 
- func (r *Report) Validate() error {
 
- 	if r.UniqueID == "" || r.Version == "" || r.Platform == "" {
 
- 		return errors.New("missing required field")
 
- 	}
 
- 	if len(r.Date) != 8 {
 
- 		return errors.New("date not initialized")
 
- 	}
 
- 	// Some fields may not be null.
 
- 	if r.RescanIntvs == nil {
 
- 		r.RescanIntvs = []int{}
 
- 	}
 
- 	if r.FolderUsesV3.FsWatcherDelays == nil {
 
- 		r.FolderUsesV3.FsWatcherDelays = []int{}
 
- 	}
 
- 	return nil
 
- }
 
- func (r *Report) ClearForVersion(version int) error {
 
- 	return clear(r, version)
 
- }
 
- func (r *Report) FieldPointers() []interface{} {
 
- 	// All the fields of the Report, in the same order as the database fields.
 
- 	return []interface{}{
 
- 		&r.Received, &r.UniqueID, &r.Version, &r.LongVersion, &r.Platform,
 
- 		&r.NumFolders, &r.NumDevices, &r.TotFiles, &r.FolderMaxFiles,
 
- 		&r.TotMiB, &r.FolderMaxMiB, &r.MemoryUsageMiB, &r.SHA256Perf,
 
- 		&r.MemorySize, &r.Date,
 
- 		// V2
 
- 		&r.URVersion, &r.NumCPU, &r.FolderUses.SendOnly, &r.FolderUses.IgnorePerms,
 
- 		&r.FolderUses.IgnoreDelete, &r.FolderUses.AutoNormalize, &r.DeviceUses.Introducer,
 
- 		&r.DeviceUses.CustomCertName, &r.DeviceUses.CompressAlways,
 
- 		&r.DeviceUses.CompressMetadata, &r.DeviceUses.CompressNever,
 
- 		&r.DeviceUses.DynamicAddr, &r.DeviceUses.StaticAddr,
 
- 		&r.Announce.GlobalEnabled, &r.Announce.LocalEnabled,
 
- 		&r.Announce.DefaultServersDNS, &r.Announce.DefaultServersIP,
 
- 		&r.Announce.OtherServers, &r.Relays.Enabled, &r.Relays.DefaultServers,
 
- 		&r.Relays.OtherServers, &r.UsesRateLimit, &r.UpgradeAllowedManual,
 
- 		&r.UpgradeAllowedAuto, &r.FolderUses.SimpleVersioning,
 
- 		&r.FolderUses.ExternalVersioning, &r.FolderUses.StaggeredVersioning,
 
- 		&r.FolderUses.TrashcanVersioning,
 
- 		// V2.5
 
- 		&r.UpgradeAllowedPre,
 
- 		// V3
 
- 		&r.Uptime, &r.NATType, &r.AlwaysLocalNets, &r.CacheIgnoredFiles,
 
- 		&r.OverwriteRemoteDeviceNames, &r.ProgressEmitterEnabled, &r.CustomDefaultFolderPath,
 
- 		&r.WeakHashSelection, &r.CustomTrafficClass, &r.CustomTempIndexMinBlocks,
 
- 		&r.TemporariesDisabled, &r.TemporariesCustom, &r.LimitBandwidthInLan,
 
- 		&r.CustomReleaseURL, &r.RestartOnWakeup, &r.CustomStunServers,
 
- 		&r.FolderUsesV3.ScanProgressDisabled, &r.FolderUsesV3.ConflictsDisabled,
 
- 		&r.FolderUsesV3.ConflictsUnlimited, &r.FolderUsesV3.ConflictsOther,
 
- 		&r.FolderUsesV3.DisableSparseFiles, &r.FolderUsesV3.DisableTempIndexes,
 
- 		&r.FolderUsesV3.AlwaysWeakHash, &r.FolderUsesV3.CustomWeakHashThreshold,
 
- 		&r.FolderUsesV3.FsWatcherEnabled,
 
- 		&r.GUIStats.Enabled, &r.GUIStats.UseTLS, &r.GUIStats.UseAuth,
 
- 		&r.GUIStats.InsecureAdminAccess,
 
- 		&r.GUIStats.Debugging, &r.GUIStats.InsecureSkipHostCheck,
 
- 		&r.GUIStats.InsecureAllowFrameLoading, &r.GUIStats.ListenLocal,
 
- 		&r.GUIStats.ListenUnspecified,
 
- 		&r.BlockStats.Total, &r.BlockStats.Renamed,
 
- 		&r.BlockStats.Reused, &r.BlockStats.Pulled, &r.BlockStats.CopyOrigin,
 
- 		&r.BlockStats.CopyOriginShifted, &r.BlockStats.CopyElsewhere,
 
- 		&r.IgnoreStats.Lines, &r.IgnoreStats.Inverts, &r.IgnoreStats.Folded,
 
- 		&r.IgnoreStats.Deletable, &r.IgnoreStats.Rooted, &r.IgnoreStats.Includes,
 
- 		&r.IgnoreStats.EscapedIncludes, &r.IgnoreStats.DoubleStars, &r.IgnoreStats.Stars,
 
- 		// V3 added late in the RC
 
- 		&r.WeakHashEnabled,
 
- 		&r.Address,
 
- 		// Receive only folders
 
- 		&r.FolderUses.ReceiveOnly,
 
- 	}
 
- }
 
- func (r *Report) FieldNames() []string {
 
- 	// The database fields that back this struct in PostgreSQL
 
- 	return []string{
 
- 		// V1
 
- 		"Received",
 
- 		"UniqueID",
 
- 		"Version",
 
- 		"LongVersion",
 
- 		"Platform",
 
- 		"NumFolders",
 
- 		"NumDevices",
 
- 		"TotFiles",
 
- 		"FolderMaxFiles",
 
- 		"TotMiB",
 
- 		"FolderMaxMiB",
 
- 		"MemoryUsageMiB",
 
- 		"SHA256Perf",
 
- 		"MemorySize",
 
- 		"Date",
 
- 		// V2
 
- 		"ReportVersion",
 
- 		"NumCPU",
 
- 		"FolderRO",
 
- 		"FolderIgnorePerms",
 
- 		"FolderIgnoreDelete",
 
- 		"FolderAutoNormalize",
 
- 		"DeviceIntroducer",
 
- 		"DeviceCustomCertName",
 
- 		"DeviceCompressAlways",
 
- 		"DeviceCompressMetadata",
 
- 		"DeviceCompressNever",
 
- 		"DeviceDynamicAddr",
 
- 		"DeviceStaticAddr",
 
- 		"AnnounceGlobalEnabled",
 
- 		"AnnounceLocalEnabled",
 
- 		"AnnounceDefaultServersDNS",
 
- 		"AnnounceDefaultServersIP",
 
- 		"AnnounceOtherServers",
 
- 		"RelayEnabled",
 
- 		"RelayDefaultServers",
 
- 		"RelayOtherServers",
 
- 		"RateLimitEnabled",
 
- 		"UpgradeAllowedManual",
 
- 		"UpgradeAllowedAuto",
 
- 		// v0.12.19+
 
- 		"FolderSimpleVersioning",
 
- 		"FolderExternalVersioning",
 
- 		"FolderStaggeredVersioning",
 
- 		"FolderTrashcanVersioning",
 
- 		// V2.5
 
- 		"UpgradeAllowedPre",
 
- 		// V3
 
- 		"Uptime",
 
- 		"NATType",
 
- 		"AlwaysLocalNets",
 
- 		"CacheIgnoredFiles",
 
- 		"OverwriteRemoteDeviceNames",
 
- 		"ProgressEmitterEnabled",
 
- 		"CustomDefaultFolderPath",
 
- 		"WeakHashSelection",
 
- 		"CustomTrafficClass",
 
- 		"CustomTempIndexMinBlocks",
 
- 		"TemporariesDisabled",
 
- 		"TemporariesCustom",
 
- 		"LimitBandwidthInLan",
 
- 		"CustomReleaseURL",
 
- 		"RestartOnWakeup",
 
- 		"CustomStunServers",
 
- 		"FolderScanProgressDisabled",
 
- 		"FolderConflictsDisabled",
 
- 		"FolderConflictsUnlimited",
 
- 		"FolderConflictsOther",
 
- 		"FolderDisableSparseFiles",
 
- 		"FolderDisableTempIndexes",
 
- 		"FolderAlwaysWeakHash",
 
- 		"FolderCustomWeakHashThreshold",
 
- 		"FolderFsWatcherEnabled",
 
- 		"GUIEnabled",
 
- 		"GUIUseTLS",
 
- 		"GUIUseAuth",
 
- 		"GUIInsecureAdminAccess",
 
- 		"GUIDebugging",
 
- 		"GUIInsecureSkipHostCheck",
 
- 		"GUIInsecureAllowFrameLoading",
 
- 		"GUIListenLocal",
 
- 		"GUIListenUnspecified",
 
- 		"BlocksTotal",
 
- 		"BlocksRenamed",
 
- 		"BlocksReused",
 
- 		"BlocksPulled",
 
- 		"BlocksCopyOrigin",
 
- 		"BlocksCopyOriginShifted",
 
- 		"BlocksCopyElsewhere",
 
- 		"IgnoreLines",
 
- 		"IgnoreInverts",
 
- 		"IgnoreFolded",
 
- 		"IgnoreDeletable",
 
- 		"IgnoreRooted",
 
- 		"IgnoreIncludes",
 
- 		"IgnoreEscapedIncludes",
 
- 		"IgnoreDoubleStars",
 
- 		"IgnoreStars",
 
- 		// V3 added late in the RC
 
- 		"WeakHashEnabled",
 
- 		"Address",
 
- 		// Receive only folders
 
- 		"FolderRecvOnly",
 
- 	}
 
- }
 
- func (r Report) Value() (driver.Value, error) {
 
- 	// This needs to be string, yet we read back bytes..
 
- 	bs, err := json.Marshal(r)
 
- 	return string(bs), err
 
- }
 
- func (r *Report) Scan(value interface{}) error {
 
- 	// Zero out the previous value
 
- 	// JSON un-marshaller does not touch fields that are not in the payload, so we carry over values from a previous
 
- 	// scan.
 
- 	*r = Report{}
 
- 	b, ok := value.([]byte)
 
- 	if !ok {
 
- 		return errors.New("type assertion to []byte failed")
 
- 	}
 
- 	return json.Unmarshal(b, &r)
 
- }
 
- func clear(v interface{}, since int) error {
 
- 	s := reflect.ValueOf(v).Elem()
 
- 	t := s.Type()
 
- 	for i := 0; i < s.NumField(); i++ {
 
- 		f := s.Field(i)
 
- 		tag := t.Field(i).Tag
 
- 		v := tag.Get("since")
 
- 		if len(v) == 0 {
 
- 			f.Set(reflect.Zero(f.Type()))
 
- 			continue
 
- 		}
 
- 		vn, err := strconv.Atoi(v)
 
- 		if err != nil {
 
- 			return err
 
- 		}
 
- 		if vn > since {
 
- 			f.Set(reflect.Zero(f.Type()))
 
- 			continue
 
- 		}
 
- 		// Dive deeper
 
- 		if f.Kind() == reflect.Ptr {
 
- 			f = f.Elem()
 
- 		}
 
- 		if f.Kind() == reflect.Struct {
 
- 			if err := clear(f.Addr().Interface(), since); err != nil {
 
- 				return err
 
- 			}
 
- 		}
 
- 	}
 
- 	return nil
 
- }
 
 
  |