Compare commits

...

10 Commits

Author SHA1 Message Date
Syncthing Release Automation
59de7048bd gui, man, authors: Update docs, translations, and contributors 2023-03-13 03:45:45 +00:00
Jakob Borg
466b56ded1 lib/protocol: Cache expensive key operations (fixes #8599) (#8820)
This adds a cache to the expensive key generation operations. It's fixes
size LRU/MRU stuff to keep memory usage bounded under absurd conditions.

Also closes #8600.
2023-03-12 20:06:59 +01:00
André Colomb
3ffe859fe8 gui: Add Persian (fa) translation template (#8822)
gui: Add Persian (fa) translation template.

Based on user request from Weblate, user @a-kbd.
2023-03-10 16:27:48 +01:00
Simon Frei
da72df6ffc lib: Correctly handle encrypted trailer size (fixes #8556) (#8563) 2023-03-10 14:14:14 +01:00
tomasz1986
b53c1b9a04 gui: Disable Restore Versions filters when no versioned files exist (fixes #5408) (#8539)
Currently, the name and date filters in the Restore Versions modal are
always enabled, even if there are no versioned files present. With this
change, they are enabled only when there are no errors and versioned
files actually exist.

In addition to disabling the filters, also completely skip date picker
generation, as it serves no function while still displaying a non-sense
date range, which starts today and ends in the past.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-03-10 13:41:15 +01:00
dependabot[bot]
b00976781c build(deps): bump github.com/chmduquesne/rollinghash from 0.0.0-20180912150627-a60f8e7142b5 to 4.0.0+incompatible (#8804)
build(deps): bump github.com/chmduquesne/rollinghash

Bumps [github.com/chmduquesne/rollinghash](https://github.com/chmduquesne/rollinghash) from 0.0.0-20180912150627-a60f8e7142b5 to 4.0.0+incompatible.
- [Release notes](https://github.com/chmduquesne/rollinghash/releases)
- [Commits](https://github.com/chmduquesne/rollinghash/commits/v4.0.0)

---
updated-dependencies:
- dependency-name: github.com/chmduquesne/rollinghash
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-10 13:36:25 +01:00
Jakob Borg
58f9dcca31 build: Update dependencies (#8821) 2023-03-10 13:04:13 +01:00
Jakob Borg
ab8e6a82ab lib/api: Expose blocksHash in file info (#8810)
This adds the BlocksHash field from the FileInfo to our API output. It
can be useful for debugging, or for external tools. I'm intentionally
leaving it as an opaque base64 string because no meaning should be
derived from it: it's just a string.
2023-03-06 15:37:15 +01:00
Syncthing Release Automation
3cdcb7dd72 gui, man, authors: Update docs, translations, and contributors 2023-03-06 03:45:45 +00:00
entity0xfe
4558eef446 lib/discover: Don't leak relay-tokens to discovery (#8762)
Use an allowlist to send only the `id` query param to the discovery server.
2023-03-04 12:16:57 +01:00
46 changed files with 750 additions and 457 deletions

View File

@@ -35,6 +35,7 @@ type CLI struct {
TokenPath string `placeholder:"PATH" help:"Path to the token file within the folder (used to determine folder ID)"`
folderKey *[32]byte
keyGen *protocol.KeyGenerator
}
type storedEncryptionToken struct {
@@ -68,7 +69,8 @@ func (c *CLI) Run() error {
}
}
c.folderKey = protocol.KeyFromPassword(c.FolderID, c.Password)
c.keyGen = protocol.NewKeyGenerator()
c.folderKey = c.keyGen.KeyFromPassword(c.FolderID, c.Password)
return c.walk()
}
@@ -151,7 +153,7 @@ func (c *CLI) process(srcFs fs.Filesystem, dstFs fs.Filesystem, path string) err
// in native format, while protocol expects wire format (slashes).
encFi.Name = osutil.NormalizedFilename(encFi.Name)
plainFi, err := protocol.DecryptFileInfo(*encFi, c.folderKey)
plainFi, err := protocol.DecryptFileInfo(c.keyGen, *encFi, c.folderKey)
if err != nil {
return fmt.Errorf("%s: decrypting metadata: %w", path, err)
}
@@ -162,7 +164,7 @@ func (c *CLI) process(srcFs fs.Filesystem, dstFs fs.Filesystem, path string) err
var plainFd fs.File
if dstFs != nil {
if err := dstFs.MkdirAll(filepath.Dir(plainFi.Name), 0700); err != nil {
if err := dstFs.MkdirAll(filepath.Dir(plainFi.Name), 0o700); err != nil {
return fmt.Errorf("%s: %w", plainFi.Name, err)
}
@@ -209,7 +211,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo,
return fmt.Errorf("block count mismatch: encrypted %d != plaintext %d", len(encFi.Blocks), len(plainFi.Blocks))
}
fileKey := protocol.FileKey(plainFi.Name, c.folderKey)
fileKey := c.keyGen.FileKey(plainFi.Name, c.folderKey)
for i, encBlock := range encFi.Blocks {
// Read the encrypted block
buf := make([]byte, encBlock.Size)

32
go.mod
View File

@@ -11,7 +11,7 @@ require (
github.com/ccding/go-stun v0.1.4
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5
github.com/chmduquesne/rollinghash v4.0.0+incompatible
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/d4l3k/messagediff v1.2.1
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
@@ -27,7 +27,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/lib/pq v1.10.7
github.com/maruel/panicparse/v2 v2.3.1
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
@@ -37,25 +37,25 @@ require (
github.com/pierrec/lz4/v4 v4.1.17
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quic-go/quic-go v0.33.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/sasha-s/go-deadlock v0.3.1
github.com/shirou/gopsutil/v3 v3.23.1
github.com/shirou/gopsutil/v3 v3.23.2
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.2
github.com/urfave/cli v1.22.12
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.6.0
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.7.0
golang.org/x/sys v0.5.0
golang.org/x/text v0.7.0
golang.org/x/crypto v0.7.0
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0
golang.org/x/sys v0.6.0
golang.org/x/text v0.8.0
golang.org/x/time v0.3.0
golang.org/x/tools v0.6.0
google.golang.org/protobuf v1.28.1
golang.org/x/tools v0.7.0
google.golang.org/protobuf v1.29.0
)
require (
@@ -64,19 +64,19 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/onsi/ginkgo/v2 v2.8.1 // indirect
github.com/onsi/ginkgo/v2 v2.9.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/petermattis/goid v0.0.0-20221215004737-a150e88a970d // indirect
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect
)
// https://github.com/gobwas/glob/pull/55

68
go.sum
View File

@@ -24,8 +24,8 @@ github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5 h1:Wg96Dh0MLTanEaPO0OkGtUIaa2jOnShAIOVUIzRHUxo=
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0=
github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo=
github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -66,8 +66,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -79,8 +80,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/greatroar/blobloom v0.7.2 h1:F30MGLHOcb4zr0pwCPTcKdlTM70rEgkf+LzdUPc5ss8=
github.com/greatroar/blobloom v0.7.2/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
@@ -99,8 +100,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@@ -126,20 +127,20 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU=
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/petermattis/goid v0.0.0-20221215004737-a150e88a970d h1:htwtWgtQo8YS6JFWWi2DNgY0RwSGJ1ruMoxY6CUUclk=
github.com/petermattis/goid v0.0.0-20221215004737-a150e88a970d/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 h1:mDrFjGWmndQXmVx3giRScTbkltpPcnGEWG1GorsuiJ4=
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -154,8 +155,8 @@ github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
@@ -171,8 +172,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -180,8 +181,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2 h1:F4snRP//nIuTTW9LYEzVH4HVwDG9T3M4t8y/2nqMbiY=
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
@@ -204,15 +206,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -224,8 +226,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -259,17 +261,17 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -278,8 +280,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -293,8 +295,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -208,7 +208,7 @@
"Inversion of the given condition (i.e. do not exclude)": "Préfixe pour inverser la condition donnée (c.-à-d. \"Ne pas exclure\")",
"Keep Versions": "Nombre de versions à conserver",
"LDAP": "LDAP",
"Largest First": "Les plus volumineux en premier",
"Largest First": "Les plus gros en premier",
"Last 30 Days": "Les 30 derniers jours",
"Last 7 Days": "Les 7 derniers jours",
"Last Month": "Le mois dernier",

View File

@@ -6,13 +6,13 @@
"About": "Over",
"Action": "Actie",
"Actions": "Acties",
"Active filter rules": "Active filter rules",
"Active filter rules": "Actieve filterregels",
"Add": "Toevoegen",
"Add Device": "Apparaat toevoegen",
"Add Folder": "Map toevoegen",
"Add Remote Device": "Extern apparaat toevoegen",
"Add devices from the introducer to our device list, for mutually shared folders.": "Apparaten van het introductie-apparaat aan onze lijst met apparaten toevoegen, voor gemeenschappelijk gedeelde mappen.",
"Add filter entry": "Add filter entry",
"Add filter entry": "Filter-item toevoegen",
"Add ignore patterns": "Negeerpatronen toevoegen",
"Add new folder?": "Nieuwe map toevoegen?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Het interval van volledige scan zal daarbij ook vergroot worden (maal 60, dit is een nieuwe standaardwaarde van 1 uur). U kunt het later voor elke map manueel configureren nadat u nee kiest.",
@@ -78,7 +78,7 @@
"Danger!": "Let op!",
"Database Location": "Locatie van database",
"Debugging Facilities": "Debugmogelijkheden",
"Default": "Default",
"Default": "Standaard",
"Default Configuration": "Standaardconfiguratie",
"Default Device": "Standaardapparaat",
"Default Folder": "Standaardmap",
@@ -144,7 +144,7 @@
"Enter up to three octal digits.": "Voer tot drie octale cijfers in.",
"Error": "Fout",
"Extended Attributes": "Uitgebreide attributen",
"Extended Attributes Filter": "Extended Attributes Filter",
"Extended Attributes Filter": "Filter voor uitgebreide kenmerken",
"External": "Extern",
"External File Versioning": "Extern versiebeheer",
"Failed Items": "Mislukte items",
@@ -186,7 +186,7 @@
"Global Discovery Servers": "Globale detectieservers",
"Global State": "Globale status",
"Help": "Help",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tip: er zijn alleen weigeren-regels gedetecteerd terwijl de standaardwaarde weigeren is. Overweeg \"om het even welke toestaan\" toe te voegen als laatste regel.",
"Home page": "Startpagina",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Uw huidige instellingen geven echter aan dat u het misschien niet wilt inschakelen. We hebben de automatische crashrapportage voor u uitgeschakeld.",
"Identification": "Identificatie",
@@ -235,8 +235,8 @@
"Major Upgrade": "Grote upgrade",
"Mass actions": "Groepsacties",
"Maximum Age": "Maximale leeftijd",
"Maximum single entry size": "Maximum single entry size",
"Maximum total size": "Maximum total size",
"Maximum single entry size": "Maximale grootte van een enkel item",
"Maximum total size": "Maximale totale grootte",
"Metadata Only": "Alleen metadata",
"Minimum Free Disk Space": "Minimale vrije schijfruimte",
"Mod. Device": "Wijzigend apparaat",
@@ -253,7 +253,7 @@
"No": "Nee",
"No File Versioning": "Geen versiebeheer",
"No files will be deleted as a result of this operation.": "Deze bewerking zal geen bestanden verwijderen.",
"No rules set": "No rules set",
"No rules set": "Geen regels ingesteld",
"No upgrades": "Geen upgrades",
"Not shared": "Niet gedeeld",
"Notice": "Mededeling",
@@ -444,7 +444,7 @@
"Time": "Tijd",
"Time the item was last modified": "Tijdstip waarop het item laatst gewijzigd is",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Om verbinding te maken met het Syncthing-apparaat \"{{devicename}}\", voegt u aan uw kant een nieuw extern apparaat toe met deze ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Vink het vakje aan om een regel toe te staan. Laat het vakje uitgevinkt om een regel te weigeren.",
"Today": "Vandaag",
"Trash Can": "Prullenbak",
"Trash Can File Versioning": "Prullenbak-versiebeheer",
@@ -509,7 +509,7 @@
"Your email app should open to let you choose the recipient and send it from your own address.": "Uw e-mail-app zou moeten openen om u de ontvanger te laten kiezen en het te versturen vanaf uw eigen adres.",
"days": "dagen",
"deleted": "verwijderd",
"deny": "deny",
"deny": "weigeren",
"directories": "mappen",
"file": "bestand",
"files": "bestanden",
@@ -517,7 +517,7 @@
"full documentation": "volledige documentatie",
"items": "items",
"modified": "gewijzigd",
"permit": "permit",
"permit": "toestaan",
"seconds": "seconden",
"theme-name-black": "Zwart",
"theme-name-dark": "Donker",

View File

@@ -6,13 +6,13 @@
"About": "Om",
"Action": "Åtgärd",
"Actions": "Åtgärder",
"Active filter rules": "Active filter rules",
"Active filter rules": "Aktiva filtreringsregler",
"Add": "Lägg till",
"Add Device": "Lägg till enhet",
"Add Folder": "Lägg till mapp",
"Add Remote Device": "Lägg till fjärrenhet",
"Add devices from the introducer to our device list, for mutually shared folders.": "Lägg till enheter från introduktören till vår enhetslista för ömsesidigt delade mappar.",
"Add filter entry": "Add filter entry",
"Add filter entry": "Lägg till filtreringsregel",
"Add ignore patterns": "Lägg till ignoreringsmönster",
"Add new folder?": "Lägg till ny mapp?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dessutom kommer det fullständiga återkommande skanningsintervallet att höjas (60 gånger, d.v.s. ny standard på 1h). Du kan också konfigurera den manuellt för varje mapp senare efter att du har valt Nej.",
@@ -57,7 +57,7 @@
"Click to see full identification string and QR code.": "Klicka för att se fullständig identifieringssträng och QR-kod.",
"Close": "Stäng",
"Command": "Kommando",
"Comment, when used at the start of a line": "Kommentara, vid användning i början av en rad.",
"Comment, when used at the start of a line": "Kommentar, vid användning i början av en rad",
"Compression": "Komprimering",
"Configuration Directory": "Konfigurationsmapp",
"Configuration File": "Konfigurationsfil",
@@ -78,7 +78,7 @@
"Danger!": "Fara!",
"Database Location": "Databasplats",
"Debugging Facilities": "Felsökningsfunktioner",
"Default": "Default",
"Default": "Förvald",
"Default Configuration": "Standardkonfiguration",
"Default Device": "Standardenhet",
"Default Folder": "Standardmapp",
@@ -144,7 +144,7 @@
"Enter up to three octal digits.": "Ange upp till tre oktala siffror.",
"Error": "Fel",
"Extended Attributes": "Utökade attribut",
"Extended Attributes Filter": "Extended Attributes Filter",
"Extended Attributes Filter": "Utökat attributfilter",
"External": "Extern",
"External File Versioning": "Extern filversionshantering",
"Failed Items": "Misslyckade objekt",
@@ -186,7 +186,7 @@
"Global Discovery Servers": "Globala annonseringsservrar",
"Global State": "Globalt tillstånd",
"Help": "Hjälp",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tips: endast neknings-regler upptäcktes med nekning förinställt. Överväg att lägga till \"tillåt allt\" som sista regel.",
"Home page": "Webbplats",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar visar dock att du kanske inte vill att den ska aktiveras. Vi har inaktiverat automatisk krasch rapportering för dig.",
"Identification": "Identifiering",
@@ -235,8 +235,8 @@
"Major Upgrade": "Större uppgradering",
"Mass actions": "Massåtgärder",
"Maximum Age": "Högsta ålder",
"Maximum single entry size": "Maximum single entry size",
"Maximum total size": "Maximum total size",
"Maximum single entry size": "Största tillåtna storlek för tillägg",
"Maximum total size": "Största tillåtna totala storlek",
"Metadata Only": "Endast metadata",
"Minimum Free Disk Space": "Minsta lediga diskutrymme",
"Mod. Device": "Enhet som utförde ändring",
@@ -253,7 +253,7 @@
"No": "Nej",
"No File Versioning": "Ingen filversionshantering",
"No files will be deleted as a result of this operation.": "Inga filer kommer att tas bort till följd av denna operation.",
"No rules set": "No rules set",
"No rules set": "Inga regler tillagda",
"No upgrades": "Inga uppgraderingar",
"Not shared": "Inte delad",
"Notice": "Observera",
@@ -392,7 +392,7 @@
"Syncthing is restarting.": "Syncthing startar om.",
"Syncthing is upgrading.": "Syncthing uppgraderas.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing stöder nu automatiskt kraschrapportering till utvecklarna. Denna funktion är aktiverad som standard.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar vara avstängd eller så är det problem med din internetanslutning. Försöker igen...",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar vara avstängt, eller så finns det problem med din internetanslutning. Försöker igen",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem med behandlingen av din förfrågan. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
"TCP LAN": "TCP LAN",
"TCP WAN": "TCP WAN",
@@ -430,7 +430,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Frekvensgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte accepterat delning av den här mappen.",
"The remote device has paused this folder.": "Fjärrenheten har pausat den här mappen.",
"The rescan interval must be a non-negative number of seconds.": "Förnyelseintervallet måste vara ett positivt antal sekunder",
"The rescan interval must be a non-negative number of seconds.": "Förnyelseintervallet måste vara ett positivt antal sekunder.",
"There are no devices to share this folder with.": "Det finns inga enheter att dela denna mapp med.",
"There are no file versions to restore.": "Det finns inga filversioner att återställa.",
"There are no folders to share with this device.": "Det finns inga mappar att dela med denna enhet.",
@@ -444,7 +444,7 @@
"Time": "Tid",
"Time the item was last modified": "Tidpunkten objektet var senast ändrad",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "För att ansluta till Syncthing-enheten med namnet \"{{devicename}}\", lägg till en ny fjärrenhet med detta ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "För att tillåta en regel, lämna rutan ikryssad. För att vägra en regel, lämna den okryssad",
"Today": "Idag",
"Trash Can": "Papperskorgen",
"Trash Can File Versioning": "Papperskorgs filversionshantering",

View File

@@ -2686,6 +2686,8 @@ angular.module('syncthing.core')
if (closed) {
resetRestoreVersions();
return;
} else if ($scope.sizeOf($scope.restoreVersions.versions) === '0') {
return;
}
$scope.restoreVersions.tree = $("#restoreTree").fancytree({
@@ -2702,7 +2704,7 @@ angular.module('syncthing.core')
indentation: 24,
},
strings: {
loading: $translate.instant("Loading..."),
loading: $translate.instant("Loading data..."),
loadError: $translate.instant("Failed to load file versions."),
noData: $translate.instant("There are no file versions to restore.")
},

View File

@@ -1,12 +1,14 @@
<modal id="restoreVersions" status="default" icon="fas fa-undo" heading="{{'Restore Versions' | translate}} ({{folderLabel(restoreVersions.folder)}})" large="yes" closeable="yes">
<div class="modal-body">
<span translate ng-if="!restoreVersions.versions && !restoreVersions.errors">Loading data...</span>
<div ng-if="restoreVersions.versions">
<div ng-if="restoreVersions.versions && !restoreVersions.errors">
<div id="restoreTree-container">
<table id="restoreTree">
<tbody>
<tr>
<td></td>
<td>
<span ng-if="sizeOf(restoreVersions.versions) > 0" translate>Loading data...</span>
<span ng-if="sizeOf(restoreVersions.versions) == 0" translate>There are no file versions to restore.</span>
</td>
<td></td>
</tr>
</tbody>
@@ -17,13 +19,13 @@
<div class="col-md-6">
<div class="form-group">
<label for="restoreVersionSearch"><span translate>Filter by name</span>:&nbsp</label>
<input id="restoreVersionSearch" class="form-control" type="text" ng-model="restoreVersions.filters.text">
<input id="restoreVersionSearch" class="form-control" type="text" ng-model="restoreVersions.filters.text" ng-disabled="sizeOf(restoreVersions.versions) == 0">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="restoreVersionDate"><span translate>Filter by date</span>:&nbsp</label>
<input id="restoreVersionDateRange" class="form-control">
<input id="restoreVersionDateRange" class="form-control" ng-disabled="sizeOf(restoreVersions.versions) == 0">
</div>
</div>
</div>

View File

@@ -1828,6 +1828,7 @@ func fileIntfJSONMap(f protocol.FileIntf) map[string]interface{} {
"localFlags": f.FileLocalFlags(),
"platform": f.PlatformData(),
"inodeChange": f.InodeChangeTime(),
"blocksHash": f.FileBlocksHash(),
}
if f.HasPermissionBits() {
out["permissions"] = fmt.Sprintf("%#o", f.FilePermissions())

View File

@@ -161,6 +161,7 @@ type service struct {
natService *nat.Service
evLogger events.Logger
registry *registry.Registry
keyGen *protocol.KeyGenerator
dialNow chan struct{}
dialNowDevices map[protocol.DeviceID]struct{}
@@ -171,7 +172,7 @@ type service struct {
listenerTokens map[string]suture.ServiceToken
}
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger, registry *registry.Registry) Service {
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger, registry *registry.Registry, keyGen *protocol.KeyGenerator) Service {
spec := svcutil.SpecWithInfoLogger(l)
service := &service{
Supervisor: suture.New("connections.Service", spec),
@@ -190,6 +191,7 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
natService: nat.NewService(myID, cfg),
evLogger: evLogger,
registry: registry,
keyGen: keyGen,
dialNowDevicesMut: sync.NewMutex(),
dialNow: make(chan struct{}, 1),
@@ -411,7 +413,7 @@ func (s *service) handleHellos(ctx context.Context) error {
// connections are limited.
rd, wr := s.limiter.getLimiters(remoteID, c, c.IsLocal())
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID))
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID), s.keyGen)
go func() {
<-protoConn.Closed()
s.dialNowDevicesMut.Lock()
@@ -426,6 +428,7 @@ func (s *service) handleHellos(ctx context.Context) error {
continue
}
}
func (s *service) connect(ctx context.Context) error {
// Map of when to earliest dial each given device + address again
nextDialAt := make(nextDialRegistry)
@@ -1020,8 +1023,10 @@ func urlsToStrings(urls []*url.URL) []string {
return strings
}
var warningLimiters = make(map[protocol.DeviceID]*rate.Limiter)
var warningLimitersMut = sync.NewMutex()
var (
warningLimiters = make(map[protocol.DeviceID]*rate.Limiter)
warningLimitersMut = sync.NewMutex()
)
func warningFor(dev protocol.DeviceID, msg string) {
warningLimitersMut.Lock()

View File

@@ -133,6 +133,10 @@ func (f FileInfoTruncated) InodeChangeTime() time.Time {
return time.Unix(0, f.InodeChangeNs)
}
func (f FileInfoTruncated) FileBlocksHash() []byte {
return f.BlocksHash
}
func (f FileInfoTruncated) ConvertToIgnoredFileInfo() protocol.FileInfo {
file := f.copyToFileInfo()
file.SetIgnored()

View File

@@ -54,6 +54,15 @@ type announcement struct {
Addresses []string `json:"addresses"`
}
func (a announcement) MarshalJSON() ([]byte, error) {
type announcementCopy announcement
a.Addresses = sanitizeRelayAddresses(a.Addresses)
aCopy := announcementCopy(a)
return json.Marshal(aCopy)
}
type serverOptions struct {
insecure bool // don't check certificate
noAnnounce bool // don't announce

View File

@@ -116,6 +116,9 @@ func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, boo
// usable as-is.
addrs = filterUnspecifiedLocal(addrs)
// do not leak relay tokens to discovery
addrs = sanitizeRelayAddresses(addrs)
if len(addrs) == 0 {
// Nothing to announce
return msg, false
@@ -315,3 +318,32 @@ func filterUnspecifiedLocal(addrs []string) []string {
}
return filtered
}
func sanitizeRelayAddresses(addrs []string) []string {
filtered := addrs[:0]
allowlist := []string{"id"}
for _, addr := range addrs {
u, err := url.Parse(addr)
if err != nil {
continue
}
if u.Scheme == "relay" {
s := url.Values{}
q := u.Query()
for _, w := range allowlist {
if q.Has(w) {
s.Add(w, q.Get(w))
}
}
u.RawQuery = s.Encode()
addr = u.String()
}
filtered = append(filtered, addr)
}
return filtered
}

View File

@@ -1251,6 +1251,7 @@ func (f *sendReceiveFolder) shortcutFile(file protocol.FileInfo, dbUpdateChan ch
}
defer fd.Close()
trailerSize, err := writeEncryptionTrailer(file, fd)
file.EncryptionTrailerSize = int(trailerSize)
if err != nil {
return err
}

View File

@@ -357,6 +357,10 @@ func prepareFileInfoForIndex(f protocol.FileInfo) protocol.FileInfo {
if f.IsReceiveOnlyChanged() {
f.Version = protocol.Vector{}
}
// The trailer with the encrypted fileinfo is device local, don't send info
// about that to remotes
f.Size -= int64(f.EncryptionTrailerSize)
f.EncryptionTrailerSize = 0
// never sent externally
f.LocalFlags = 0
f.VersionHash = nil

View File

@@ -142,6 +142,7 @@ type model struct {
folderIOLimiter *util.Semaphore
fatalChan chan error
started chan struct{}
keyGen *protocol.KeyGenerator
// fields protected by fmut
fmut sync.RWMutex
@@ -174,9 +175,7 @@ var _ config.Verifier = &model{}
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *util.Semaphore) service
var (
folderFactories = make(map[config.FolderType]folderFactory)
)
var folderFactories = make(map[config.FolderType]folderFactory)
var (
errDeviceUnknown = errors.New("unknown device")
@@ -205,7 +204,7 @@ var (
// NewModel creates and starts a new model. The model starts in read-only mode,
// where it sends index information to connected peers and responds to requests
// for file data without altering the local folder in any way.
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger) Model {
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger, keyGen *protocol.KeyGenerator) Model {
spec := svcutil.SpecWithDebugLogger(l)
m := &model{
Supervisor: suture.New("model", spec),
@@ -227,6 +226,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
folderIOLimiter: util.NewSemaphore(cfg.Options().MaxFolderConcurrency()),
fatalChan: make(chan error),
started: make(chan struct{}),
keyGen: keyGen,
// fields protected by fmut
fmut: sync.NewRWMutex(),
@@ -1462,7 +1462,7 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
}
if isEncryptedRemote {
passwordToken := protocol.PasswordToken(fcfg.ID, folderDevice.EncryptionPassword)
passwordToken := protocol.PasswordToken(m.keyGen, fcfg.ID, folderDevice.EncryptionPassword)
match := false
if hasTokenLocal {
match = bytes.Equal(passwordToken, ccDeviceInfos.local.EncryptionPasswordToken)
@@ -2483,7 +2483,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
if deviceCfg.DeviceID == m.id && hasEncryptionToken {
protocolDevice.EncryptionPasswordToken = encryptionToken
} else if folderDevice.EncryptionPassword != "" {
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(folderCfg.ID, folderDevice.EncryptionPassword)
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(m.keyGen, folderCfg.ID, folderDevice.EncryptionPassword)
if folderDevice.DeviceID == device {
passwords[folderCfg.ID] = folderDevice.EncryptionPassword
}
@@ -3264,7 +3264,7 @@ func readEncryptionToken(cfg config.FolderConfiguration) ([]byte, error) {
func writeEncryptionToken(token []byte, cfg config.FolderConfiguration) error {
tokenName := encryptionTokenPath(cfg)
fd, err := cfg.Filesystem(nil).OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0666)
fd, err := cfg.Filesystem(nil).OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0o666)
if err != nil {
return err
}

View File

@@ -271,7 +271,7 @@ func BenchmarkRequestOut(b *testing.B) {
fc := newFakeConnection(device1, m)
for _, f := range files {
fc.addFile(f.Name, 0644, protocol.FileInfoTypeFile, []byte("some data to return"))
fc.addFile(f.Name, 0o644, protocol.FileInfoTypeFile, []byte("some data to return"))
}
m.AddConnection(fc, protocol.Hello{})
must(b, m.Index(device1, "default", files))
@@ -296,7 +296,7 @@ func BenchmarkRequestInSingleFile(b *testing.B) {
rand.Read(buf)
mustRemove(b, defaultFs.RemoveAll("request"))
defer func() { mustRemove(b, defaultFs.RemoveAll("request")) }()
must(b, defaultFs.MkdirAll("request/for/a/file/in/a/couple/of/dirs", 0755))
must(b, defaultFs.MkdirAll("request/for/a/file/in/a/couple/of/dirs", 0o755))
writeFile(b, defaultFs, "request/for/a/file/in/a/couple/of/dirs/128k", buf)
b.ResetTimer()
@@ -1148,8 +1148,8 @@ func TestAutoAcceptNameConflict(t *testing.T) {
id := srand.String(8)
label := srand.String(8)
testOs.MkdirAll(id, 0777)
testOs.MkdirAll(label, 0777)
testOs.MkdirAll(id, 0o777)
testOs.MkdirAll(label, 0o777)
defer os.RemoveAll(id)
defer os.RemoveAll(label)
m, cancel := newState(t, defaultAutoAcceptCfg)
@@ -1198,7 +1198,7 @@ func TestAutoAcceptFallsBackToID(t *testing.T) {
id := srand.String(8)
label := srand.String(8)
t.Log(id, label)
testOs.MkdirAll(label, 0777)
testOs.MkdirAll(label, 0o777)
defer os.RemoveAll(label)
defer os.RemoveAll(id)
defer cleanupModel(m)
@@ -1330,7 +1330,8 @@ func TestAutoAcceptEnc(t *testing.T) {
Folders: []protocol.Folder{{
ID: id,
Label: id,
}}}
}},
}
}
// Earlier tests might cause the connection to get closed, thus ClusterConfig
@@ -1486,7 +1487,7 @@ func changeIgnores(t *testing.T, m *testModel, expected []string) {
func TestIgnores(t *testing.T) {
// Assure a clean start state
mustRemove(t, defaultFs.RemoveAll(config.DefaultMarkerName))
mustRemove(t, defaultFs.MkdirAll(config.DefaultMarkerName, 0644))
mustRemove(t, defaultFs.MkdirAll(config.DefaultMarkerName, 0o644))
writeFile(t, defaultFs, ".stignore", []byte(".*\nquux\n"))
m := setupModel(t, defaultCfgWrapper)
@@ -1548,7 +1549,7 @@ func TestIgnores(t *testing.T) {
func TestEmptyIgnores(t *testing.T) {
// Assure a clean start state
mustRemove(t, defaultFs.RemoveAll(config.DefaultMarkerName))
must(t, defaultFs.MkdirAll(config.DefaultMarkerName, 0644))
must(t, defaultFs.MkdirAll(config.DefaultMarkerName, 0o644))
m := setupModel(t, defaultCfgWrapper)
defer cleanupModel(m)
@@ -1634,7 +1635,7 @@ func TestROScanRecovery(t *testing.T) {
waitForState(t, sub, "default", "folder path missing")
testOs.Mkdir(fcfg.Path, 0700)
testOs.Mkdir(fcfg.Path, 0o700)
waitForState(t, sub, "default", config.ErrMarkerMissing.Error())
@@ -1687,7 +1688,7 @@ func TestRWScanRecovery(t *testing.T) {
waitForState(t, sub, "default", "folder path missing")
testOs.Mkdir(fcfg.Path, 0700)
testOs.Mkdir(fcfg.Path, 0o700)
waitForState(t, sub, "default", config.ErrMarkerMissing.Error())
@@ -2147,10 +2148,10 @@ func TestIssue2782(t *testing.T) {
if err := os.RemoveAll(testDir); err != nil {
t.Skip(err)
}
if err := os.MkdirAll(testDir+"/syncdir", 0755); err != nil {
if err := os.MkdirAll(testDir+"/syncdir", 0o755); err != nil {
t.Skip(err)
}
if err := os.WriteFile(testDir+"/syncdir/file", []byte("hello, world\n"), 0644); err != nil {
if err := os.WriteFile(testDir+"/syncdir/file", []byte("hello, world\n"), 0o644); err != nil {
t.Skip(err)
}
if err := os.Symlink("syncdir", testDir+"/synclink"); err != nil {
@@ -2480,7 +2481,7 @@ func TestIssue2571(t *testing.T) {
defer os.RemoveAll(testFs.URI())
for _, dir := range []string{"toLink", "linkTarget"} {
must(t, testFs.MkdirAll(dir, 0775))
must(t, testFs.MkdirAll(dir, 0o775))
fd, err := testFs.Create(filepath.Join(dir, "a"))
must(t, err)
fd.Close()
@@ -2518,8 +2519,8 @@ func TestIssue4573(t *testing.T) {
testFs := fcfg.Filesystem(nil)
defer os.RemoveAll(testFs.URI())
must(t, testFs.MkdirAll("inaccessible", 0755))
defer testFs.Chmod("inaccessible", 0777)
must(t, testFs.MkdirAll("inaccessible", 0o755))
defer testFs.Chmod("inaccessible", 0o777)
file := filepath.Join("inaccessible", "a")
fd, err := testFs.Create(file)
@@ -2529,7 +2530,7 @@ func TestIssue4573(t *testing.T) {
m := setupModel(t, w)
defer cleanupModel(m)
must(t, testFs.Chmod("inaccessible", 0000))
must(t, testFs.Chmod("inaccessible", 0o000))
m.ScanFolder("default")
@@ -2561,7 +2562,7 @@ func TestInternalScan(t *testing.T) {
for _, dir := range baseDirs {
sub := filepath.Join(dir, "subDir")
for _, dir := range []string{dir, sub} {
if err := testFs.MkdirAll(dir, 0775); err != nil {
if err := testFs.MkdirAll(dir, 0o775); err != nil {
t.Fatalf("%v: %v", dir, err)
}
}
@@ -2633,7 +2634,7 @@ func TestCustomMarkerName(t *testing.T) {
waitForState(t, sub, "default", "folder path missing")
testOs.Mkdir(fcfg.Path, 0700)
testOs.Mkdir(fcfg.Path, 0o700)
fd := testOs.Create(filepath.Join(fcfg.Path, "myfile"))
fd.Close()
@@ -2646,7 +2647,7 @@ func TestRemoveDirWithContent(t *testing.T) {
tfs := fcfg.Filesystem(nil)
defer cleanupModelAndRemoveDir(m, tfs.URI())
tfs.MkdirAll("dirwith", 0755)
tfs.MkdirAll("dirwith", 0o755)
content := filepath.Join("dirwith", "content")
fd, err := tfs.Create(content)
must(t, err)
@@ -2712,7 +2713,7 @@ func TestIssue4475(t *testing.T) {
// This should result in the directory being recreated and added to the
// db locally.
must(t, testFs.MkdirAll("delDir", 0755))
must(t, testFs.MkdirAll("delDir", 0o755))
m.ScanFolder("default")
@@ -2721,7 +2722,7 @@ func TestIssue4475(t *testing.T) {
}
fileName := filepath.Join("delDir", "file")
conn.addFile(fileName, 0644, protocol.FileInfoTypeFile, nil)
conn.addFile(fileName, 0o644, protocol.FileInfoTypeFile, nil)
conn.sendIndexUpdate()
// Is there something we could trigger on instead of just waiting?
@@ -2805,7 +2806,7 @@ func TestVersionRestore(t *testing.T) {
file = filepath.FromSlash(file)
}
dir := filepath.Dir(file)
must(t, filesystem.MkdirAll(dir, 0755))
must(t, filesystem.MkdirAll(dir, 0o755))
if fd, err := filesystem.Create(file); err != nil {
t.Fatal(err)
} else if _, err := fd.Write([]byte(file)); err != nil {
@@ -3185,7 +3186,7 @@ func TestConnCloseOnRestart(t *testing.T) {
br := &testutils.BlockingRW{}
nw := &testutils.NoopRW{}
m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil), protocol.Hello{})
m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil, m.keyGen), protocol.Hello{})
m.pmut.RLock()
if len(m.closed) != 1 {
t.Fatalf("Expected just one conn (len(m.conn) == %v)", len(m.conn))
@@ -3654,7 +3655,7 @@ func TestBlockListMap(t *testing.T) {
// Change type
must(t, ffs.Remove("four"))
must(t, ffs.Mkdir("four", 0644))
must(t, ffs.Mkdir("four", 0o644))
m.ScanFolders()
@@ -3933,7 +3934,7 @@ func TestIssue6961(t *testing.T) {
// Remote, invalid (receive-only) and existing file
must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}}))
// Create a local file
if fd, err := tfs.OpenFile(name, fs.OptCreate, 0666); err != nil {
if fd, err := tfs.OpenFile(name, fs.OptCreate, 0o666); err != nil {
t.Fatal(err)
} else {
fd.Close()
@@ -4038,7 +4039,7 @@ func TestCcCheckEncryption(t *testing.T) {
defer cleanupModel(m)
pw := "foo"
token := protocol.PasswordToken(fcfg.ID, pw)
token := protocol.PasswordToken(m.keyGen, fcfg.ID, pw)
m.folderEncryptionPasswordTokens[fcfg.ID] = token
testCases := []struct {

View File

@@ -55,7 +55,7 @@ func TestRequestSimple(t *testing.T) {
// Send an update for the test file, wait for it to sync and be reported back.
contents := []byte("test file contents\n")
fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
fc.sendIndexUpdate()
select {
case <-done:
@@ -101,7 +101,7 @@ func TestSymlinkTraversalRead(t *testing.T) {
// Send an update for the symlink, wait for it to sync and be reported back.
contents := []byte("..")
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
fc.addFile("symlink", 0o644, protocol.FileInfoTypeSymlink, contents)
fc.sendIndexUpdate()
<-done
@@ -151,7 +151,7 @@ func TestSymlinkTraversalWrite(t *testing.T) {
// Send an update for the symlink, wait for it to sync and be reported back.
contents := []byte("..")
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
fc.addFile("symlink", 0o644, protocol.FileInfoTypeSymlink, contents)
fc.sendIndexUpdate()
<-done
@@ -159,9 +159,9 @@ func TestSymlinkTraversalWrite(t *testing.T) {
// blocks for any of them to come back, or index entries. Hopefully none
// of that should happen.
contents = []byte("testdata testdata\n")
fc.addFile("symlink/testfile", 0644, protocol.FileInfoTypeFile, contents)
fc.addFile("symlink/testdir", 0644, protocol.FileInfoTypeDirectory, contents)
fc.addFile("symlink/testsyml", 0644, protocol.FileInfoTypeSymlink, contents)
fc.addFile("symlink/testfile", 0o644, protocol.FileInfoTypeFile, contents)
fc.addFile("symlink/testdir", 0o644, protocol.FileInfoTypeDirectory, contents)
fc.addFile("symlink/testsyml", 0o644, protocol.FileInfoTypeSymlink, contents)
fc.sendIndexUpdate()
select {
@@ -203,7 +203,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
})
// Send an update for the test file, wait for it to sync and be reported back.
fc.addFile(name, 0644, protocol.FileInfoTypeSymlink, []byte(".."))
fc.addFile(name, 0o644, protocol.FileInfoTypeSymlink, []byte(".."))
fc.sendIndexUpdate()
select {
@@ -257,7 +257,7 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) {
}
// Send an update for the test file, wait for it to sync and be reported back.
fc.addFile("foo", 0644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
fc.addFile("foo", 0o644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
fc.sendIndexUpdate()
waitForIdx()
@@ -267,8 +267,8 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) {
waitForIdx()
// Recreate foo and a file in it with some data
fc.updateFile("foo", 0755, protocol.FileInfoTypeDirectory, nil)
fc.addFile("foo/test", 0644, protocol.FileInfoTypeFile, []byte("testtesttest"))
fc.updateFile("foo", 0o755, protocol.FileInfoTypeDirectory, nil)
fc.addFile("foo/test", 0o644, protocol.FileInfoTypeFile, []byte("testtesttest"))
fc.sendIndexUpdate()
waitForIdx()
@@ -286,7 +286,6 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) {
func TestPullInvalidIgnoredSO(t *testing.T) {
t.Skip("flaky")
pullInvalidIgnored(t, config.FolderTypeSendOnly)
}
func TestPullInvalidIgnoredSR(t *testing.T) {
@@ -322,11 +321,11 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
ign := "ignoredNonExisting"
ignExisting := "ignoredExisting"
fc.addFile(invIgn, 0644, protocol.FileInfoTypeFile, contents)
fc.addFile(invDel, 0644, protocol.FileInfoTypeFile, contents)
fc.addFile(invIgn, 0o644, protocol.FileInfoTypeFile, contents)
fc.addFile(invDel, 0o644, protocol.FileInfoTypeFile, contents)
fc.deleteFile(invDel)
fc.addFile(ign, 0644, protocol.FileInfoTypeFile, contents)
fc.addFile(ignExisting, 0644, protocol.FileInfoTypeFile, contents)
fc.addFile(ign, 0o644, protocol.FileInfoTypeFile, contents)
fc.addFile(ignExisting, 0o644, protocol.FileInfoTypeFile, contents)
writeFile(t, fss, ignExisting, otherContents)
done := make(chan struct{})
@@ -549,8 +548,8 @@ func TestParentDeletion(t *testing.T) {
child := filepath.Join(parent, "bar")
received := make(chan []protocol.FileInfo)
fc.addFile(parent, 0777, protocol.FileInfoTypeDirectory, nil)
fc.addFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
fc.addFile(parent, 0o777, protocol.FileInfoTypeDirectory, nil)
fc.addFile(child, 0o777, protocol.FileInfoTypeDirectory, nil)
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
received <- fs
return nil
@@ -588,7 +587,7 @@ func TestParentDeletion(t *testing.T) {
}
// Recreate the child dir on the remote
fc.updateFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
fc.updateFile(child, 0o777, protocol.FileInfoTypeDirectory, nil)
fc.sendIndexUpdate()
// Wait for the child dir to be recreated and sent to the remote
@@ -634,7 +633,7 @@ func TestRequestSymlinkWindows(t *testing.T) {
return nil
})
fc.addFile("link", 0644, protocol.FileInfoTypeSymlink, nil)
fc.addFile("link", 0o644, protocol.FileInfoTypeSymlink, nil)
fc.sendIndexUpdate()
select {
@@ -714,7 +713,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
b: []byte("bData"),
}
for _, n := range [2]string{a, b} {
fc.addFile(n, 0644, protocol.FileInfoTypeFile, data[n])
fc.addFile(n, 0o644, protocol.FileInfoTypeFile, data[n])
}
fc.sendIndexUpdate()
select {
@@ -772,7 +771,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
return nil
})
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0644)
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
if err != nil {
t.Fatal(err)
}
@@ -784,7 +783,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
// rename
fc.deleteFile(a)
fc.updateFile(b, 0644, protocol.FileInfoTypeFile, data[a])
fc.updateFile(b, 0o644, protocol.FileInfoTypeFile, data[a])
// Make sure the remote file for b is newer and thus stays global -> local conflict
fc.mut.Lock()
for i := range fc.files {
@@ -843,7 +842,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
b: []byte("bData"),
}
for _, n := range [2]string{a, b} {
fc.addFile(n, 0644, protocol.FileInfoTypeFile, data[n])
fc.addFile(n, 0o644, protocol.FileInfoTypeFile, data[n])
}
fc.sendIndexUpdate()
select {
@@ -859,7 +858,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
}
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0644)
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
if err != nil {
t.Fatal(err)
}
@@ -883,7 +882,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
// rename
fc.deleteFile(a)
fc.updateFile(b, 0644, protocol.FileInfoTypeFile, data[a])
fc.updateFile(b, 0o644, protocol.FileInfoTypeFile, data[a])
fc.sendIndexUpdate()
select {
case <-recv:
@@ -933,7 +932,7 @@ func TestRequestDeleteChanged(t *testing.T) {
// setup
a := "a"
data := []byte("aData")
fc.addFile(a, 0644, protocol.FileInfoTypeFile, data)
fc.addFile(a, 0o644, protocol.FileInfoTypeFile, data)
fc.sendIndexUpdate()
select {
case <-done:
@@ -952,7 +951,7 @@ func TestRequestDeleteChanged(t *testing.T) {
return nil
})
fd, err := tfs.OpenFile(a, fs.OptReadWrite, 0644)
fd, err := tfs.OpenFile(a, fs.OptReadWrite, 0o644)
if err != nil {
t.Fatal(err)
}
@@ -999,7 +998,7 @@ func TestNeedFolderFiles(t *testing.T) {
data := []byte("foo")
num := 20
for i := 0; i < num; i++ {
fc.addFile(strconv.Itoa(i), 0644, protocol.FileInfoTypeFile, data)
fc.addFile(strconv.Itoa(i), 0o644, protocol.FileInfoTypeFile, data)
}
fc.sendIndexUpdate()
@@ -1146,7 +1145,7 @@ func TestRequestLastFileProgress(t *testing.T) {
})
contents := []byte("test file contents\n")
fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
fc.sendIndexUpdate()
select {
@@ -1280,7 +1279,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
dir2 := "bar"
// Initialise db with an entry and then stop everything again
must(t, tfs.Mkdir(dir1, 0777))
must(t, tfs.Mkdir(dir1, 0o777))
m := newModel(t, w, myID, "syncthing", "dev", nil)
defer cleanupModelAndRemoveDir(m, tfs.URI())
m.ServeBackground()
@@ -1290,7 +1289,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
// Add connection (sends incoming cluster config) before starting the new model
m = &testModel{
model: NewModel(m.cfg, m.id, m.clientName, m.clientVersion, m.db, m.protectedFiles, m.evLogger).(*model),
model: NewModel(m.cfg, m.id, m.clientName, m.clientVersion, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model),
evCancel: m.evCancel,
stopped: make(chan struct{}),
}
@@ -1326,7 +1325,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
}
// Check that an index is sent for the newly added item
must(t, tfs.Mkdir(dir2, 0777))
must(t, tfs.Mkdir(dir2, 0o777))
m.ScanFolders()
select {
case <-timeout:
@@ -1346,8 +1345,9 @@ func TestRequestReceiveEncrypted(t *testing.T) {
fcfg.Type = config.FolderTypeReceiveEncrypted
setFolder(t, w, fcfg)
encToken := protocol.PasswordToken(fcfg.ID, "pw")
must(t, tfs.Mkdir(config.DefaultMarkerName, 0777))
keyGen := protocol.NewKeyGenerator()
encToken := protocol.PasswordToken(keyGen, fcfg.ID, "pw")
must(t, tfs.Mkdir(config.DefaultMarkerName, 0o777))
must(t, writeEncryptionToken(encToken, fcfg))
m := setupModel(t, w)
@@ -1407,7 +1407,7 @@ func TestRequestReceiveEncrypted(t *testing.T) {
name := "foo"
data := make([]byte, 2000)
rand.Read(data)
fc.addFile(name, 0664, protocol.FileInfoTypeFile, data)
fc.addFile(name, 0o664, protocol.FileInfoTypeFile, data)
fc.sendIndexUpdate()
select {
@@ -1467,7 +1467,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) {
// Setup device with valid file, do not send index yet
contents := []byte("test file contents\n")
fc.addFile(name, 0644, protocol.FileInfoTypeFile, contents)
fc.addFile(name, 0o644, protocol.FileInfoTypeFile, contents)
// Third device ignoring the same file
fc.mut.Lock()

View File

@@ -154,7 +154,7 @@ func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, clientName
if err != nil {
t.Fatal(err)
}
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger).(*model)
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
ctx, cancel := context.WithCancel(context.Background())
go evLogger.Serve(ctx)
return &testModel{

View File

@@ -60,9 +60,9 @@ func benchmarkRequestsTLS(b *testing.B, conn0, conn1 net.Conn) {
func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
// Start up Connections on them
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil)
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
c0.Start()
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil)
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
c1.Start()
// Satisfy the assertions in the protocol by sending an initial cluster config

View File

@@ -517,9 +517,12 @@ type FileInfo struct {
// The time when the inode was last changed (i.e., permissions, xattrs
// etc changed). This is host-local, not sent over the wire.
InodeChangeNs int64 `protobuf:"varint,1002,opt,name=inode_change_ns,json=inodeChangeNs,proto3" json:"inodeChangeNs" xml:"inodeChangeNs"`
Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted" xml:"deleted"`
RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid" xml:"invalid"`
NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"noPermissions" xml:"noPermissions"`
// The size of the data appended to the encrypted file on disk. This is
// host-local, not sent over the wire.
EncryptionTrailerSize int `protobuf:"varint,1003,opt,name=encryption_trailer_size,json=encryptionTrailerSize,proto3,casttype=int" json:"encryptionTrailerSize" xml:"encryptionTrailerSize"`
Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted" xml:"deleted"`
RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid" xml:"invalid"`
NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"noPermissions" xml:"noPermissions"`
}
func (m *FileInfo) Reset() { *m = FileInfo{} }
@@ -1139,203 +1142,205 @@ func init() {
func init() { proto.RegisterFile("lib/protocol/bep.proto", fileDescriptor_311ef540e10d9705) }
var fileDescriptor_311ef540e10d9705 = []byte{
// 3122 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4f, 0x6c, 0x1b, 0xc7,
0xb9, 0x17, 0xc5, 0x3f, 0xa2, 0x46, 0xb2, 0x4d, 0x8d, 0xff, 0x31, 0xb4, 0xad, 0xe5, 0x9b, 0x38,
0xef, 0x29, 0xca, 0x8b, 0x9d, 0x28, 0x7f, 0x5e, 0x5e, 0x9c, 0xe7, 0x40, 0x14, 0x29, 0x99, 0xb1,
0x4c, 0x2a, 0x43, 0xd9, 0x8e, 0x8d, 0x57, 0x10, 0x2b, 0xee, 0x88, 0x5a, 0x98, 0xdc, 0x65, 0x77,
0x49, 0xfd, 0x09, 0x7a, 0x69, 0x03, 0x14, 0x81, 0x50, 0x14, 0x45, 0x4e, 0x45, 0x51, 0xa1, 0x41,
0x2f, 0xbd, 0x15, 0xe8, 0xa1, 0x97, 0x9c, 0x7a, 0xf4, 0xd1, 0x08, 0x50, 0xa0, 0xe8, 0x61, 0x81,
0xd8, 0x97, 0x96, 0x47, 0x1e, 0x7b, 0x2a, 0xe6, 0x9b, 0xd9, 0xd9, 0x59, 0xc9, 0x4a, 0xe5, 0xe4,
0xd0, 0x93, 0xf9, 0xfd, 0xbe, 0xdf, 0xf7, 0xed, 0xec, 0xcc, 0xf7, 0x6f, 0x56, 0x46, 0x17, 0x3a,
0xf6, 0xc6, 0xf5, 0x9e, 0xe7, 0xf6, 0xdd, 0x96, 0xdb, 0xb9, 0xbe, 0xc1, 0x7a, 0xd7, 0x40, 0xc0,
0xd9, 0x10, 0x2b, 0x4c, 0xb2, 0xdd, 0xbe, 0x00, 0x0b, 0x2f, 0x7b, 0xac, 0xe7, 0xfa, 0x82, 0xbe,
0x31, 0xd8, 0xbc, 0xde, 0x76, 0xdb, 0x2e, 0x08, 0xf0, 0x4b, 0x90, 0xc8, 0xd3, 0x04, 0x4a, 0xdf,
0x62, 0x9d, 0x8e, 0x8b, 0x97, 0xd0, 0x94, 0xc5, 0xb6, 0xed, 0x16, 0x6b, 0x3a, 0x66, 0x97, 0xe5,
0x13, 0xc5, 0xc4, 0xdc, 0x64, 0x89, 0x0c, 0x03, 0x03, 0x09, 0xb8, 0x66, 0x76, 0xd9, 0x28, 0x30,
0x72, 0xbb, 0xdd, 0xce, 0xfb, 0x24, 0x82, 0x08, 0xd5, 0xf4, 0xdc, 0x49, 0xab, 0x63, 0x33, 0xa7,
0x2f, 0x9c, 0x8c, 0x47, 0x4e, 0x04, 0x1c, 0x73, 0x12, 0x41, 0x84, 0x6a, 0x7a, 0x5c, 0x47, 0xa7,
0xa5, 0x93, 0x6d, 0xe6, 0xf9, 0xb6, 0xeb, 0xe4, 0x93, 0xe0, 0x67, 0x6e, 0x18, 0x18, 0xa7, 0x84,
0xe6, 0x9e, 0x50, 0x8c, 0x02, 0xe3, 0xac, 0xe6, 0x4a, 0xa2, 0x84, 0xc6, 0x59, 0xe4, 0x0f, 0x09,
0x94, 0xb9, 0xc5, 0x4c, 0x8b, 0x79, 0x78, 0x11, 0xa5, 0xfa, 0x7b, 0x3d, 0xf1, 0x7a, 0xa7, 0x17,
0xce, 0x5f, 0x0b, 0x37, 0xee, 0xda, 0x1d, 0xe6, 0xfb, 0x66, 0x9b, 0xad, 0xef, 0xf5, 0x58, 0xe9,
0xc2, 0x30, 0x30, 0x80, 0x36, 0x0a, 0x0c, 0x04, 0xfe, 0xb9, 0x40, 0x28, 0x60, 0xd8, 0x42, 0x53,
0x2d, 0xb7, 0xdb, 0xf3, 0x98, 0x0f, 0x6b, 0x1b, 0x07, 0x4f, 0x97, 0x8f, 0x78, 0x5a, 0x8a, 0x38,
0xa5, 0xab, 0xc3, 0xc0, 0xd0, 0x8d, 0x46, 0x81, 0x31, 0x23, 0xd6, 0x1d, 0x61, 0x84, 0xea, 0x0c,
0xf2, 0xff, 0xe8, 0xd4, 0x52, 0x67, 0xe0, 0xf7, 0x99, 0xb7, 0xe4, 0x3a, 0x9b, 0x76, 0x1b, 0xdf,
0x46, 0x13, 0x9b, 0x6e, 0xc7, 0x62, 0x9e, 0x9f, 0x4f, 0x14, 0x93, 0x73, 0x53, 0x0b, 0xb9, 0xe8,
0x91, 0xcb, 0xa0, 0x28, 0x19, 0x8f, 0x03, 0x63, 0x6c, 0x18, 0x18, 0x21, 0x71, 0x14, 0x18, 0xd3,
0xf0, 0x18, 0x21, 0x13, 0x1a, 0x2a, 0xc8, 0x57, 0x29, 0x94, 0x11, 0x46, 0xf8, 0x1a, 0x1a, 0xb7,
0x2d, 0x79, 0xdc, 0xb3, 0x4f, 0x03, 0x63, 0xbc, 0x5a, 0x1e, 0x06, 0xc6, 0xb8, 0x6d, 0x8d, 0x02,
0x23, 0x0b, 0xd6, 0xb6, 0x45, 0xbe, 0x78, 0x72, 0x75, 0xbc, 0x5a, 0xa6, 0xe3, 0xb6, 0x85, 0xaf,
0xa1, 0x74, 0xc7, 0xdc, 0x60, 0x1d, 0x79, 0xb8, 0xf9, 0x61, 0x60, 0x08, 0x60, 0x14, 0x18, 0x53,
0xc0, 0x07, 0x89, 0x50, 0x81, 0xe2, 0x1b, 0x68, 0xd2, 0x63, 0xa6, 0xd5, 0x74, 0x9d, 0xce, 0x1e,
0x1c, 0x64, 0xb6, 0x34, 0x3b, 0x0c, 0x8c, 0x2c, 0x07, 0xeb, 0x4e, 0x67, 0x6f, 0x14, 0x18, 0xa7,
0xc1, 0x2c, 0x04, 0x08, 0x55, 0x3a, 0xdc, 0x44, 0xd8, 0x6e, 0x3b, 0xae, 0xc7, 0x9a, 0x3d, 0xe6,
0x75, 0x6d, 0xd8, 0x1a, 0x3f, 0x9f, 0x02, 0x2f, 0x6f, 0x0c, 0x03, 0x63, 0x46, 0x68, 0xd7, 0x22,
0xe5, 0x28, 0x30, 0x2e, 0x8a, 0x55, 0x1f, 0xd6, 0x10, 0x7a, 0x94, 0x8d, 0x6f, 0xa3, 0x53, 0xf2,
0x01, 0x16, 0xeb, 0xb0, 0x3e, 0xcb, 0xa7, 0xc1, 0xf7, 0x7f, 0x0e, 0x03, 0x63, 0x5a, 0x28, 0xca,
0x80, 0x8f, 0x02, 0x03, 0x6b, 0x6e, 0x05, 0x48, 0x68, 0x8c, 0x83, 0x2d, 0x74, 0xce, 0xb2, 0x7d,
0x73, 0xa3, 0xc3, 0x9a, 0x7d, 0xd6, 0xed, 0x35, 0x6d, 0xc7, 0x62, 0xbb, 0xcc, 0xcf, 0x67, 0xc0,
0xe7, 0xc2, 0x30, 0x30, 0xb0, 0xd4, 0xaf, 0xb3, 0x6e, 0xaf, 0x2a, 0xb4, 0xa3, 0xc0, 0xc8, 0x8b,
0x9c, 0x3a, 0xa2, 0x22, 0xf4, 0x39, 0x7c, 0xbc, 0x80, 0x32, 0x3d, 0x73, 0xe0, 0x33, 0x2b, 0x3f,
0x01, 0x7e, 0x0b, 0xc3, 0xc0, 0x90, 0x88, 0x3a, 0x70, 0x21, 0x12, 0x2a, 0x71, 0x1e, 0x3c, 0x22,
0x4b, 0xfd, 0x7c, 0xee, 0x70, 0xf0, 0x94, 0x41, 0x11, 0x05, 0x8f, 0x24, 0x2a, 0x5f, 0x42, 0x26,
0x34, 0x54, 0x90, 0x3f, 0x65, 0x50, 0x46, 0x18, 0xe1, 0x92, 0x0a, 0x9e, 0xe9, 0xd2, 0x02, 0x77,
0xf0, 0xd7, 0xc0, 0xc8, 0x0a, 0x5d, 0xb5, 0x7c, 0x5c, 0x30, 0x7d, 0xfe, 0xe4, 0x6a, 0x42, 0x0b,
0xa8, 0x79, 0x94, 0xd2, 0x8a, 0x05, 0xe4, 0x9e, 0x23, 0xca, 0x84, 0xc8, 0x3d, 0x07, 0x0a, 0x04,
0x60, 0xf8, 0x03, 0x34, 0x69, 0x5a, 0x16, 0xcf, 0x11, 0xe6, 0xe7, 0x93, 0xc5, 0x24, 0x8f, 0xd9,
0x61, 0x60, 0x44, 0xe0, 0x28, 0x30, 0x4e, 0x81, 0x95, 0x44, 0x08, 0x8d, 0x74, 0xf8, 0x07, 0xf1,
0xcc, 0x4d, 0x1d, 0xae, 0x01, 0xdf, 0x2f, 0x65, 0x79, 0xa4, 0xb7, 0x98, 0x27, 0x4b, 0x5f, 0x5a,
0x24, 0x14, 0x8f, 0x74, 0x0e, 0xca, 0xc2, 0x27, 0x22, 0x3d, 0x04, 0x08, 0x55, 0x3a, 0xbc, 0x82,
0xa6, 0xbb, 0xe6, 0x6e, 0xd3, 0x67, 0x3f, 0x1c, 0x30, 0xa7, 0xc5, 0x20, 0x66, 0x92, 0x62, 0x15,
0x5d, 0x73, 0xb7, 0x21, 0x61, 0xb5, 0x0a, 0x0d, 0x23, 0x54, 0x67, 0xe0, 0x12, 0x42, 0xb6, 0xd3,
0xf7, 0x5c, 0x6b, 0xd0, 0x62, 0x9e, 0x0c, 0x11, 0xa8, 0xc0, 0x11, 0xaa, 0x2a, 0x70, 0x04, 0x11,
0xaa, 0xe9, 0x71, 0x1b, 0x65, 0x21, 0x76, 0x9b, 0xb6, 0x95, 0xcf, 0x16, 0x13, 0x73, 0xa9, 0xd2,
0xaa, 0x3c, 0xdc, 0x09, 0x88, 0x42, 0x38, 0xdb, 0xf0, 0x27, 0x8f, 0x19, 0x60, 0x57, 0x2d, 0xb5,
0xfb, 0x52, 0xe6, 0x75, 0x23, 0xa4, 0xfd, 0x2a, 0xfa, 0x49, 0x43, 0x3e, 0xfe, 0x11, 0x2a, 0xf8,
0x8f, 0x6c, 0x9e, 0x29, 0xe2, 0xd9, 0x7d, 0xdb, 0x75, 0x9a, 0x1e, 0xeb, 0xba, 0xdb, 0x66, 0xc7,
0xcf, 0x4f, 0xc2, 0xe2, 0x6f, 0x0e, 0x03, 0x23, 0xcf, 0x59, 0x55, 0x8d, 0x44, 0x25, 0x67, 0x14,
0x18, 0xb3, 0xf0, 0xc4, 0xe3, 0x08, 0x84, 0x1e, 0x6b, 0x8b, 0x77, 0xd1, 0x4b, 0xcc, 0x69, 0x79,
0x7b, 0x3d, 0x78, 0x6c, 0xcf, 0xf4, 0xfd, 0x1d, 0xd7, 0xb3, 0x9a, 0x7d, 0xf7, 0x11, 0x73, 0xf2,
0x08, 0x82, 0xfa, 0x83, 0x61, 0x60, 0x5c, 0x8c, 0x48, 0x6b, 0x92, 0xb3, 0xce, 0x29, 0xa3, 0xc0,
0xb8, 0x02, 0xcf, 0x3e, 0x46, 0x4f, 0xe8, 0x71, 0x96, 0xe4, 0x27, 0x09, 0x94, 0x86, 0xcd, 0xe0,
0xd9, 0x2c, 0x8a, 0xb2, 0x2c, 0xc1, 0x90, 0xcd, 0x02, 0x39, 0x52, 0xbe, 0x25, 0x8e, 0x2b, 0x28,
0xbd, 0x69, 0x77, 0x98, 0x9f, 0x1f, 0x87, 0x5c, 0xc6, 0x5a, 0x23, 0xb0, 0x3b, 0xac, 0xea, 0x6c,
0xba, 0xa5, 0x4b, 0x32, 0x9b, 0x05, 0x51, 0xe5, 0x12, 0x97, 0x08, 0x15, 0x20, 0xf9, 0x3c, 0x81,
0xa6, 0x60, 0x11, 0x77, 0x7b, 0x96, 0xd9, 0x67, 0xff, 0xce, 0xa5, 0xfc, 0x6c, 0x1a, 0x65, 0x43,
0x03, 0x55, 0x10, 0x12, 0x27, 0x28, 0x08, 0xf3, 0x28, 0xe5, 0xdb, 0x9f, 0x32, 0x68, 0x2c, 0x49,
0xc1, 0xe5, 0xb2, 0xe2, 0x72, 0x81, 0x50, 0xc0, 0xf0, 0x87, 0x08, 0x75, 0x5d, 0xcb, 0xde, 0xb4,
0x99, 0xd5, 0xf4, 0x21, 0x41, 0x93, 0xa5, 0x22, 0xaf, 0x1e, 0x21, 0xda, 0x18, 0x05, 0xc6, 0x19,
0x91, 0x5e, 0x21, 0x42, 0x68, 0xa4, 0xe5, 0xf5, 0x43, 0x39, 0xd8, 0xd8, 0xcb, 0x4f, 0x43, 0x66,
0x7c, 0x10, 0x66, 0x46, 0x63, 0xcb, 0xf5, 0xfa, 0x90, 0x0e, 0xea, 0x31, 0xa5, 0x3d, 0x95, 0x6a,
0x11, 0x44, 0x78, 0x26, 0x48, 0x32, 0xd5, 0xa8, 0x78, 0x15, 0x4d, 0x84, 0x03, 0x0f, 0x8f, 0xfc,
0x58, 0x91, 0xbe, 0xc7, 0x5a, 0x7d, 0xd7, 0x2b, 0x15, 0xc3, 0x22, 0xbd, 0xad, 0x06, 0x20, 0x91,
0x70, 0xdb, 0xe1, 0xe8, 0x13, 0x6a, 0xf0, 0xfb, 0x28, 0xab, 0x8a, 0x09, 0x82, 0x77, 0x85, 0x62,
0xe4, 0x47, 0x95, 0x44, 0x14, 0x23, 0x5f, 0x95, 0x11, 0xa5, 0xc3, 0x1f, 0xa1, 0xcc, 0x46, 0xc7,
0x6d, 0x3d, 0x0a, 0xbb, 0xc5, 0xd9, 0x68, 0x21, 0x25, 0x8e, 0xc3, 0xb9, 0x5e, 0x91, 0x6b, 0x91,
0x54, 0xd5, 0xfe, 0x41, 0x24, 0x54, 0xc2, 0x7c, 0x9a, 0xf3, 0xf7, 0xba, 0x1d, 0xdb, 0x79, 0xd4,
0xec, 0x9b, 0x5e, 0x9b, 0xf5, 0xf3, 0x33, 0xd1, 0x34, 0x27, 0x35, 0xeb, 0xa0, 0x50, 0xd3, 0x5c,
0x0c, 0x25, 0x34, 0xce, 0xe2, 0x33, 0xa6, 0x70, 0xdd, 0xdc, 0x32, 0xfd, 0xad, 0x3c, 0x86, 0x3c,
0x85, 0x0a, 0x27, 0xe0, 0x5b, 0xa6, 0xbf, 0xa5, 0xb6, 0x3d, 0x82, 0x08, 0xd5, 0xf4, 0xf8, 0x26,
0x9a, 0x94, 0xb9, 0xc9, 0xac, 0xfc, 0x59, 0x70, 0x01, 0xa1, 0xa0, 0x40, 0x15, 0x0a, 0x0a, 0x21,
0x34, 0xd2, 0xe2, 0x92, 0x9c, 0x23, 0xc5, 0xf4, 0x77, 0xe1, 0x68, 0xd8, 0x9f, 0x60, 0x90, 0x5c,
0x46, 0x53, 0x87, 0xa7, 0x9a, 0x53, 0xa2, 0xe2, 0xf7, 0x62, 0xf3, 0x8c, 0xa8, 0xf8, 0x3d, 0x7d,
0x92, 0xd1, 0x19, 0xf8, 0x23, 0x2d, 0x2c, 0x1d, 0x3f, 0x3f, 0x55, 0x4c, 0xcc, 0xa5, 0x4b, 0xaf,
0xea, 0x71, 0x58, 0xf3, 0x8f, 0xc4, 0x61, 0xcd, 0x27, 0xff, 0x08, 0x8c, 0xa4, 0xed, 0xf4, 0xa9,
0x46, 0xc3, 0x9b, 0x48, 0xec, 0x52, 0x13, 0xb2, 0xea, 0x14, 0xb8, 0x5a, 0x79, 0x1a, 0x18, 0xd3,
0xd4, 0xdc, 0x81, 0xa3, 0x6f, 0xd8, 0x9f, 0x32, 0xbe, 0x51, 0x1b, 0xa1, 0xa0, 0x36, 0x4a, 0x21,
0xa1, 0xe3, 0x2f, 0x9e, 0x5c, 0x8d, 0x99, 0xd1, 0xc8, 0x08, 0xdf, 0x43, 0xd9, 0x5e, 0xc7, 0xec,
0x6f, 0xba, 0x5e, 0x37, 0x7f, 0x1a, 0x82, 0x5d, 0xdb, 0xc3, 0x35, 0xa9, 0x29, 0x9b, 0x7d, 0xb3,
0x44, 0x64, 0x98, 0x29, 0xbe, 0x8a, 0xdc, 0x10, 0x20, 0x54, 0xe9, 0x70, 0x19, 0x4d, 0x75, 0xdc,
0x96, 0xd9, 0x69, 0x6e, 0x76, 0xcc, 0xb6, 0x9f, 0xff, 0xdb, 0x04, 0x6c, 0x2a, 0x44, 0x07, 0xe0,
0xcb, 0x1c, 0x56, 0x9b, 0x11, 0x41, 0x84, 0x6a, 0x7a, 0x7c, 0x0b, 0x4d, 0xcb, 0x34, 0x12, 0x31,
0xf6, 0xf7, 0x09, 0x88, 0x10, 0x38, 0x1b, 0xa9, 0x90, 0x51, 0x36, 0xa3, 0x67, 0x9f, 0x08, 0x33,
0x9d, 0x81, 0x3f, 0x46, 0x67, 0x6c, 0xc7, 0xb5, 0x58, 0xb3, 0xb5, 0x65, 0x3a, 0x6d, 0xc6, 0xcf,
0x67, 0x38, 0x01, 0xd9, 0x08, 0xf1, 0x0f, 0xba, 0x25, 0x50, 0xc1, 0x19, 0x9d, 0x95, 0xdd, 0x53,
0x43, 0x09, 0x8d, 0xb3, 0xf0, 0xbb, 0x7c, 0x96, 0xe3, 0xf3, 0xa6, 0x25, 0x07, 0xcb, 0xcb, 0x62,
0x6a, 0x03, 0x48, 0x15, 0x04, 0x29, 0xc3, 0xd8, 0x06, 0xbf, 0x30, 0x45, 0x13, 0xb6, 0xb3, 0x6d,
0x76, 0xec, 0x70, 0x70, 0x7c, 0xef, 0x69, 0x60, 0x20, 0x6a, 0xee, 0x54, 0x05, 0x2a, 0xfa, 0x38,
0xfc, 0xd4, 0xfa, 0x38, 0xc8, 0xbc, 0x8f, 0x6b, 0x4c, 0x1a, 0xf2, 0x78, 0x72, 0x3b, 0x6e, 0x6c,
0x36, 0xcf, 0x82, 0x6b, 0x78, 0x39, 0xc7, 0x8d, 0xcf, 0xe5, 0xe2, 0xe5, 0x62, 0x28, 0xa1, 0x71,
0xd6, 0xfb, 0xa9, 0x5f, 0x7e, 0x69, 0x8c, 0x91, 0x6f, 0x12, 0x68, 0x52, 0x15, 0x1a, 0x5e, 0xe3,
0xe1, 0x14, 0x92, 0x70, 0x08, 0x90, 0x53, 0x5b, 0x62, 0xf7, 0x45, 0x4e, 0x6d, 0xc1, 0xb6, 0x03,
0xc6, 0x7b, 0x98, 0xbb, 0xb9, 0xe9, 0xb3, 0x3e, 0x74, 0x8f, 0xa4, 0xe8, 0x61, 0x02, 0x51, 0x3d,
0x4c, 0x88, 0x84, 0x4a, 0x1c, 0xbf, 0x29, 0x7b, 0xc8, 0x38, 0x44, 0xfb, 0x95, 0xe7, 0xf7, 0x90,
0x30, 0x59, 0x44, 0x2b, 0xb9, 0x81, 0x26, 0x77, 0x98, 0xf9, 0x48, 0x44, 0x87, 0x48, 0x5c, 0xa8,
0xae, 0x1c, 0x94, 0x91, 0x21, 0x62, 0x34, 0x04, 0x08, 0x55, 0x3a, 0xf9, 0x8e, 0x0f, 0x51, 0x46,
0x14, 0x75, 0xbc, 0x86, 0xb2, 0x2d, 0x77, 0xe0, 0xf4, 0xa3, 0xab, 0xdd, 0x8c, 0x3e, 0x93, 0x82,
0xa6, 0xf4, 0x1f, 0x61, 0x1a, 0x84, 0x54, 0x75, 0x46, 0x12, 0xe0, 0xc3, 0xa4, 0x54, 0x91, 0xcf,
0x12, 0x68, 0x42, 0x1a, 0xe2, 0x5b, 0x6a, 0x44, 0x4f, 0x95, 0xde, 0x3b, 0xd4, 0xab, 0xbe, 0xfd,
0xba, 0xa7, 0xf7, 0x29, 0x79, 0xf3, 0xdb, 0x36, 0x3b, 0x03, 0xb1, 0x51, 0x29, 0x71, 0xf3, 0x03,
0x40, 0x95, 0x7e, 0x90, 0x08, 0x15, 0x28, 0xf9, 0x2c, 0x85, 0xa6, 0xf5, 0x54, 0xe6, 0x45, 0x73,
0xe0, 0xd8, 0xbb, 0xb0, 0x98, 0xd8, 0xac, 0x70, 0xd7, 0xb1, 0x77, 0x21, 0xd9, 0x0b, 0x8f, 0x03,
0x23, 0xc1, 0x0f, 0x80, 0xf3, 0xd4, 0x01, 0x70, 0x81, 0x50, 0xc0, 0xf0, 0xc7, 0x68, 0x62, 0xc7,
0x76, 0x2c, 0x77, 0xc7, 0x87, 0x65, 0x4c, 0xe9, 0xf3, 0xfb, 0x7d, 0xa1, 0x00, 0x4f, 0x45, 0xe9,
0x29, 0x64, 0xab, 0xed, 0x92, 0x32, 0xa1, 0xa1, 0x06, 0xaf, 0xa0, 0x74, 0xc7, 0x76, 0x06, 0xbb,
0x10, 0x60, 0xb1, 0x66, 0xf7, 0x89, 0xd9, 0xef, 0x7b, 0xe0, 0xee, 0xb2, 0x74, 0x27, 0x98, 0xd1,
0x55, 0x97, 0x4b, 0xfc, 0xaa, 0xcb, 0xff, 0xc5, 0xb7, 0x51, 0xc6, 0x32, 0xbd, 0x1d, 0x5b, 0x5c,
0x2d, 0x8e, 0xf1, 0x34, 0x2b, 0x3d, 0x49, 0x6a, 0x74, 0xcd, 0x02, 0x91, 0x50, 0x89, 0x63, 0x86,
0x26, 0x36, 0x3d, 0xc6, 0x36, 0x7c, 0x0b, 0x46, 0x95, 0x63, 0xbc, 0xbd, 0xcb, 0xbd, 0xf1, 0x61,
0x7c, 0xd9, 0x63, 0xac, 0xd4, 0x80, 0x61, 0x5c, 0x9a, 0xa9, 0x37, 0x96, 0x32, 0x0c, 0xe3, 0x92,
0x46, 0x43, 0x12, 0x6e, 0xa2, 0x8c, 0xc3, 0xfa, 0xfc, 0x29, 0x99, 0xe3, 0x9f, 0xb2, 0x20, 0x9f,
0x92, 0xa9, 0xb1, 0xbe, 0x78, 0x88, 0x34, 0x52, 0xab, 0x17, 0x22, 0x7f, 0x84, 0xe4, 0x50, 0xc9,
0x20, 0x3f, 0x1d, 0x47, 0xd9, 0xf0, 0x7c, 0xf9, 0x08, 0xe6, 0xee, 0x38, 0xcc, 0xd3, 0xbf, 0x31,
0x41, 0xdf, 0x05, 0x54, 0x5e, 0x92, 0x44, 0x3b, 0x51, 0x08, 0xa1, 0x91, 0x96, 0x3b, 0x68, 0x7b,
0xee, 0xa0, 0xa7, 0x7f, 0x5f, 0x02, 0x07, 0x80, 0xc6, 0x1c, 0x28, 0x84, 0xd0, 0x48, 0x8b, 0x6f,
0xa0, 0xe4, 0xc0, 0xb6, 0xe0, 0xa8, 0xd3, 0xa5, 0x57, 0x9f, 0x06, 0x46, 0xf2, 0x2e, 0x64, 0x00,
0x47, 0x47, 0x81, 0x31, 0x29, 0x02, 0xce, 0xb6, 0xb4, 0x26, 0xc6, 0x19, 0x94, 0xeb, 0xb9, 0x71,
0xdb, 0xb6, 0xe0, 0x74, 0xa5, 0xf1, 0x8a, 0x30, 0x6e, 0x6b, 0xc6, 0xed, 0xb8, 0xf1, 0x0a, 0x37,
0xe6, 0xd8, 0xaf, 0x13, 0x68, 0x4a, 0x8b, 0xd0, 0xef, 0xbf, 0x17, 0xab, 0xe8, 0xb4, 0x70, 0x60,
0xfb, 0x4d, 0x78, 0x41, 0xd8, 0x0f, 0xf9, 0xf1, 0x02, 0x34, 0x55, 0x7f, 0x85, 0xe3, 0xea, 0xe3,
0x85, 0x0e, 0x12, 0x1a, 0xe3, 0x90, 0x06, 0x9a, 0x54, 0x07, 0x8e, 0x97, 0x51, 0x66, 0x97, 0x0b,
0x61, 0x41, 0x3a, 0x73, 0x28, 0x2a, 0xa2, 0xe1, 0x4f, 0xd0, 0x54, 0x42, 0x80, 0x48, 0xa8, 0x84,
0x49, 0x0b, 0xa5, 0x81, 0xff, 0x42, 0x33, 0x7d, 0xac, 0xce, 0x4c, 0xff, 0xeb, 0x3a, 0xf3, 0xe3,
0x14, 0x9a, 0xa0, 0x7c, 0x74, 0xf5, 0xfb, 0xf8, 0x1d, 0x55, 0xed, 0xd2, 0xa5, 0x57, 0x8e, 0x2b,
0x6f, 0xd1, 0xe9, 0x84, 0xdf, 0x20, 0xa2, 0xab, 0xcf, 0xf8, 0x89, 0xaf, 0x3e, 0xe1, 0x2b, 0x25,
0x4f, 0xf0, 0x4a, 0x51, 0x5b, 0x4a, 0xbd, 0x70, 0x5b, 0x4a, 0x9f, 0xbc, 0x2d, 0x85, 0x9d, 0x32,
0x73, 0x82, 0x4e, 0x59, 0x47, 0xa7, 0x37, 0x3d, 0xb7, 0x0b, 0x5f, 0xaa, 0x5c, 0xcf, 0xf4, 0xf6,
0xe4, 0x54, 0x00, 0xad, 0x9b, 0x6b, 0xd6, 0x43, 0x85, 0x6a, 0xdd, 0x31, 0x94, 0xd0, 0x38, 0x2b,
0xde, 0x13, 0xb3, 0x2f, 0xd6, 0x13, 0xf1, 0x4d, 0x94, 0x15, 0x73, 0xa7, 0xe3, 0xc2, 0xe5, 0x27,
0x5d, 0x7a, 0x99, 0x97, 0x32, 0xc0, 0x6a, 0xae, 0x2a, 0x65, 0x52, 0x56, 0xaf, 0x1d, 0x12, 0xc8,
0xef, 0x13, 0x28, 0x4b, 0x99, 0xdf, 0x73, 0x1d, 0x9f, 0x7d, 0xd7, 0x20, 0x98, 0x47, 0x29, 0xcb,
0xec, 0x9b, 0x32, 0xec, 0x60, 0xf7, 0xb8, 0xac, 0x76, 0x8f, 0x0b, 0x84, 0x02, 0x86, 0x3f, 0x44,
0xa9, 0x96, 0x6b, 0x89, 0xc3, 0x3f, 0xad, 0x17, 0xcd, 0x8a, 0xe7, 0xb9, 0xde, 0x92, 0x6b, 0xc9,
0xe1, 0x9f, 0x93, 0x94, 0x03, 0x2e, 0x10, 0x0a, 0x18, 0xf9, 0x5d, 0x02, 0xe5, 0xca, 0xee, 0x8e,
0xd3, 0x71, 0x4d, 0x6b, 0xcd, 0x73, 0xdb, 0x1e, 0xf3, 0xfd, 0xef, 0x74, 0x03, 0x6f, 0xa2, 0x89,
0x01, 0xdc, 0xdf, 0xc3, 0x3b, 0xf8, 0xd5, 0xf8, 0x65, 0xe4, 0xf0, 0x43, 0xc4, 0x65, 0x3f, 0xfa,
0xdc, 0x27, 0x8d, 0x95, 0x7f, 0x21, 0x13, 0x1a, 0x2a, 0xc8, 0x6f, 0x93, 0xa8, 0x70, 0xbc, 0x23,
0xdc, 0x45, 0x53, 0x82, 0xd9, 0xd4, 0x3e, 0xac, 0xcf, 0x9d, 0x64, 0x0d, 0x70, 0x45, 0x82, 0xd1,
0x7c, 0xa0, 0x64, 0x35, 0x9a, 0x47, 0x10, 0xa1, 0x9a, 0xfe, 0x85, 0xbe, 0x16, 0x6a, 0x17, 0xea,
0xe4, 0xf7, 0xbf, 0x50, 0x37, 0xd0, 0x29, 0x11, 0xa2, 0xe1, 0x67, 0xdd, 0x54, 0x31, 0x39, 0x97,
0x2e, 0x5d, 0xe3, 0xd5, 0x76, 0x43, 0x0c, 0xab, 0xe1, 0x07, 0xdd, 0x99, 0x28, 0x58, 0x05, 0x18,
0x46, 0x5b, 0x6e, 0x8c, 0xc6, 0xb8, 0x78, 0x39, 0x76, 0xdf, 0x12, 0xa9, 0xfe, 0x5f, 0x27, 0xbc,
0x5f, 0x69, 0xf7, 0x29, 0x92, 0x41, 0xa9, 0x35, 0xdb, 0x69, 0x93, 0x1b, 0x28, 0xbd, 0xd4, 0x71,
0x7d, 0xa8, 0x38, 0x1e, 0x33, 0x7d, 0xd7, 0xd1, 0x43, 0x49, 0x20, 0xea, 0xa8, 0x85, 0x48, 0xa8,
0xc4, 0xe7, 0xbf, 0x4a, 0xa2, 0x29, 0xed, 0xef, 0x20, 0xf8, 0xff, 0xd0, 0xa5, 0x3b, 0x95, 0x46,
0x63, 0x71, 0xa5, 0xd2, 0x5c, 0x7f, 0xb0, 0x56, 0x69, 0x2e, 0xad, 0xde, 0x6d, 0xac, 0x57, 0x68,
0x73, 0xa9, 0x5e, 0x5b, 0xae, 0xae, 0xe4, 0xc6, 0x0a, 0x97, 0xf7, 0x0f, 0x8a, 0x79, 0xcd, 0x22,
0xfe, 0x17, 0x8b, 0xff, 0x46, 0x38, 0x66, 0x5e, 0xad, 0x95, 0x2b, 0x9f, 0xe4, 0x12, 0x85, 0x73,
0xfb, 0x07, 0xc5, 0x9c, 0x66, 0x25, 0x3e, 0x84, 0xfd, 0x2f, 0x7a, 0xe9, 0x28, 0xbb, 0x79, 0x77,
0xad, 0xbc, 0xb8, 0x5e, 0xc9, 0x8d, 0x17, 0x0a, 0xfb, 0x07, 0xc5, 0x0b, 0x87, 0x8d, 0x64, 0x08,
0xbe, 0x81, 0xce, 0xc5, 0x4c, 0x69, 0xe5, 0xe3, 0xbb, 0x95, 0xc6, 0x7a, 0x2e, 0x59, 0xb8, 0xb0,
0x7f, 0x50, 0xc4, 0x9a, 0x55, 0xd8, 0x26, 0x16, 0xd0, 0xf9, 0x43, 0x16, 0x8d, 0xb5, 0x7a, 0xad,
0x51, 0xc9, 0xa5, 0x0a, 0x17, 0xf7, 0x0f, 0x8a, 0x67, 0x63, 0x26, 0xb2, 0xaa, 0x2c, 0xa1, 0xd9,
0x98, 0x4d, 0xb9, 0x7e, 0xbf, 0xb6, 0x5a, 0x5f, 0x2c, 0x37, 0xd7, 0x68, 0x7d, 0x85, 0x56, 0x1a,
0x8d, 0x5c, 0xba, 0x60, 0xec, 0x1f, 0x14, 0x2f, 0x69, 0xc6, 0x47, 0x32, 0x7c, 0x1e, 0xcd, 0xc4,
0x9c, 0xac, 0x55, 0x6b, 0x2b, 0xb9, 0x4c, 0xe1, 0xec, 0xfe, 0x41, 0xf1, 0x8c, 0x66, 0xc7, 0xcf,
0xf2, 0xc8, 0xfe, 0x2d, 0xad, 0xd6, 0x1b, 0x95, 0xdc, 0xc4, 0x91, 0xfd, 0x83, 0x03, 0x9f, 0xff,
0x4d, 0x02, 0xe1, 0xa3, 0x7f, 0x7a, 0xc2, 0xef, 0xa1, 0x7c, 0xe8, 0x64, 0xa9, 0x7e, 0x67, 0x8d,
0xaf, 0xb3, 0x5a, 0xaf, 0x35, 0x6b, 0xf5, 0x5a, 0x25, 0x37, 0x16, 0xdb, 0x55, 0xcd, 0xaa, 0xe6,
0x3a, 0x0c, 0xd7, 0xd1, 0xc5, 0xe7, 0x59, 0xae, 0x3e, 0x7c, 0x3b, 0x97, 0x28, 0x2c, 0xec, 0x1f,
0x14, 0xcf, 0x1f, 0x35, 0x5c, 0x7d, 0xf8, 0xf6, 0xd7, 0x3f, 0x7f, 0xe5, 0xf9, 0x8a, 0x79, 0x3e,
0x00, 0xe9, 0x4b, 0x7b, 0x13, 0x9d, 0xd3, 0x1d, 0xdf, 0xa9, 0xac, 0x2f, 0x96, 0x17, 0xd7, 0x17,
0x73, 0x63, 0xe2, 0x0c, 0x34, 0xea, 0x1d, 0xd6, 0x37, 0xa1, 0xec, 0xbe, 0x86, 0x66, 0x62, 0x6f,
0x51, 0xb9, 0x57, 0xa1, 0x61, 0x44, 0xe9, 0xeb, 0x67, 0xdb, 0xcc, 0xc3, 0xaf, 0x23, 0xac, 0x93,
0x17, 0x57, 0xef, 0x2f, 0x3e, 0x68, 0xe4, 0xc6, 0x0b, 0xe7, 0xf7, 0x0f, 0x8a, 0x33, 0x1a, 0x7b,
0xb1, 0xb3, 0x63, 0xee, 0xf9, 0xf3, 0x7f, 0x1c, 0x47, 0xd3, 0xfa, 0xd7, 0x1b, 0xfc, 0x3a, 0x3a,
0xbb, 0x5c, 0x5d, 0xe5, 0x91, 0xb8, 0x5c, 0x17, 0x27, 0xc0, 0xc5, 0xdc, 0x98, 0x78, 0x9c, 0x4e,
0xe5, 0xbf, 0xf1, 0xff, 0xa0, 0xfc, 0x21, 0x7a, 0xb9, 0x4a, 0x2b, 0x4b, 0xeb, 0x75, 0xfa, 0x20,
0x97, 0x28, 0xbc, 0xc4, 0x37, 0x4c, 0xb7, 0x29, 0xdb, 0x1e, 0x94, 0xa0, 0x3d, 0x7c, 0x13, 0x5d,
0x3a, 0x64, 0xd8, 0x78, 0x70, 0x67, 0xb5, 0x5a, 0xbb, 0x2d, 0x9e, 0x37, 0x5e, 0xb8, 0xb2, 0x7f,
0x50, 0xbc, 0xa8, 0xdb, 0x36, 0xc4, 0x07, 0x31, 0x0e, 0x65, 0x13, 0xf8, 0x16, 0x2a, 0x1e, 0x63,
0x1f, 0x2d, 0x20, 0x59, 0x20, 0xfb, 0x07, 0xc5, 0xcb, 0xcf, 0x71, 0xa2, 0xd6, 0x91, 0x4d, 0xe0,
0xb7, 0xd0, 0x85, 0xe7, 0x7b, 0x0a, 0xf3, 0xe2, 0x39, 0xf6, 0xf3, 0x7f, 0x4e, 0xa0, 0x49, 0xd5,
0xf5, 0xf8, 0xa6, 0x55, 0x28, 0xad, 0xf3, 0x22, 0x51, 0xae, 0x34, 0x6b, 0xf5, 0x26, 0x48, 0xe1,
0xa6, 0x29, 0x5e, 0xcd, 0x85, 0x9f, 0x3c, 0xc6, 0x35, 0xfa, 0x4a, 0xa5, 0x56, 0xa1, 0xd5, 0xa5,
0xf0, 0x44, 0x15, 0x7b, 0x85, 0x39, 0xcc, 0xb3, 0x5b, 0xf8, 0x6d, 0x74, 0x31, 0xee, 0xbc, 0x71,
0x77, 0xe9, 0x56, 0xb8, 0x4b, 0xb0, 0x40, 0xed, 0x01, 0x8d, 0x41, 0x6b, 0x0b, 0x0e, 0xe6, 0x9d,
0x98, 0x55, 0xb5, 0x76, 0x6f, 0x71, 0xb5, 0x5a, 0x16, 0x56, 0xc9, 0x42, 0x7e, 0xff, 0xa0, 0x78,
0x4e, 0x59, 0xc9, 0x0f, 0x1c, 0xdc, 0x6c, 0xfe, 0xeb, 0x04, 0x9a, 0xfd, 0xf6, 0xe6, 0x85, 0xef,
0xa3, 0x57, 0x61, 0xbf, 0x8e, 0x94, 0x02, 0x59, 0xb7, 0xc4, 0x1e, 0x2e, 0xae, 0xad, 0x55, 0x6a,
0xe5, 0xdc, 0x58, 0x61, 0x6e, 0xff, 0xa0, 0x78, 0xf5, 0xdb, 0x5d, 0x2e, 0xf6, 0x7a, 0xcc, 0xb1,
0x4e, 0xe8, 0x78, 0xb9, 0x4e, 0x57, 0x2a, 0xeb, 0xb9, 0xc4, 0x49, 0x1c, 0x2f, 0xbb, 0x5e, 0x9b,
0xf5, 0x4b, 0x77, 0x1e, 0x7f, 0x33, 0x3b, 0xf6, 0xe4, 0x9b, 0xd9, 0xb1, 0xc7, 0x4f, 0x67, 0x13,
0x4f, 0x9e, 0xce, 0x26, 0x7e, 0xf1, 0x6c, 0x76, 0xec, 0xcb, 0x67, 0xb3, 0x89, 0x27, 0xcf, 0x66,
0xc7, 0xfe, 0xf2, 0x6c, 0x76, 0xec, 0xe1, 0x6b, 0x6d, 0xbb, 0xbf, 0x35, 0xd8, 0xb8, 0xd6, 0x72,
0xbb, 0xd7, 0xfd, 0x3d, 0xa7, 0xd5, 0xdf, 0xb2, 0x9d, 0xb6, 0xf6, 0x4b, 0xff, 0x2f, 0x08, 0x1b,
0x19, 0xf8, 0xf5, 0xd6, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xc3, 0x32, 0x75, 0x99, 0x20,
0x00, 0x00,
// 3163 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4d, 0x6c, 0x1b, 0xc7,
0xbd, 0x17, 0xc5, 0x0f, 0x51, 0x23, 0xc9, 0xa6, 0xc6, 0x5f, 0x0c, 0x6d, 0x6b, 0xf9, 0x26, 0xce,
0x7b, 0x8a, 0xf2, 0x62, 0x27, 0xca, 0xc7, 0xcb, 0x8b, 0xf3, 0x1c, 0x88, 0x22, 0x25, 0x33, 0x96,
0x49, 0x65, 0x28, 0xdb, 0xb1, 0xf1, 0x1e, 0x88, 0x15, 0x77, 0x44, 0x2d, 0x4c, 0xee, 0xf2, 0xed,
0x52, 0x5f, 0x41, 0x2f, 0x6d, 0x80, 0x20, 0xd0, 0xa1, 0x28, 0x72, 0x2a, 0x8a, 0x0a, 0x0d, 0x7a,
0xe9, 0xad, 0x40, 0x0f, 0xbd, 0xe4, 0xd4, 0xa3, 0x8f, 0x46, 0x80, 0x02, 0x45, 0x0f, 0x0b, 0xc4,
0xbe, 0xb4, 0xec, 0x8d, 0xc7, 0x9e, 0x8a, 0xf9, 0xcf, 0xec, 0xec, 0xac, 0x3e, 0x52, 0x39, 0x39,
0xf4, 0x64, 0xfe, 0x7f, 0xff, 0xdf, 0xff, 0xbf, 0xb3, 0xf3, 0xff, 0x9a, 0x59, 0x19, 0x5d, 0xec,
0xd8, 0xeb, 0x37, 0x7a, 0x9e, 0xdb, 0x77, 0x5b, 0x6e, 0xe7, 0xc6, 0x3a, 0xeb, 0x5d, 0x07, 0x01,
0x67, 0x43, 0xac, 0x30, 0xce, 0x76, 0xfb, 0x02, 0x2c, 0xbc, 0xec, 0xb1, 0x9e, 0xeb, 0x0b, 0xfa,
0xfa, 0xd6, 0xc6, 0x8d, 0xb6, 0xdb, 0x76, 0x41, 0x80, 0x5f, 0x82, 0x44, 0x9e, 0x25, 0x50, 0xfa,
0x36, 0xeb, 0x74, 0x5c, 0xbc, 0x88, 0x26, 0x2c, 0xb6, 0x6d, 0xb7, 0x58, 0xd3, 0x31, 0xbb, 0x2c,
0x9f, 0x28, 0x26, 0x66, 0xc7, 0x4b, 0x64, 0x10, 0x18, 0x48, 0xc0, 0x35, 0xb3, 0xcb, 0x86, 0x81,
0x91, 0xdb, 0xed, 0x76, 0xde, 0x27, 0x11, 0x44, 0xa8, 0xa6, 0xe7, 0x4e, 0x5a, 0x1d, 0x9b, 0x39,
0x7d, 0xe1, 0x64, 0x34, 0x72, 0x22, 0xe0, 0x98, 0x93, 0x08, 0x22, 0x54, 0xd3, 0xe3, 0x3a, 0x3a,
0x23, 0x9d, 0x6c, 0x33, 0xcf, 0xb7, 0x5d, 0x27, 0x9f, 0x04, 0x3f, 0xb3, 0x83, 0xc0, 0x98, 0x12,
0x9a, 0xfb, 0x42, 0x31, 0x0c, 0x8c, 0x73, 0x9a, 0x2b, 0x89, 0x12, 0x1a, 0x67, 0x91, 0xdf, 0x25,
0x50, 0xe6, 0x36, 0x33, 0x2d, 0xe6, 0xe1, 0x05, 0x94, 0xea, 0xef, 0xf5, 0xc4, 0xeb, 0x9d, 0x99,
0xbf, 0x70, 0x3d, 0xdc, 0xb8, 0xeb, 0x77, 0x99, 0xef, 0x9b, 0x6d, 0xb6, 0xb6, 0xd7, 0x63, 0xa5,
0x8b, 0x83, 0xc0, 0x00, 0xda, 0x30, 0x30, 0x10, 0xf8, 0xe7, 0x02, 0xa1, 0x80, 0x61, 0x0b, 0x4d,
0xb4, 0xdc, 0x6e, 0xcf, 0x63, 0x3e, 0xac, 0x6d, 0x14, 0x3c, 0x5d, 0x39, 0xe2, 0x69, 0x31, 0xe2,
0x94, 0xae, 0x0d, 0x02, 0x43, 0x37, 0x1a, 0x06, 0xc6, 0xb4, 0x58, 0x77, 0x84, 0x11, 0xaa, 0x33,
0xc8, 0xff, 0xa2, 0xa9, 0xc5, 0xce, 0x96, 0xdf, 0x67, 0xde, 0xa2, 0xeb, 0x6c, 0xd8, 0x6d, 0x7c,
0x07, 0x8d, 0x6d, 0xb8, 0x1d, 0x8b, 0x79, 0x7e, 0x3e, 0x51, 0x4c, 0xce, 0x4e, 0xcc, 0xe7, 0xa2,
0x47, 0x2e, 0x81, 0xa2, 0x64, 0x3c, 0x09, 0x8c, 0x91, 0x41, 0x60, 0x84, 0xc4, 0x61, 0x60, 0x4c,
0xc2, 0x63, 0x84, 0x4c, 0x68, 0xa8, 0x20, 0x5f, 0xa7, 0x50, 0x46, 0x18, 0xe1, 0xeb, 0x68, 0xd4,
0xb6, 0x64, 0xb8, 0x67, 0x9e, 0x05, 0xc6, 0x68, 0xb5, 0x3c, 0x08, 0x8c, 0x51, 0xdb, 0x1a, 0x06,
0x46, 0x16, 0xac, 0x6d, 0x8b, 0x7c, 0xf9, 0xf4, 0xda, 0x68, 0xb5, 0x4c, 0x47, 0x6d, 0x0b, 0x5f,
0x47, 0xe9, 0x8e, 0xb9, 0xce, 0x3a, 0x32, 0xb8, 0xf9, 0x41, 0x60, 0x08, 0x60, 0x18, 0x18, 0x13,
0xc0, 0x07, 0x89, 0x50, 0x81, 0xe2, 0x9b, 0x68, 0xdc, 0x63, 0xa6, 0xd5, 0x74, 0x9d, 0xce, 0x1e,
0x04, 0x32, 0x5b, 0x9a, 0x19, 0x04, 0x46, 0x96, 0x83, 0x75, 0xa7, 0xb3, 0x37, 0x0c, 0x8c, 0x33,
0x60, 0x16, 0x02, 0x84, 0x2a, 0x1d, 0x6e, 0x22, 0x6c, 0xb7, 0x1d, 0xd7, 0x63, 0xcd, 0x1e, 0xf3,
0xba, 0x36, 0x6c, 0x8d, 0x9f, 0x4f, 0x81, 0x97, 0x37, 0x06, 0x81, 0x31, 0x2d, 0xb4, 0xab, 0x91,
0x72, 0x18, 0x18, 0x97, 0xc4, 0xaa, 0x0f, 0x6b, 0x08, 0x3d, 0xca, 0xc6, 0x77, 0xd0, 0x94, 0x7c,
0x80, 0xc5, 0x3a, 0xac, 0xcf, 0xf2, 0x69, 0xf0, 0xfd, 0xef, 0x83, 0xc0, 0x98, 0x14, 0x8a, 0x32,
0xe0, 0xc3, 0xc0, 0xc0, 0x9a, 0x5b, 0x01, 0x12, 0x1a, 0xe3, 0x60, 0x0b, 0x9d, 0xb7, 0x6c, 0xdf,
0x5c, 0xef, 0xb0, 0x66, 0x9f, 0x75, 0x7b, 0x4d, 0xdb, 0xb1, 0xd8, 0x2e, 0xf3, 0xf3, 0x19, 0xf0,
0x39, 0x3f, 0x08, 0x0c, 0x2c, 0xf5, 0x6b, 0xac, 0xdb, 0xab, 0x0a, 0xed, 0x30, 0x30, 0xf2, 0xa2,
0xa6, 0x8e, 0xa8, 0x08, 0x3d, 0x86, 0x8f, 0xe7, 0x51, 0xa6, 0x67, 0x6e, 0xf9, 0xcc, 0xca, 0x8f,
0x81, 0xdf, 0xc2, 0x20, 0x30, 0x24, 0xa2, 0x02, 0x2e, 0x44, 0x42, 0x25, 0xce, 0x93, 0x47, 0x54,
0xa9, 0x9f, 0xcf, 0x1d, 0x4e, 0x9e, 0x32, 0x28, 0xa2, 0xe4, 0x91, 0x44, 0xe5, 0x4b, 0xc8, 0x84,
0x86, 0x0a, 0xf2, 0x87, 0x0c, 0xca, 0x08, 0x23, 0x5c, 0x52, 0xc9, 0x33, 0x59, 0x9a, 0xe7, 0x0e,
0xfe, 0x1c, 0x18, 0x59, 0xa1, 0xab, 0x96, 0x4f, 0x4a, 0xa6, 0x2f, 0x9e, 0x5e, 0x4b, 0x68, 0x09,
0x35, 0x87, 0x52, 0x5a, 0xb3, 0x80, 0xda, 0x73, 0x44, 0x9b, 0x10, 0xb5, 0xe7, 0x40, 0x83, 0x00,
0x0c, 0x7f, 0x80, 0xc6, 0x4d, 0xcb, 0xe2, 0x35, 0xc2, 0xfc, 0x7c, 0xb2, 0x98, 0xe4, 0x39, 0x3b,
0x08, 0x8c, 0x08, 0x1c, 0x06, 0xc6, 0x14, 0x58, 0x49, 0x84, 0xd0, 0x48, 0x87, 0xff, 0x2f, 0x5e,
0xb9, 0xa9, 0xc3, 0x3d, 0xe0, 0x87, 0x95, 0x2c, 0xcf, 0xf4, 0x16, 0xf3, 0x64, 0xeb, 0x4b, 0x8b,
0x82, 0xe2, 0x99, 0xce, 0x41, 0xd9, 0xf8, 0x44, 0xa6, 0x87, 0x00, 0xa1, 0x4a, 0x87, 0x97, 0xd1,
0x64, 0xd7, 0xdc, 0x6d, 0xfa, 0xec, 0xff, 0xb7, 0x98, 0xd3, 0x62, 0x90, 0x33, 0x49, 0xb1, 0x8a,
0xae, 0xb9, 0xdb, 0x90, 0xb0, 0x5a, 0x85, 0x86, 0x11, 0xaa, 0x33, 0x70, 0x09, 0x21, 0xdb, 0xe9,
0x7b, 0xae, 0xb5, 0xd5, 0x62, 0x9e, 0x4c, 0x11, 0xe8, 0xc0, 0x11, 0xaa, 0x3a, 0x70, 0x04, 0x11,
0xaa, 0xe9, 0x71, 0x1b, 0x65, 0x21, 0x77, 0x9b, 0xb6, 0x95, 0xcf, 0x16, 0x13, 0xb3, 0xa9, 0xd2,
0x8a, 0x0c, 0xee, 0x18, 0x64, 0x21, 0xc4, 0x36, 0xfc, 0xc9, 0x73, 0x06, 0xd8, 0x55, 0x4b, 0xed,
0xbe, 0x94, 0x79, 0xdf, 0x08, 0x69, 0xbf, 0x88, 0x7e, 0xd2, 0x90, 0x8f, 0x7f, 0x84, 0x0a, 0xfe,
0x63, 0x9b, 0x57, 0x8a, 0x78, 0x76, 0xdf, 0x76, 0x9d, 0xa6, 0xc7, 0xba, 0xee, 0xb6, 0xd9, 0xf1,
0xf3, 0xe3, 0xb0, 0xf8, 0x5b, 0x83, 0xc0, 0xc8, 0x73, 0x56, 0x55, 0x23, 0x51, 0xc9, 0x19, 0x06,
0xc6, 0x0c, 0x3c, 0xf1, 0x24, 0x02, 0xa1, 0x27, 0xda, 0xe2, 0x5d, 0xf4, 0x12, 0x73, 0x5a, 0xde,
0x5e, 0x0f, 0x1e, 0xdb, 0x33, 0x7d, 0x7f, 0xc7, 0xf5, 0xac, 0x66, 0xdf, 0x7d, 0xcc, 0x9c, 0x3c,
0x82, 0xa4, 0xfe, 0x60, 0x10, 0x18, 0x97, 0x22, 0xd2, 0xaa, 0xe4, 0xac, 0x71, 0xca, 0x30, 0x30,
0xae, 0xc2, 0xb3, 0x4f, 0xd0, 0x13, 0x7a, 0x92, 0x25, 0xf9, 0x49, 0x02, 0xa5, 0x61, 0x33, 0x78,
0x35, 0x8b, 0xa6, 0x2c, 0x5b, 0x30, 0x54, 0xb3, 0x40, 0x8e, 0xb4, 0x6f, 0x89, 0xe3, 0x0a, 0x4a,
0x6f, 0xd8, 0x1d, 0xe6, 0xe7, 0x47, 0xa1, 0x96, 0xb1, 0x36, 0x08, 0xec, 0x0e, 0xab, 0x3a, 0x1b,
0x6e, 0xe9, 0xb2, 0xac, 0x66, 0x41, 0x54, 0xb5, 0xc4, 0x25, 0x42, 0x05, 0x48, 0xbe, 0x48, 0xa0,
0x09, 0x58, 0xc4, 0xbd, 0x9e, 0x65, 0xf6, 0xd9, 0xbf, 0x72, 0x29, 0x9f, 0x4f, 0xa1, 0x6c, 0x68,
0xa0, 0x1a, 0x42, 0xe2, 0x14, 0x0d, 0x61, 0x0e, 0xa5, 0x7c, 0xfb, 0x53, 0x06, 0x83, 0x25, 0x29,
0xb8, 0x5c, 0x56, 0x5c, 0x2e, 0x10, 0x0a, 0x18, 0xfe, 0x10, 0xa1, 0xae, 0x6b, 0xd9, 0x1b, 0x36,
0xb3, 0x9a, 0x3e, 0x14, 0x68, 0xb2, 0x54, 0xe4, 0xdd, 0x23, 0x44, 0x1b, 0xc3, 0xc0, 0x38, 0x2b,
0xca, 0x2b, 0x44, 0x08, 0x8d, 0xb4, 0xbc, 0x7f, 0x28, 0x07, 0xeb, 0x7b, 0xf9, 0x49, 0xa8, 0x8c,
0x0f, 0xc2, 0xca, 0x68, 0x6c, 0xba, 0x5e, 0x1f, 0xca, 0x41, 0x3d, 0xa6, 0xb4, 0xa7, 0x4a, 0x2d,
0x82, 0x08, 0xaf, 0x04, 0x49, 0xa6, 0x1a, 0x15, 0xaf, 0xa0, 0xb1, 0xf0, 0xc0, 0xc3, 0x33, 0x3f,
0xd6, 0xa4, 0xef, 0xb3, 0x56, 0xdf, 0xf5, 0x4a, 0xc5, 0xb0, 0x49, 0x6f, 0xab, 0x03, 0x90, 0x28,
0xb8, 0xed, 0xf0, 0xe8, 0x13, 0x6a, 0xf0, 0xfb, 0x28, 0xab, 0x9a, 0x09, 0x82, 0x77, 0x85, 0x66,
0xe4, 0x47, 0x9d, 0x44, 0x34, 0x23, 0x5f, 0xb5, 0x11, 0xa5, 0xc3, 0x1f, 0xa1, 0xcc, 0x7a, 0xc7,
0x6d, 0x3d, 0x0e, 0xa7, 0xc5, 0xb9, 0x68, 0x21, 0x25, 0x8e, 0x43, 0x5c, 0xaf, 0xca, 0xb5, 0x48,
0xaa, 0x1a, 0xff, 0x20, 0x12, 0x2a, 0x61, 0x7e, 0x9a, 0xf3, 0xf7, 0xba, 0x1d, 0xdb, 0x79, 0xdc,
0xec, 0x9b, 0x5e, 0x9b, 0xf5, 0xf3, 0xd3, 0xd1, 0x69, 0x4e, 0x6a, 0xd6, 0x40, 0xa1, 0x4e, 0x73,
0x31, 0x94, 0xd0, 0x38, 0x8b, 0x9f, 0x31, 0x85, 0xeb, 0xe6, 0xa6, 0xe9, 0x6f, 0xe6, 0x31, 0xd4,
0x29, 0x74, 0x38, 0x01, 0xdf, 0x36, 0xfd, 0x4d, 0xb5, 0xed, 0x11, 0x44, 0xa8, 0xa6, 0xc7, 0xb7,
0xd0, 0xb8, 0xac, 0x4d, 0x66, 0xe5, 0xcf, 0x81, 0x0b, 0x48, 0x05, 0x05, 0xaa, 0x54, 0x50, 0x08,
0xa1, 0x91, 0x16, 0x97, 0xe4, 0x39, 0x52, 0x9c, 0xfe, 0x2e, 0x1e, 0x4d, 0xfb, 0x53, 0x1c, 0x24,
0x97, 0xd0, 0xc4, 0xe1, 0x53, 0xcd, 0x94, 0xe8, 0xf8, 0xbd, 0xd8, 0x79, 0x46, 0x74, 0xfc, 0x9e,
0x7e, 0x92, 0xd1, 0x19, 0xf8, 0x23, 0x2d, 0x2d, 0x1d, 0x3f, 0x3f, 0x51, 0x4c, 0xcc, 0xa6, 0x4b,
0xaf, 0xea, 0x79, 0x58, 0xf3, 0x8f, 0xe4, 0x61, 0xcd, 0x27, 0x7f, 0x0f, 0x8c, 0xa4, 0xed, 0xf4,
0xa9, 0x46, 0xc3, 0x1b, 0x48, 0xec, 0x52, 0x13, 0xaa, 0x6a, 0x0a, 0x5c, 0x2d, 0x3f, 0x0b, 0x8c,
0x49, 0x6a, 0xee, 0x40, 0xe8, 0x1b, 0xf6, 0xa7, 0x8c, 0x6f, 0xd4, 0x7a, 0x28, 0xa8, 0x8d, 0x52,
0x48, 0xe8, 0xf8, 0xcb, 0xa7, 0xd7, 0x62, 0x66, 0x34, 0x32, 0xc2, 0xf7, 0x51, 0xb6, 0xd7, 0x31,
0xfb, 0x1b, 0xae, 0xd7, 0xcd, 0x9f, 0x81, 0x64, 0xd7, 0xf6, 0x70, 0x55, 0x6a, 0xca, 0x66, 0xdf,
0x2c, 0x11, 0x99, 0x66, 0x8a, 0xaf, 0x32, 0x37, 0x04, 0x08, 0x55, 0x3a, 0x5c, 0x46, 0x13, 0x1d,
0xb7, 0x65, 0x76, 0x9a, 0x1b, 0x1d, 0xb3, 0xed, 0xe7, 0xff, 0x32, 0x06, 0x9b, 0x0a, 0xd9, 0x01,
0xf8, 0x12, 0x87, 0xd5, 0x66, 0x44, 0x10, 0xa1, 0x9a, 0x1e, 0xdf, 0x46, 0x93, 0xb2, 0x8c, 0x44,
0x8e, 0xfd, 0x75, 0x0c, 0x32, 0x04, 0x62, 0x23, 0x15, 0x32, 0xcb, 0xa6, 0xf5, 0xea, 0x13, 0x69,
0xa6, 0x33, 0xf0, 0xc7, 0xe8, 0xac, 0xed, 0xb8, 0x16, 0x6b, 0xb6, 0x36, 0x4d, 0xa7, 0xcd, 0x78,
0x7c, 0x06, 0x63, 0x50, 0x8d, 0x90, 0xff, 0xa0, 0x5b, 0x04, 0x15, 0xc4, 0xe8, 0x9c, 0x9c, 0x9e,
0x1a, 0x4a, 0x68, 0x9c, 0x85, 0x77, 0x91, 0x36, 0x56, 0x9a, 0x7d, 0xcf, 0xb4, 0x3b, 0xcc, 0x13,
0xf1, 0xfa, 0xdb, 0x18, 0x04, 0xec, 0xc3, 0x41, 0x60, 0x5c, 0x88, 0x38, 0x6b, 0x82, 0x22, 0x83,
0x75, 0xf9, 0xd0, 0xc8, 0xd2, 0xb4, 0x2a, 0x23, 0x8e, 0x37, 0xc6, 0xef, 0xf2, 0x53, 0x24, 0x3f,
0xe9, 0x5a, 0xf2, 0x48, 0x7b, 0x45, 0x9c, 0x17, 0x01, 0x52, 0xad, 0x48, 0xca, 0x70, 0x60, 0x84,
0x5f, 0x98, 0xa2, 0x31, 0xdb, 0xd9, 0x36, 0x3b, 0x76, 0x78, 0x64, 0x7d, 0xef, 0x59, 0x60, 0x20,
0x6a, 0xee, 0x54, 0x05, 0x2a, 0x4e, 0x10, 0xf0, 0x53, 0x3b, 0x41, 0x80, 0xcc, 0x4f, 0x10, 0x1a,
0x93, 0x86, 0x3c, 0xde, 0x56, 0x1c, 0x37, 0x76, 0x2b, 0xc8, 0x82, 0x6b, 0xd8, 0x56, 0xc7, 0x8d,
0xdf, 0x08, 0xc4, 0xb6, 0xc6, 0x50, 0x42, 0xe3, 0xac, 0xf7, 0x53, 0x3f, 0xff, 0xca, 0x18, 0x21,
0xdf, 0x26, 0xd0, 0xb8, 0x6a, 0x71, 0x7c, 0xba, 0x40, 0xfc, 0x93, 0x10, 0x7e, 0xa8, 0xe6, 0x4d,
0x11, 0x77, 0x51, 0xcd, 0x9b, 0x10, 0x70, 0xc0, 0xf8, 0xf4, 0x74, 0x37, 0x36, 0x7c, 0xd6, 0x87,
0xb9, 0x95, 0x14, 0xd3, 0x53, 0x20, 0x6a, 0x7a, 0x0a, 0x91, 0x50, 0x89, 0xe3, 0x37, 0xe5, 0xf4,
0x1a, 0x85, 0xb0, 0x5d, 0x3d, 0x7e, 0x7a, 0x85, 0x41, 0x11, 0x43, 0xec, 0x26, 0x1a, 0xdf, 0x61,
0xe6, 0x63, 0x91, 0x97, 0xa2, 0x65, 0x40, 0x5f, 0xe7, 0xa0, 0xcc, 0x49, 0x51, 0x1d, 0x21, 0x40,
0xa8, 0xd2, 0xc9, 0x77, 0x7c, 0x84, 0x32, 0x62, 0x9c, 0xe0, 0x55, 0x94, 0x6d, 0xb9, 0x5b, 0x4e,
0x3f, 0xba, 0x54, 0x4e, 0xeb, 0xa7, 0x61, 0xd0, 0x94, 0xfe, 0x2d, 0x2c, 0xc0, 0x90, 0xaa, 0x62,
0x24, 0x01, 0x7e, 0x8c, 0x95, 0x2a, 0xf2, 0x59, 0x02, 0x8d, 0x49, 0x43, 0x7c, 0x5b, 0x5d, 0x0e,
0x52, 0xa5, 0xf7, 0x0e, 0x4d, 0xc9, 0xef, 0xbe, 0x68, 0xea, 0x13, 0x52, 0xde, 0x39, 0xb7, 0xcd,
0xce, 0x96, 0xd8, 0xa8, 0x94, 0xb8, 0x73, 0x02, 0xa0, 0x86, 0x0e, 0x48, 0x84, 0x0a, 0x94, 0x7c,
0x96, 0x42, 0x93, 0x7a, 0x13, 0xe1, 0xed, 0x7a, 0xcb, 0xb1, 0x77, 0x61, 0x31, 0xb1, 0x53, 0xca,
0x3d, 0xc7, 0xde, 0x85, 0x36, 0x53, 0x78, 0x12, 0x18, 0x09, 0x1e, 0x00, 0xce, 0x53, 0x01, 0xe0,
0x02, 0xa1, 0x80, 0xe1, 0x8f, 0xd1, 0xd8, 0x8e, 0xed, 0x58, 0xee, 0x8e, 0x0f, 0xcb, 0x98, 0xd0,
0x6f, 0x0e, 0x0f, 0x84, 0x02, 0x3c, 0x15, 0xa5, 0xa7, 0x90, 0xad, 0xb6, 0x4b, 0xca, 0x84, 0x86,
0x1a, 0xbc, 0x8c, 0xd2, 0x1d, 0xdb, 0xd9, 0xda, 0x85, 0x04, 0x8b, 0x8d, 0xd9, 0x4f, 0xcc, 0x7e,
0xdf, 0x03, 0x77, 0x57, 0xa4, 0x3b, 0xc1, 0x8c, 0x2e, 0xd9, 0x5c, 0xe2, 0x97, 0x6c, 0xfe, 0x2f,
0xbe, 0x83, 0x32, 0x96, 0xe9, 0xed, 0xd8, 0xe2, 0x52, 0x73, 0x82, 0xa7, 0x19, 0xe9, 0x49, 0x52,
0xa3, 0x0b, 0x1e, 0x88, 0x84, 0x4a, 0x1c, 0x33, 0x34, 0xb6, 0xe1, 0x31, 0xb6, 0xee, 0x5b, 0x70,
0x48, 0x3a, 0xc1, 0xdb, 0xbb, 0xdc, 0x1b, 0xbf, 0x06, 0x2c, 0x79, 0x8c, 0x95, 0x1a, 0x70, 0x0d,
0x90, 0x66, 0xea, 0x8d, 0xa5, 0x0c, 0xd7, 0x00, 0x49, 0xa3, 0x21, 0x09, 0x37, 0x51, 0xc6, 0x61,
0x7d, 0xfe, 0x94, 0xcc, 0xc9, 0x4f, 0x99, 0x97, 0x4f, 0xc9, 0xd4, 0x58, 0x5f, 0x3c, 0x44, 0x1a,
0xa9, 0xd5, 0x0b, 0x91, 0x3f, 0x42, 0x72, 0xa8, 0x64, 0x90, 0xcf, 0x47, 0x51, 0x36, 0x8c, 0x2f,
0x3f, 0xfc, 0xb9, 0x3b, 0x0e, 0xf3, 0xf4, 0xaf, 0x5b, 0x30, 0xf1, 0x01, 0x95, 0xd7, 0x33, 0x31,
0xc8, 0x14, 0x42, 0x68, 0xa4, 0xe5, 0x0e, 0xda, 0x9e, 0xbb, 0xd5, 0xd3, 0xbf, 0x6c, 0x81, 0x03,
0x40, 0x63, 0x0e, 0x14, 0x42, 0x68, 0xa4, 0xc5, 0x37, 0x51, 0x72, 0xcb, 0xb6, 0x20, 0xd4, 0xe9,
0xd2, 0xab, 0xcf, 0x02, 0x23, 0x79, 0x0f, 0x2a, 0x80, 0xa3, 0xc3, 0xc0, 0x18, 0x17, 0x09, 0x67,
0x5b, 0xda, 0xf8, 0xe4, 0x0c, 0xca, 0xf5, 0xdc, 0xb8, 0x6d, 0x5b, 0x10, 0x5d, 0x69, 0xbc, 0x2c,
0x8c, 0xdb, 0x9a, 0x71, 0x3b, 0x6e, 0xbc, 0xcc, 0x8d, 0x39, 0xf6, 0xcb, 0x04, 0x9a, 0xd0, 0x32,
0xf4, 0x87, 0xef, 0xc5, 0x0a, 0x3a, 0x23, 0x1c, 0xd8, 0x7e, 0x13, 0x5e, 0x10, 0xf6, 0x43, 0x7e,
0x36, 0x01, 0x4d, 0xd5, 0x5f, 0xe6, 0xb8, 0xfa, 0x6c, 0xa2, 0x83, 0x84, 0xc6, 0x38, 0xa4, 0x81,
0xc6, 0x55, 0xc0, 0xf1, 0x12, 0xca, 0xec, 0x72, 0x21, 0x6c, 0x48, 0x67, 0x0f, 0x65, 0x45, 0x74,
0xec, 0x14, 0x34, 0x55, 0x10, 0x20, 0x12, 0x2a, 0x61, 0xd2, 0x42, 0x69, 0xe0, 0xbf, 0xd0, 0x6d,
0x22, 0xd6, 0x67, 0x26, 0xff, 0x79, 0x9f, 0xf9, 0x71, 0x0a, 0x8d, 0x51, 0x7e, 0x68, 0xf6, 0xfb,
0xf8, 0x1d, 0xd5, 0xed, 0xd2, 0xa5, 0x57, 0x4e, 0x6a, 0x6f, 0x51, 0x74, 0xc2, 0xaf, 0x1f, 0xd1,
0xa5, 0x6b, 0xf4, 0xd4, 0x97, 0xae, 0xf0, 0x95, 0x92, 0xa7, 0x78, 0xa5, 0x68, 0x2c, 0xa5, 0x5e,
0x78, 0x2c, 0xa5, 0x4f, 0x3f, 0x96, 0xc2, 0x49, 0x99, 0x39, 0xc5, 0xa4, 0xac, 0xa3, 0x33, 0x1b,
0x9e, 0xdb, 0x85, 0x6f, 0x64, 0xae, 0x67, 0x7a, 0x7b, 0xf2, 0x54, 0x00, 0xa3, 0x9b, 0x6b, 0xd6,
0x42, 0x85, 0x1a, 0xdd, 0x31, 0x94, 0xd0, 0x38, 0x2b, 0x3e, 0x13, 0xb3, 0x2f, 0x36, 0x13, 0xf1,
0x2d, 0x94, 0x15, 0x27, 0x5e, 0xc7, 0x85, 0x6b, 0x57, 0xba, 0xf4, 0x32, 0x6f, 0x65, 0x80, 0xd5,
0x5c, 0xd5, 0xca, 0xa4, 0xac, 0x5e, 0x3b, 0x24, 0x90, 0xdf, 0x26, 0x50, 0x96, 0x32, 0xbf, 0xe7,
0x3a, 0x3e, 0xfb, 0xbe, 0x49, 0x30, 0x87, 0x52, 0x96, 0xd9, 0x37, 0x65, 0xda, 0xc1, 0xee, 0x71,
0x59, 0xed, 0x1e, 0x17, 0x08, 0x05, 0x0c, 0x7f, 0x88, 0x52, 0x2d, 0xd7, 0x12, 0xc1, 0x3f, 0xa3,
0x37, 0xcd, 0x8a, 0xe7, 0xb9, 0xde, 0xa2, 0x6b, 0xc9, 0x6b, 0x07, 0x27, 0x29, 0x07, 0x5c, 0x20,
0x14, 0x30, 0xf2, 0x9b, 0x04, 0xca, 0x95, 0xdd, 0x1d, 0xa7, 0xe3, 0x9a, 0xd6, 0xaa, 0xe7, 0xb6,
0x3d, 0xe6, 0xfb, 0xdf, 0xeb, 0xee, 0xdf, 0x44, 0x63, 0x5b, 0xf0, 0xe5, 0x20, 0xbc, 0xfd, 0x5f,
0x8b, 0x5f, 0x83, 0x0e, 0x3f, 0x44, 0x7c, 0x66, 0x88, 0x3e, 0x34, 0x4a, 0x63, 0xe5, 0x5f, 0xc8,
0x84, 0x86, 0x0a, 0xf2, 0xeb, 0x24, 0x2a, 0x9c, 0xec, 0x08, 0x77, 0xd1, 0x84, 0x60, 0x36, 0xb5,
0x4f, 0xfa, 0xb3, 0xa7, 0x59, 0x03, 0x5c, 0xce, 0xe0, 0x52, 0xb0, 0xa5, 0x64, 0x75, 0x29, 0x88,
0x20, 0x42, 0x35, 0xfd, 0x0b, 0x7d, 0xa7, 0xd4, 0xae, 0xf2, 0xc9, 0x1f, 0x7e, 0x95, 0x6f, 0xa0,
0x29, 0x91, 0xa2, 0xe1, 0x07, 0xe5, 0x54, 0x31, 0x39, 0x9b, 0x2e, 0x5d, 0xe7, 0xdd, 0x76, 0x5d,
0x1c, 0x56, 0xc3, 0x4f, 0xc9, 0xd3, 0x51, 0xb2, 0x0a, 0x30, 0xcc, 0xb6, 0xdc, 0x08, 0x8d, 0x71,
0xf1, 0x52, 0xec, 0xa6, 0x27, 0x4a, 0xfd, 0x3f, 0x4e, 0x79, 0xb3, 0xd3, 0x6e, 0x72, 0x24, 0x83,
0x52, 0xab, 0xb6, 0xd3, 0x26, 0x37, 0x51, 0x7a, 0xb1, 0xe3, 0xfa, 0xd0, 0x71, 0x3c, 0x66, 0xfa,
0xae, 0xa3, 0xa7, 0x92, 0x40, 0x54, 0xa8, 0x85, 0x48, 0xa8, 0xc4, 0xe7, 0xbe, 0x4e, 0xa2, 0x09,
0xed, 0x2f, 0x30, 0xf8, 0x7f, 0xd0, 0xe5, 0xbb, 0x95, 0x46, 0x63, 0x61, 0xb9, 0xd2, 0x5c, 0x7b,
0xb8, 0x5a, 0x69, 0x2e, 0xae, 0xdc, 0x6b, 0xac, 0x55, 0x68, 0x73, 0xb1, 0x5e, 0x5b, 0xaa, 0x2e,
0xe7, 0x46, 0x0a, 0x57, 0xf6, 0x0f, 0x8a, 0x79, 0xcd, 0x22, 0xfe, 0xb7, 0x92, 0xff, 0x44, 0x38,
0x66, 0x5e, 0xad, 0x95, 0x2b, 0x9f, 0xe4, 0x12, 0x85, 0xf3, 0xfb, 0x07, 0xc5, 0x9c, 0x66, 0x25,
0x3e, 0xc1, 0xfd, 0x37, 0x7a, 0xe9, 0x28, 0xbb, 0x79, 0x6f, 0xb5, 0xbc, 0xb0, 0x56, 0xc9, 0x8d,
0x16, 0x0a, 0xfb, 0x07, 0xc5, 0x8b, 0x87, 0x8d, 0x64, 0x0a, 0xbe, 0x81, 0xce, 0xc7, 0x4c, 0x69,
0xe5, 0xe3, 0x7b, 0x95, 0xc6, 0x5a, 0x2e, 0x59, 0xb8, 0xb8, 0x7f, 0x50, 0xc4, 0x9a, 0x55, 0x38,
0x26, 0xe6, 0xd1, 0x85, 0x43, 0x16, 0x8d, 0xd5, 0x7a, 0xad, 0x51, 0xc9, 0xa5, 0x0a, 0x97, 0xf6,
0x0f, 0x8a, 0xe7, 0x62, 0x26, 0xb2, 0xab, 0x2c, 0xa2, 0x99, 0x98, 0x4d, 0xb9, 0xfe, 0xa0, 0xb6,
0x52, 0x5f, 0x28, 0x37, 0x57, 0x69, 0x7d, 0x99, 0x56, 0x1a, 0x8d, 0x5c, 0xba, 0x60, 0xec, 0x1f,
0x14, 0x2f, 0x6b, 0xc6, 0x47, 0x2a, 0x7c, 0x0e, 0x4d, 0xc7, 0x9c, 0xac, 0x56, 0x6b, 0xcb, 0xb9,
0x4c, 0xe1, 0xdc, 0xfe, 0x41, 0xf1, 0xac, 0x66, 0xc7, 0x63, 0x79, 0x64, 0xff, 0x16, 0x57, 0xea,
0x8d, 0x4a, 0x6e, 0xec, 0xc8, 0xfe, 0x41, 0xc0, 0xe7, 0x7e, 0x95, 0x40, 0xf8, 0xe8, 0x1f, 0xbd,
0xf0, 0x7b, 0x28, 0x1f, 0x3a, 0x59, 0xac, 0xdf, 0x5d, 0xe5, 0xeb, 0xac, 0xd6, 0x6b, 0xcd, 0x5a,
0xbd, 0x56, 0xc9, 0x8d, 0xc4, 0x76, 0x55, 0xb3, 0xaa, 0xb9, 0x0e, 0xc3, 0x75, 0x74, 0xe9, 0x38,
0xcb, 0x95, 0x47, 0x6f, 0xe7, 0x12, 0x85, 0xf9, 0xfd, 0x83, 0xe2, 0x85, 0xa3, 0x86, 0x2b, 0x8f,
0xde, 0xfe, 0xe6, 0xa7, 0xaf, 0x1c, 0xaf, 0x98, 0xe3, 0x07, 0x20, 0x7d, 0x69, 0x6f, 0xa2, 0xf3,
0xba, 0xe3, 0xbb, 0x95, 0xb5, 0x85, 0xf2, 0xc2, 0xda, 0x42, 0x6e, 0x44, 0xc4, 0x40, 0xa3, 0xde,
0x65, 0x7d, 0x13, 0xda, 0xee, 0x6b, 0x68, 0x3a, 0xf6, 0x16, 0x95, 0xfb, 0x15, 0x1a, 0x66, 0x94,
0xbe, 0x7e, 0xb6, 0xcd, 0x3c, 0xfc, 0x3a, 0xc2, 0x3a, 0x79, 0x61, 0xe5, 0xc1, 0xc2, 0xc3, 0x46,
0x6e, 0xb4, 0x70, 0x61, 0xff, 0xa0, 0x38, 0xad, 0xb1, 0x17, 0x3a, 0x3b, 0xe6, 0x9e, 0x3f, 0xf7,
0xfb, 0x51, 0x34, 0xa9, 0x7f, 0x37, 0xc2, 0xaf, 0xa3, 0x73, 0x4b, 0xd5, 0x15, 0x9e, 0x89, 0x4b,
0x75, 0x11, 0x01, 0x2e, 0xe6, 0x46, 0xc4, 0xe3, 0x74, 0x2a, 0xff, 0x8d, 0xff, 0x0b, 0xe5, 0x0f,
0xd1, 0xcb, 0x55, 0x5a, 0x59, 0x5c, 0xab, 0xd3, 0x87, 0xb9, 0x44, 0xe1, 0x25, 0xbe, 0x61, 0xba,
0x4d, 0xd9, 0xf6, 0xa0, 0x05, 0xed, 0xe1, 0x5b, 0xe8, 0xf2, 0x21, 0xc3, 0xc6, 0xc3, 0xbb, 0x2b,
0xd5, 0xda, 0x1d, 0xf1, 0xbc, 0xd1, 0xc2, 0xd5, 0xfd, 0x83, 0xe2, 0x25, 0xdd, 0xb6, 0x21, 0x3e,
0xc5, 0x71, 0x28, 0x9b, 0xc0, 0xb7, 0x51, 0xf1, 0x04, 0xfb, 0x68, 0x01, 0xc9, 0x02, 0xd9, 0x3f,
0x28, 0x5e, 0x39, 0xc6, 0x89, 0x5a, 0x47, 0x36, 0x81, 0xdf, 0x42, 0x17, 0x8f, 0xf7, 0x14, 0xd6,
0xc5, 0x31, 0xf6, 0x73, 0x7f, 0x4c, 0xa0, 0x71, 0x35, 0xf5, 0xf8, 0xa6, 0x55, 0x28, 0xad, 0xf3,
0x26, 0x51, 0xae, 0x34, 0x6b, 0xf5, 0x26, 0x48, 0xe1, 0xa6, 0x29, 0x5e, 0xcd, 0x85, 0x9f, 0x3c,
0xc7, 0x35, 0xfa, 0x72, 0xa5, 0x56, 0xa1, 0xd5, 0xc5, 0x30, 0xa2, 0x8a, 0xbd, 0xcc, 0x1c, 0xe6,
0xd9, 0x2d, 0xfc, 0x36, 0xba, 0x14, 0x77, 0xde, 0xb8, 0xb7, 0x78, 0x3b, 0xdc, 0x25, 0x58, 0xa0,
0xf6, 0x80, 0xc6, 0x56, 0x6b, 0x13, 0x02, 0xf3, 0x4e, 0xcc, 0xaa, 0x5a, 0xbb, 0xbf, 0xb0, 0x52,
0x2d, 0x0b, 0xab, 0x64, 0x21, 0xbf, 0x7f, 0x50, 0x3c, 0xaf, 0xac, 0xe4, 0x07, 0x0e, 0x6e, 0x36,
0xf7, 0x4d, 0x02, 0xcd, 0x7c, 0xf7, 0xf0, 0xc2, 0x0f, 0xd0, 0xab, 0xb0, 0x5f, 0x47, 0x5a, 0x81,
0xec, 0x5b, 0x62, 0x0f, 0x17, 0x56, 0x57, 0x2b, 0xb5, 0x72, 0x6e, 0xa4, 0x30, 0xbb, 0x7f, 0x50,
0xbc, 0xf6, 0xdd, 0x2e, 0x17, 0x7a, 0x3d, 0xe6, 0x58, 0xa7, 0x74, 0xbc, 0x54, 0xa7, 0xcb, 0x95,
0xb5, 0x5c, 0xe2, 0x34, 0x8e, 0x97, 0x5c, 0xaf, 0xcd, 0xfa, 0xa5, 0xbb, 0x4f, 0xbe, 0x9d, 0x19,
0x79, 0xfa, 0xed, 0xcc, 0xc8, 0x93, 0x67, 0x33, 0x89, 0xa7, 0xcf, 0x66, 0x12, 0x3f, 0x7b, 0x3e,
0x33, 0xf2, 0xd5, 0xf3, 0x99, 0xc4, 0xd3, 0xe7, 0x33, 0x23, 0x7f, 0x7a, 0x3e, 0x33, 0xf2, 0xe8,
0xb5, 0xb6, 0xdd, 0xdf, 0xdc, 0x5a, 0xbf, 0xde, 0x72, 0xbb, 0x37, 0xfc, 0x3d, 0xa7, 0xd5, 0xdf,
0xb4, 0x9d, 0xb6, 0xf6, 0x4b, 0xff, 0xcf, 0x0f, 0xeb, 0x19, 0xf8, 0xf5, 0xd6, 0x3f, 0x02, 0x00,
0x00, 0xff, 0xff, 0x68, 0x4a, 0x6e, 0xeb, 0x13, 0x21, 0x00, 0x00,
}
func (m *Hello) Marshal() (dAtA []byte, err error) {
@@ -1761,6 +1766,13 @@ func (m *FileInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.EncryptionTrailerSize != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.EncryptionTrailerSize))
i--
dAtA[i] = 0x3e
i--
dAtA[i] = 0xd8
}
if m.InodeChangeNs != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.InodeChangeNs))
i--
@@ -2828,6 +2840,9 @@ func (m *FileInfo) ProtoSize() (n int) {
if m.InodeChangeNs != 0 {
n += 2 + sovBep(uint64(m.InodeChangeNs))
}
if m.EncryptionTrailerSize != 0 {
n += 2 + sovBep(uint64(m.EncryptionTrailerSize))
}
return n
}
@@ -4771,6 +4786,25 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
break
}
}
case 1003:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field EncryptionTrailerSize", wireType)
}
m.EncryptionTrailerSize = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.EncryptionTrailerSize |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipBep(dAtA[iNdEx:])

View File

@@ -46,6 +46,7 @@ type FileIntf interface {
ModTime() time.Time
PlatformData() PlatformData
InodeChangeTime() time.Time
FileBlocksHash() []byte
}
func (Hello) Magic() uint32 {
@@ -170,6 +171,10 @@ func (f FileInfo) InodeChangeTime() time.Time {
return time.Unix(0, f.InodeChangeNs)
}
func (f FileInfo) FileBlocksHash() []byte {
return f.BlocksHash
}
// WinsConflict returns true if "f" is the one to choose when it is in
// conflict with "other".
func WinsConflict(f, other FileIntf) bool {

View File

@@ -17,6 +17,7 @@ import (
"sync"
"github.com/gogo/protobuf/proto"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/miscreant/miscreant.go"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sha256"
@@ -34,6 +35,8 @@ const (
maxPathComponent = 200 // characters
encryptedDirExtension = ".syncthing-enc" // for top level dirs
miscreantAlgo = "AES-SIV"
folderKeyCacheEntries = 1000
fileKeyCacheEntries = 5000
)
// The encryptedModel sits between the encrypted device and the model. It
@@ -42,12 +45,21 @@ const (
type encryptedModel struct {
model Model
folderKeys *folderKeyRegistry
keyGen *KeyGenerator
}
func newEncryptedModel(model Model, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
return encryptedModel{
model: model,
folderKeys: folderKeys,
keyGen: keyGen,
}
}
func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys.get(folder); ok {
// incoming index data to be decrypted
if err := decryptFileInfos(files, folderKey); err != nil {
if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil {
return err
}
}
@@ -57,7 +69,7 @@ func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo
func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys.get(folder); ok {
// incoming index data to be decrypted
if err := decryptFileInfos(files, folderKey); err != nil {
if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil {
return err
}
}
@@ -86,7 +98,7 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo,
// Decrypt the block hash.
fileKey := FileKey(realName, folderKey)
fileKey := e.keyGen.FileKey(realName, folderKey)
var additional [8]byte
binary.BigEndian.PutUint64(additional[:], uint64(realOffset))
realHash, err := decryptDeterministic(hash, fileKey, additional[:])
@@ -145,6 +157,16 @@ type encryptedConnection struct {
ConnectionInfo
conn *rawConnection
folderKeys *folderKeyRegistry
keyGen *KeyGenerator
}
func newEncryptedConnection(ci ConnectionInfo, conn *rawConnection, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedConnection {
return encryptedConnection{
ConnectionInfo: ci,
conn: conn,
folderKeys: folderKeys,
keyGen: keyGen,
}
}
func (e encryptedConnection) Start() {
@@ -161,14 +183,14 @@ func (e encryptedConnection) ID() DeviceID {
func (e encryptedConnection) Index(ctx context.Context, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys.get(folder); ok {
encryptFileInfos(files, folderKey)
encryptFileInfos(e.keyGen, files, folderKey)
}
return e.conn.Index(ctx, folder, files)
}
func (e encryptedConnection) IndexUpdate(ctx context.Context, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys.get(folder); ok {
encryptFileInfos(files, folderKey)
encryptFileInfos(e.keyGen, files, folderKey)
}
return e.conn.IndexUpdate(ctx, folder, files)
}
@@ -200,7 +222,7 @@ func (e encryptedConnection) Request(ctx context.Context, folder string, name st
// Return the decrypted block (or an error if it fails decryption)
fileKey := FileKey(name, folderKey)
fileKey := e.keyGen.FileKey(name, folderKey)
bs, err = DecryptBytes(bs, fileKey)
if err != nil {
return nil, err
@@ -232,16 +254,16 @@ func (e encryptedConnection) Statistics() Statistics {
return e.conn.Statistics()
}
func encryptFileInfos(files []FileInfo, folderKey *[keySize]byte) {
func encryptFileInfos(keyGen *KeyGenerator, files []FileInfo, folderKey *[keySize]byte) {
for i, fi := range files {
files[i] = encryptFileInfo(fi, folderKey)
files[i] = encryptFileInfo(keyGen, fi, folderKey)
}
}
// encryptFileInfo encrypts a FileInfo and wraps it into a new fake FileInfo
// with an encrypted name.
func encryptFileInfo(fi FileInfo, folderKey *[keySize]byte) FileInfo {
fileKey := FileKey(fi.Name, folderKey)
func encryptFileInfo(keyGen *KeyGenerator, fi FileInfo, folderKey *[keySize]byte) FileInfo {
fileKey := keyGen.FileKey(fi.Name, folderKey)
// The entire FileInfo is encrypted with a random nonce, and concatenated
// with that nonce.
@@ -319,7 +341,7 @@ func encryptFileInfo(fi FileInfo, folderKey *[keySize]byte) FileInfo {
enc := FileInfo{
Name: encryptName(fi.Name, folderKey),
Type: typ,
Permissions: 0644,
Permissions: 0o644,
ModifiedS: 1234567890, // Sat Feb 14 00:31:30 CET 2009
Deleted: fi.Deleted,
RawInvalid: fi.IsInvalid(),
@@ -336,9 +358,9 @@ func encryptFileInfo(fi FileInfo, folderKey *[keySize]byte) FileInfo {
return enc
}
func decryptFileInfos(files []FileInfo, folderKey *[keySize]byte) error {
func decryptFileInfos(keyGen *KeyGenerator, files []FileInfo, folderKey *[keySize]byte) error {
for i, fi := range files {
decFI, err := DecryptFileInfo(fi, folderKey)
decFI, err := DecryptFileInfo(keyGen, fi, folderKey)
if err != nil {
return err
}
@@ -349,13 +371,13 @@ func decryptFileInfos(files []FileInfo, folderKey *[keySize]byte) error {
// DecryptFileInfo extracts the encrypted portion of a FileInfo, decrypts it
// and returns that.
func DecryptFileInfo(fi FileInfo, folderKey *[keySize]byte) (FileInfo, error) {
func DecryptFileInfo(keyGen *KeyGenerator, fi FileInfo, folderKey *[keySize]byte) (FileInfo, error) {
realName, err := decryptName(fi.Name, folderKey)
if err != nil {
return FileInfo{}, err
}
fileKey := FileKey(realName, folderKey)
fileKey := keyGen.FileKey(realName, folderKey)
dec, err := DecryptBytes(fi.Encrypted, fileKey)
if err != nil {
return FileInfo{}, err
@@ -476,10 +498,10 @@ func randomNonce() *[nonceSize]byte {
// keysFromPasswords converts a set of folder ID to password into a set of
// folder ID to encryption key, using our key derivation function.
func keysFromPasswords(passwords map[string]string) map[string]*[keySize]byte {
func keysFromPasswords(keyGen *KeyGenerator, passwords map[string]string) map[string]*[keySize]byte {
res := make(map[string]*[keySize]byte, len(passwords))
for folder, password := range passwords {
res[folder] = KeyFromPassword(folder, password)
res[folder] = keyGen.KeyFromPassword(folder, password)
}
return res
}
@@ -488,9 +510,35 @@ func knownBytes(folderID string) []byte {
return []byte("syncthing" + folderID)
}
type KeyGenerator struct {
mut sync.Mutex
folderKeys *lru.TwoQueueCache[folderKeyCacheKey, *[keySize]byte]
fileKeys *lru.TwoQueueCache[fileKeyCacheKey, *[keySize]byte]
}
func NewKeyGenerator() *KeyGenerator {
folderKeys, _ := lru.New2Q[folderKeyCacheKey, *[keySize]byte](folderKeyCacheEntries)
fileKeys, _ := lru.New2Q[fileKeyCacheKey, *[keySize]byte](fileKeyCacheEntries)
return &KeyGenerator{
folderKeys: folderKeys,
fileKeys: fileKeys,
}
}
type folderKeyCacheKey struct {
folderID string
password string
}
// KeyFromPassword uses key derivation to generate a stronger key from a
// probably weak password.
func KeyFromPassword(folderID, password string) *[keySize]byte {
func (g *KeyGenerator) KeyFromPassword(folderID, password string) *[keySize]byte {
cacheKey := folderKeyCacheKey{folderID, password}
g.mut.Lock()
defer g.mut.Unlock()
if key, ok := g.folderKeys.Get(cacheKey); ok {
return key
}
bs, err := scrypt.Key([]byte(password), knownBytes(folderID), 32768, 8, 1, keySize)
if err != nil {
panic("key derivation failure: " + err.Error())
@@ -500,23 +548,36 @@ func KeyFromPassword(folderID, password string) *[keySize]byte {
}
var key [keySize]byte
copy(key[:], bs)
g.folderKeys.Add(cacheKey, &key)
return &key
}
var hkdfSalt = []byte("syncthing")
func FileKey(filename string, folderKey *[keySize]byte) *[keySize]byte {
type fileKeyCacheKey struct {
file string
key [keySize]byte
}
func (g *KeyGenerator) FileKey(filename string, folderKey *[keySize]byte) *[keySize]byte {
g.mut.Lock()
defer g.mut.Unlock()
cacheKey := fileKeyCacheKey{filename, *folderKey}
if key, ok := g.fileKeys.Get(cacheKey); ok {
return key
}
kdf := hkdf.New(sha256.New, append(folderKey[:], filename...), hkdfSalt, nil)
var fileKey [keySize]byte
n, err := io.ReadFull(kdf, fileKey[:])
if err != nil || n != keySize {
panic("hkdf failure")
}
g.fileKeys.Add(cacheKey, &fileKey)
return &fileKey
}
func PasswordToken(folderID, password string) []byte {
return encryptDeterministic(knownBytes(folderID), KeyFromPassword(folderID, password), nil)
func PasswordToken(keyGen *KeyGenerator, folderID, password string) []byte {
return encryptDeterministic(knownBytes(folderID), keyGen.KeyFromPassword(folderID, password), nil)
}
// slashify inserts slashes (and file extension) in the string to create an
@@ -593,13 +654,15 @@ func IsEncryptedParent(pathComponents []string) bool {
}
type folderKeyRegistry struct {
keys map[string]*[keySize]byte // folder ID -> key
mut sync.RWMutex
keyGen *KeyGenerator
keys map[string]*[keySize]byte // folder ID -> key
mut sync.RWMutex
}
func newFolderKeyRegistry(passwords map[string]string) *folderKeyRegistry {
func newFolderKeyRegistry(keyGen *KeyGenerator, passwords map[string]string) *folderKeyRegistry {
return &folderKeyRegistry{
keys: keysFromPasswords(passwords),
keyGen: keyGen,
keys: keysFromPasswords(keyGen, passwords),
}
}
@@ -612,6 +675,6 @@ func (r *folderKeyRegistry) get(folder string) (*[keySize]byte, bool) {
func (r *folderKeyRegistry) setPasswords(passwords map[string]string) {
r.mut.Lock()
r.keys = keysFromPasswords(passwords)
r.keys = keysFromPasswords(r.keyGen, passwords)
r.mut.Unlock()
}

View File

@@ -12,13 +12,13 @@ import (
"reflect"
"regexp"
"strings"
"sync"
"testing"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sha256"
)
var testKeyGen = NewKeyGenerator()
func TestEnDecryptName(t *testing.T) {
pattern := regexp.MustCompile(
fmt.Sprintf("^[0-9A-V]%s/[0-9A-V]{2}/([0-9A-V]{%d}/)*[0-9A-V]{1,%d}$",
@@ -72,13 +72,13 @@ func TestEnDecryptName(t *testing.T) {
}
func TestKeyDerivation(t *testing.T) {
folderKey := KeyFromPassword("my folder", "my password")
folderKey := testKeyGen.KeyFromPassword("my folder", "my password")
encryptedName := encryptDeterministic([]byte("filename.txt"), folderKey, nil)
if base32Hex.EncodeToString(encryptedName) != "3T5957I4IOA20VEIEER6JSQG0PEPIRV862II3K7LOF75Q" {
t.Error("encrypted name mismatch")
}
fileKey := FileKey("filename.txt", folderKey)
fileKey := testKeyGen.FileKey("filename.txt", folderKey)
// fmt.Println(base32Hex.EncodeToString(encryptBytes([]byte("hello world"), fileKey))) => A1IPD...
const encrypted = `A1IPD28ISL7VNPRSSSQM2L31L3IJPC08283RO89J5UG0TI9P38DO9RFGK12DK0KD7PKQP6U51UL2B6H96O`
bs, _ := base32Hex.DecodeString(encrypted)
@@ -137,7 +137,7 @@ func encFileInfo() FileInfo {
return FileInfo{
Name: "hello",
Size: 45,
Permissions: 0755,
Permissions: 0o755,
ModifiedS: 8080,
Sequence: 1000,
Blocks: []BlockInfo{
@@ -159,7 +159,7 @@ func TestEnDecryptFileInfo(t *testing.T) {
var key [32]byte
fi := encFileInfo()
enc := encryptFileInfo(fi, &key)
enc := encryptFileInfo(testKeyGen, fi, &key)
if bytes.Equal(enc.Blocks[0].Hash, enc.Blocks[1].Hash) {
t.Error("block hashes should not repeat when on different offsets")
}
@@ -169,7 +169,7 @@ func TestEnDecryptFileInfo(t *testing.T) {
if enc.Sequence != fi.Sequence {
t.Error("encrypted fileinfo didn't maintain sequence number")
}
again := encryptFileInfo(fi, &key)
again := encryptFileInfo(testKeyGen, fi, &key)
if !bytes.Equal(enc.Blocks[0].Hash, again.Blocks[0].Hash) {
t.Error("block hashes should remain stable (0)")
}
@@ -180,7 +180,7 @@ func TestEnDecryptFileInfo(t *testing.T) {
// Simulate the remote setting the sequence number when writing to db
enc.Sequence = 10
dec, err := DecryptFileInfo(enc, &key)
dec, err := DecryptFileInfo(testKeyGen, enc, &key)
if err != nil {
t.Error(err)
}
@@ -201,7 +201,7 @@ func TestEncryptedFileInfoConsistency(t *testing.T) {
}
files[1].SetIgnored()
for i, f := range files {
enc := encryptFileInfo(f, &key)
enc := encryptFileInfo(testKeyGen, f, &key)
if err := checkFileInfoConsistency(enc); err != nil {
t.Errorf("%v: %v", i, err)
}
@@ -235,22 +235,3 @@ func TestIsEncryptedParent(t *testing.T) {
}
}
}
var benchmarkFileKey struct {
key [keySize]byte
sync.Once
}
func BenchmarkFileKey(b *testing.B) {
benchmarkFileKey.Do(func() {
sha256.SelectAlgo()
rand.Read(benchmarkFileKey.key[:])
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
FileKey("a_kind_of_long_filename.ext", &benchmarkFileKey.key)
}
}

View File

@@ -63,9 +63,7 @@ var sha256OfEmptyBlock = map[int][sha256.Size]byte{
16 << MiB: {0x8, 0xa, 0xcf, 0x35, 0xa5, 0x7, 0xac, 0x98, 0x49, 0xcf, 0xcb, 0xa4, 0x7d, 0xc2, 0xad, 0x83, 0xe0, 0x1b, 0x75, 0x66, 0x3a, 0x51, 0x62, 0x79, 0xc8, 0xb9, 0xd2, 0x43, 0xb7, 0x19, 0x64, 0x3e},
}
var (
errNotCompressible = errors.New("not compressible")
)
var errNotCompressible = errors.New("not compressible")
func init() {
for blockSize := MinBlockSize; blockSize <= MaxBlockSize; blockSize *= 2 {
@@ -231,16 +229,16 @@ const (
// Should not be modified in production code, just for testing.
var CloseTimeout = 10 * time.Second
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string) Connection {
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection {
// Encryption / decryption is first (outermost) before conversion to
// native path formats.
nm := makeNative(receiver)
em := &encryptedModel{model: nm, folderKeys: newFolderKeyRegistry(passwords)}
em := newEncryptedModel(nm, newFolderKeyRegistry(keyGen, passwords), keyGen)
// We do the wire format conversion first (outermost) so that the
// metadata is in wire format when it reaches the encryption step.
rc := newRawConnection(deviceID, reader, writer, closer, em, connInfo, compress)
ec := encryptedConnection{ConnectionInfo: rc, conn: rc, folderKeys: em.folderKeys}
ec := newEncryptedConnection(rc, rc, em.folderKeys, keyGen)
wc := wireFormatConnection{ec}
return wc

View File

@@ -32,10 +32,10 @@ func TestPing(t *testing.T) {
ar, aw := io.Pipe()
br, bw := io.Pipe()
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil))
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c0.Start()
defer closeAndWait(c0, ar, bw)
c1 := getRawConnection(NewConnection(c1ID, br, aw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil))
c1 := getRawConnection(NewConnection(c1ID, br, aw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c1.Start()
defer closeAndWait(c1, ar, bw)
c0.ClusterConfig(ClusterConfig{})
@@ -58,10 +58,10 @@ func TestClose(t *testing.T) {
ar, aw := io.Pipe()
br, bw := io.Pipe()
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways, nil))
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c0.Start()
defer closeAndWait(c0, ar, bw)
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways, nil)
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen)
c1.Start()
defer closeAndWait(c1, ar, bw)
c0.ClusterConfig(ClusterConfig{})
@@ -103,7 +103,7 @@ func TestCloseOnBlockingSend(t *testing.T) {
m := newTestModel()
rw := testutils.NewBlockingRW()
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c.Start()
defer closeAndWait(c, rw)
@@ -154,10 +154,10 @@ func TestCloseRace(t *testing.T) {
ar, aw := io.Pipe()
br, bw := io.Pipe()
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever, nil))
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever, nil, testKeyGen))
c0.Start()
defer closeAndWait(c0, ar, bw)
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever, nil)
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever, nil, testKeyGen)
c1.Start()
defer closeAndWait(c1, ar, bw)
c0.ClusterConfig(ClusterConfig{})
@@ -194,7 +194,7 @@ func TestClusterConfigFirst(t *testing.T) {
m := newTestModel()
rw := testutils.NewBlockingRW()
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c.Start()
defer closeAndWait(c, rw)
@@ -246,7 +246,7 @@ func TestCloseTimeout(t *testing.T) {
m := newTestModel()
rw := testutils.NewBlockingRW()
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c.Start()
defer closeAndWait(c, rw)
@@ -432,8 +432,8 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
bs1, _ := json.MarshalIndent(m1, "", " ")
bs2, _ := json.MarshalIndent(m2, "", " ")
if !bytes.Equal(bs1, bs2) {
os.WriteFile(prefix+"-1.txt", bs1, 0644)
os.WriteFile(prefix+"-2.txt", bs2, 0644)
os.WriteFile(prefix+"-1.txt", bs1, 0o644)
os.WriteFile(prefix+"-2.txt", bs2, 0o644)
return false
}
@@ -794,16 +794,16 @@ func TestIsEquivalent(t *testing.T) {
// Difference in permissions is not OK.
{
a: FileInfo{Permissions: 0444},
b: FileInfo{Permissions: 0666},
a: FileInfo{Permissions: 0o444},
b: FileInfo{Permissions: 0o666},
ignPerms: b(false),
eq: false,
},
// ... unless we say it is
{
a: FileInfo{Permissions: 0666},
b: FileInfo{Permissions: 0444},
a: FileInfo{Permissions: 0o666},
b: FileInfo{Permissions: 0o444},
ignPerms: b(true),
eq: true,
},
@@ -852,8 +852,8 @@ func TestIsEquivalent(t *testing.T) {
// On windows we only check the user writable bit of the permission
// set, so these are equivalent.
cases = append(cases, testCase{
a: FileInfo{Permissions: 0777},
b: FileInfo{Permissions: 0600},
a: FileInfo{Permissions: 0o777},
b: FileInfo{Permissions: 0o600},
ignPerms: b(false),
eq: true,
})
@@ -899,7 +899,7 @@ func TestClusterConfigAfterClose(t *testing.T) {
m := newTestModel()
rw := testutils.NewBlockingRW()
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
c.Start()
defer closeAndWait(c, rw)
@@ -923,7 +923,7 @@ func TestDispatcherToCloseDeadlock(t *testing.T) {
// the model callbacks (ClusterConfig).
m := newTestModel()
rw := testutils.NewBlockingRW()
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
m.ccFn = func(devID DeviceID, cc ClusterConfig) {
c.Close(errManual)
}

View File

@@ -0,0 +1,131 @@
// Copyright (C) 2022 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 syncthing
import (
"encoding/binary"
"io"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
const (
globalMigrationVersion = 1
globalMigrationDBKey = "globalMigrationVersion"
)
func globalMigration(ll *db.Lowlevel, cfg config.Wrapper) error {
miscDB := db.NewMiscDataNamespace(ll)
prevVersion, _, err := miscDB.Int64(globalMigrationDBKey)
if err != nil {
return err
}
if prevVersion >= globalMigrationVersion {
return nil
}
if prevVersion < 1 {
return encryptionTrailerSizeMigration(ll, cfg)
}
return miscDB.PutInt64(globalMigrationDBKey, globalMigrationVersion)
}
func encryptionTrailerSizeMigration(ll *db.Lowlevel, cfg config.Wrapper) error {
encFolders := cfg.Folders()
for folderID, folderCfg := range cfg.Folders() {
if folderCfg.Type != config.FolderTypeReceiveEncrypted {
delete(encFolders, folderID)
}
}
if len(encFolders) == 0 {
return nil
}
l.Infoln("Running global migration to fix encryption file sizes")
// Trigger index re-transfer with fixed up sizes
db.DropDeltaIndexIDs(ll)
for folderID, folderCfg := range encFolders {
fset, err := db.NewFileSet(folderID, ll)
if err != nil {
return err
}
snap, err := fset.Snapshot()
if err != nil {
return err
}
batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error {
// As we can't touch the version, we need to first invalidate the
// files, and then re-add the modified valid files
invalidFiles := make([]protocol.FileInfo, len(files))
for i, f := range files {
f.SetUnsupported()
invalidFiles[i] = f
}
fset.Update(protocol.LocalDeviceID, invalidFiles)
fset.Update(protocol.LocalDeviceID, files)
return nil
})
filesystem := folderCfg.Filesystem(fset)
var innerErr error
snap.WithHave(protocol.LocalDeviceID, func(intf protocol.FileIntf) bool {
fi := intf.(protocol.FileInfo)
size, err := sizeOfEncryptedTrailer(filesystem, fi.Name)
if err != nil {
// Best effort: If we fail to read a file, it will show as
// locally changed on next scan.
return true
}
fi.EncryptionTrailerSize = size
batch.Append(fi)
err = batch.FlushIfFull()
if err != nil {
innerErr = err
return false
}
return true
})
snap.Release()
if innerErr != nil {
return innerErr
}
err = batch.Flush()
if err != nil {
return err
}
}
return nil
}
// sizeOfEncryptedTrailer returns the size of the encrypted trailer on disk.
// This amount of bytes should be subtracted from the file size to get the
// original file size.
func sizeOfEncryptedTrailer(fs fs.Filesystem, name string) (int, error) {
f, err := fs.Open(name)
if err != nil {
return 0, err
}
defer f.Close()
if _, err := f.Seek(-4, io.SeekEnd); err != nil {
return 0, err
}
var buf [4]byte
if _, err := io.ReadFull(f, buf[:]); err != nil {
return 0, err
}
// The stored size is the size of the encrypted data.
size := int(binary.BigEndian.Uint32(buf[:]))
// We add the size of the length word itself as well.
return size + 4, nil
}

View File

@@ -243,7 +243,13 @@ func (a *App) startup() error {
miscDB.PutString("prevVersion", build.Version)
}
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger)
if err := globalMigration(a.ll, a.cfg); err != nil {
l.Warnln("Global migration:", err)
return err
}
keyGen := protocol.NewKeyGenerator()
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger, keyGen)
if a.opts.DeadlockTimeoutS > 0 {
m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second)
@@ -278,7 +284,7 @@ func (a *App) startup() error {
connRegistry := registry.New()
discoveryManager := discover.NewManager(a.myID, a.cfg, a.cert, a.evLogger, addrLister, connRegistry)
connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, discoveryManager, bepProtocolName, tlsDefaultCommonName, a.evLogger, connRegistry)
connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, discoveryManager, bepProtocolName, tlsDefaultCommonName, a.evLogger, connRegistry, keyGen)
addrLister.AddressLister = connectionsService

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STDISCOSRV" "1" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "STDISCOSRV" "1" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STRELAYSRV" "1" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "STRELAYSRV" "1" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-BEP" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-BEP" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-CONFIG" "5" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-DEVICE-IDS" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-EVENT-API" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-FAQ" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0
@@ -172,6 +172,9 @@ Windows ACLs (not preserved)
Devices, FIFOs, and other specials (ignored)
.IP \(bu 2
Sparse file sparseness (will become sparse, when supported by the OS & filesystem)
.IP \(bu 2
Syncthing internal files and folders (e.g. \fB\&.stfolder\fP, \fB\&.stignore\fP,
\fB\&.stversions\fP, temporary files, etc.)
.UNINDENT
.SS Is synchronization fast?
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-GLOBALDISCO" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-LOCALDISCO" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-NETWORKING" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-RELAY" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-REST-API" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp
@@ -1831,7 +1831,7 @@ event types. The \fBevents\fP parameter is not used.
.SS GET /rest/stats/device
.sp
Returns general statistics about devices. Currently, only contains the
time the device was last seen.
time the device was last seen and the last connection duration.
.INDENT 0.0
.INDENT 3.5
.sp
@@ -1841,6 +1841,7 @@ $ curl \-s http://localhost:8384/rest/stats/device | json
{
"P56IOI7\-MZJNU2Y\-IQGDREY\-DM2MGTI\-MGL3BXN\-PQ6W5BM\-TBBZ4TJ\-XZWICQ2": {
"lastSeen" : "2015\-04\-18T11:21:31.3256277+01:00"
"lastConnectionDurationS": 556335.421708141
}
}
.ft P

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-SECURITY" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-STIGNORE" "5" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-VERSIONING" "7" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING" "1" "Feb 22, 2023" "v1.23.1" "Syncthing"
.TH "SYNCTHING" "1" "Mar 11, 2023" "v1.23.2" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS

View File

@@ -123,6 +123,10 @@ message FileInfo {
// etc changed). This is host-local, not sent over the wire.
int64 inode_change_ns = 1002;
// The size of the data appended to the encrypted file on disk. This is
// host-local, not sent over the wire.
int32 encryption_trailer_size = 1003;
bool deleted = 6;
bool invalid = 7 [(ext.goname) = "RawInvalid"];
bool no_permissions = 8;