| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- // 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"`
- CaseSensitiveFS int `json:"caseSensitiveFS,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
- }
|