Compare commits

...
37 Commits
Author SHA1 Message Date
Jakob BorgandGitHub f47de83914 lib/protocol: Ensure starting & closing a connection are exclusive (fixes #9102) (#9103)
In principle a connection can close while it's in progress with
starting, and then it's undefined if we wait for goroutines to exit etc.
With this change, we will wait for start to complete before starting to
stop everything.
2023-09-12 14:48:15 +02:00
tomasz1986andGitHub caedb19307 gui: Remove unused hard-coded styles from Recent Changes modal (#9101)
gui: Remove unused hard-coded styles from globalChangesModalView modal

Currently, the globalChangesModalView modal has hardcoded th and td
styles. However, they are not even used in the modal itself, because
Bootstrap overrides them with its own styles for these elements in the
same modal. Yet, when hard-coded like that, these styles can conflict
with other table elements in the GUI. Thus, remove them completely.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-12 14:47:31 +02:00
bt90andGitHub e860d3b974 lib/connections: Make assumptions about isLAN when interface address listing fails (#9093) 2023-09-12 12:34:30 +00:00
bt90andGitHub ed66fba42b lib/beacon, lib/discover: Send IPv4 limited broadcast when address listing fails (fixes #1628) (#9087) 2023-09-12 14:28:17 +02:00
Jakob Borg 415f320005 build: Update dependencies 2023-09-12 14:08:59 +02:00
Jakob BorgandGitHub 4812600098 lib/versioner: Don't complain when folder is stopping (#9097) 2023-09-11 23:10:18 +02:00
Jakob BorgandGitHub 5ff11ce142 gui: Add help link for numConnections (#9082) 2023-09-11 14:59:48 +02:00
tomasz1986andGitHub 541572781b gui: Add missing translation related to Number of Connections (ref #8918) (#9095)
Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-11 05:50:23 +02:00
Syncthing Release Automation e38679d9bf gui, man, authors: Update docs, translations, and contributors 2023-09-11 03:45:45 +00:00
Jakob Borg f25a169c4c build: Go 1.21.1 or higher 2023-09-06 21:11:19 +02:00
bt90andGitHub 06ac10ee37 cmd/stdiscosrv: Deduplicate addresses (fixes #8482) (#9080) 2023-09-06 14:36:00 +02:00
Jakob Borg 7c0223bd06 lib/build: Next version is the Gold Grasshopper 2023-09-06 13:13:39 +02:00
c6334e61aa all: Support multiple device connections (fixes #141) (#8918)
This adds the ability to have multiple concurrent connections to a single device. This is primarily useful when the network has multiple physical links for aggregated bandwidth. A single connection will never see a higher rate than a single link can give, but multiple connections are load-balanced over multiple links.

It is also incidentally useful for older multi-core CPUs, where bandwidth could be limited by the TLS performance of a single CPU core -- using multiple connections achieves concurrency in the required crypto calculations...

Co-authored-by: Simon Frei <freisim93@gmail.com>
Co-authored-by: tomasz1986 <twilczynski@naver.com>
Co-authored-by: bt90 <btom1990@googlemail.com>
2023-09-06 12:52:01 +02:00
Jakob Borg 38bbdebffa build: Use actions/checkout@v4 2023-09-05 09:52:19 +02:00
Jakob Borg e80d04845e build: Minor dependency update 2023-09-05 09:47:51 +02:00
Syncthing Release Automation 4138e22898 gui, man, authors: Update docs, translations, and contributors 2023-09-04 03:45:39 +00:00
MaximilianandGitHub c42c0e7ceb lib/connections: Fix WANAddresses returning only unspecified IPs (ref #9010) (#9073)
Avoids taking the address of the same variable twice.
2023-09-03 15:03:27 +00:00
Jakob BorgandGitHub 5118538179 lib/model: Refactor folderRunners to use a serviceMap (#9071)
Instead of separately tracking the token.

Also changes serviceMap to have a channel version of RemoveAndWait, so
that it's possible to do the removal under a lock but wait outside of
the lock. And changed where we do that in connection close, reversing
the change that happened when I added the serviceMap in 40b3b9ad1.
2023-09-02 16:42:46 +02:00
tomasz1986andGitHub 4d93648f75 gui: Don't hide default values for folders and devices (#8987)
Currently, some of the information for folders and devices displayed in
the GUI relies on arbitrary values that come pre-set as defaults on a
fresh Syncthing installation, i.e. if the value matches the default, it
is hidden, and if does not, then it is displayed.

With this change, the GUI always displays all information regardless
of their value, making the overall experience more consistent and
predictable.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-02 12:19:18 +02:00
tomasz1986andGitHub 29f100c162 gui: Fix File Versioning icon to match in all places (#9070)
Currently, different icons are used for File Versioning when displayed
in the unfolded folder info in the main part of the GUI, and the icon
used in the Edit Folder modal. This changes the main GUI icon to match
the icon used in the modal.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-01 11:15:20 +02:00
tomasz1986andGitHub cd98a43b80 gui: Fix Logs modal icon to match header icon (ref #9067) (#9069)
The Logs icon was changed in [1] in the header, however the icon used in
the modal was left out. This changes it, so that the header and the
modal icons match.

[1] 2abba1dfb0

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-01 11:14:28 +02:00
Jakob Borg 4bf982376e build: Be more subtle about cross compilation errors
Summarize platforms that fail to build, without overloading the build
log with errors that we anyway ignore. (Currently freebsd/riscv64 fails
to build.)
2023-09-01 09:16:14 +02:00
Jakob BorgandGitHub 29056d5873 build: Update dependencies (#9068) 2023-09-01 08:39:15 +02:00
tomasz1986andGitHub 2abba1dfb0 gui: Remove footer and move links to header (fixes #5607) (#9067)
* gui: Remove footer and move links to header (fixes #5607)

Currently, the footer is always present and takes space at the bottom of
the GUI. However, the links listed there are not part of everyday user
interaction, and as such, they unnecessarily clutter the page, reducing
the usable screen space. Thus, transform the current Help link in the
header into a Help dropdown menu, and move the links from the footer
into it.

Also apply the following tweaks:

1. Move the About dialog from Actions to Help.
2. Add an Introduction (to the GUI) link to Help.
3. Change the Support icon from a question mark to a group of people.
4. Change the Changelog and About icons to a filled version to match the
   other icons better.
5. Use a source code icon for Source Code instead of a wrench icon, and
   move the wrench icon to Logs. This is done to prevent Changelog and
   Logs from using the same icon.
6. Update all dropdown icons' Fork Awesome styles to "fa fa-fw <icon>".

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>

* a few more Fork Awesome style updates

---------

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-09-01 08:18:30 +02:00
tomasz1986andGitHub 325b3b114f gui: Fix lastSeenDays error due to undefined deviceStats when adding new devices (ref #8730) (#9066) 2023-09-01 07:22:04 +02:00
tomasz1986andGitHub 03590e5ac7 gui: Automatically select device ID on click (ref #8544) (#9065)
The CSS method to select device IDs on click was added in [1]. However,
it was later mistakenly overwritten by [2]. This commit fixes the
regression and also applies the same behaviour to the Edit Device modal
which was omitted in the original commit.

[1] 5baf5fedb5
[2] 5e384c9185

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-08-31 22:16:59 +02:00
tomasz1986andGitHub 95b3c26da7 gui: Prevent modifications when saving changes (fixes #9019) (#9063) 2023-08-31 17:11:03 +02:00
tomasz1986andGitHub 3e5f0b1d0e gui: Show in GUI if limitBandwidthInLan is enabled (#9062) 2023-08-31 07:22:24 +02:00
Jakob BorgandGitHub 3130af3773 lib/upgrade: Enable HTTP/2 for upgrade checks (#9060) 2023-08-30 21:58:34 +02:00
Jakob BorgandGitHub abd89f15f7 lib/discover: Enable HTTP/2 for global discovery requests (#9059)
By creating the http.Transport and tls.Configuration ourselves we
override some default behavior and end up with a client that speaks only
HTTP/1.1.

This adds a call to http.ConfigureTransport to do the relevant magic to
enable HTTP/2.

Also tweaks the keepalive settings to be a little kinder to the
server(s).
2023-08-30 21:58:05 +02:00
Jakob Borg a80e6be353 cmd/stdiscosrv: Streamline context handling 2023-08-30 09:36:27 +02:00
Jakob Borg acc532fc60 cmd/stdiscosrv: Explicitly enable HTTP/2
The server supports it, but it's not negotiated unless explicitly
allowed in the TLS config NextProtos.
2023-08-30 09:09:52 +02:00
Syncthing Release Automation 3cc3fb7504 gui, man, authors: Update docs, translations, and contributors 2023-08-28 03:45:57 +00:00
Jakob Borg a04cc95005 cmd/stdiscosrv: Separate HTTPS and replication certificates 2023-08-23 13:43:54 +02:00
Jakob Borg 480fa4b915 cmd/stdiscosrv: Use larger database settings 2023-08-23 13:43:14 +02:00
Jakob Borg 92a4931850 cmd/stdiscosrv: Modernise TLS settings, remove excessive HTTP logging 2023-08-23 13:39:52 +02:00
Jakob Borg bdfef9010f cmd/stdiscosrv: Serve compressed responses 2023-08-23 13:39:14 +02:00
101 changed files with 2134 additions and 1239 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ on:
- infrastructure
env:
GO_VERSION: "^1.21.0"
GO_VERSION: "~1.21.1"
CGO_ENABLED: "0"
BUILD_USER: docker
BUILD_HOST: github.syncthing.net
@@ -24,7 +24,7 @@ jobs:
- strelaypoolsrv
- stupgrades
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
+24 -18
View File
@@ -12,7 +12,7 @@ env:
# The go version to use for builds. We set check-latest to true when
# installing, so we get the latest patch version that matches the
# expression.
GO_VERSION: "~1.21.0"
GO_VERSION: "~1.21.1"
# Optimize compatibility on the slow archictures.
GO386: softfloat
@@ -61,7 +61,7 @@ jobs:
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
@@ -95,7 +95,7 @@ jobs:
name: Check correctness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
@@ -128,7 +128,7 @@ jobs:
- package-debian
- govulncheck
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
#
# Windows
@@ -149,7 +149,7 @@ jobs:
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -197,7 +197,7 @@ jobs:
name: Package for Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -239,7 +239,7 @@ jobs:
environment: signing
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -361,7 +361,7 @@ jobs:
name: Package cross compiled
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -393,12 +393,18 @@ jobs:
| grep -v /wasm \
)
# Build for each platform with errors silenced, because we expect
# some oddball platforms to fail. This avoids a bunch of errors in
# the GitHub Actions output, instead summarizing each build
# failure as a warning.
for plat in $platforms; do
goos="${plat%/*}"
goarch="${plat#*/}"
if ! go run build.go -goos "$goos" -goarch "$goarch" tar ; then
echo "*** $plat failed ***"
echo "::group ::$plat"
if ! go run build.go -goos "$goos" -goarch "$goarch" tar 2>/dev/null; then
echo "::warning ::Failed to build for $plat"
fi
echo "::endgroup::"
done
env:
CGO_ENABLED: "0"
@@ -417,7 +423,7 @@ jobs:
name: Package source code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -466,11 +472,11 @@ jobs:
- package-source
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
repository: syncthing/release-tools
path: tools
@@ -529,7 +535,7 @@ jobs:
name: Package for Debian
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -581,7 +587,7 @@ jobs:
- notarize-macos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
repository: syncthing/release-tools
path: tools
@@ -625,7 +631,7 @@ jobs:
- package-debian
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -696,7 +702,7 @@ jobs:
dockerfile: Dockerfile.stdiscosrv
image: syncthing/discosrv
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -774,7 +780,7 @@ jobs:
runs-on: ubuntu-latest
name: Run govulncheck
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
name: Push to release-nightly to trigger build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
fetch-depth: 0
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
name: Update translations and documentation
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
+1
View File
@@ -217,6 +217,7 @@ Max <github@germancoding.com>
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
MaximAL <almaximal@ya.ru>
Maxime Thirouin <m@moox.io>
Maximilian <maxi.rostock@outlook.de>
mclang <1721600+mclang@users.noreply.github.com>
Michael Jephcote (Rewt0r) <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
Michael Ploujnikov (plouj) <ploujj@gmail.com>
+34 -28
View File
@@ -8,6 +8,7 @@ package main
import (
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"encoding/base64"
@@ -15,6 +16,7 @@ import (
"encoding/pem"
"errors"
"fmt"
io "io"
"log"
"math/rand"
"net"
@@ -27,6 +29,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/stringutil"
)
// announcement is the format received from and sent to clients
@@ -78,18 +81,10 @@ func (s *apiSrv) Serve(_ context.Context) error {
s.listener = listener
} else {
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{s.cert},
ClientAuth: tls.RequestClientCert,
SessionTicketsDisabled: true,
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
},
Certificates: []tls.Certificate{s.cert},
ClientAuth: tls.RequestClientCert,
MinVersion: tls.VersionTLS12,
NextProtos: []string{"h2", "http/1.1"},
}
tlsListener, err := tls.Listen("tcp", s.addr, tlsCfg)
@@ -107,6 +102,7 @@ func (s *apiSrv) Serve(_ context.Context) error {
ReadTimeout: httpReadTimeout,
WriteTimeout: httpWriteTimeout,
MaxHeaderBytes: httpMaxHeaderBytes,
ErrorLog: log.New(io.Discard, "", 0),
}
err := srv.Serve(s.listener)
@@ -116,8 +112,6 @@ func (s *apiSrv) Serve(_ context.Context) error {
return err
}
var topCtx = context.Background()
func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
t0 := time.Now()
@@ -130,10 +124,10 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
}()
reqID := requestID(rand.Int63())
ctx := context.WithValue(topCtx, idKey, reqID)
req = req.WithContext(context.WithValue(req.Context(), idKey, reqID))
if debug {
log.Println(reqID, req.Method, req.URL)
log.Println(reqID, req.Method, req.URL, req.Proto)
}
remoteAddr := &net.TCPAddr{
@@ -159,17 +153,17 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
}
switch req.Method {
case "GET":
s.handleGET(ctx, lw, req)
case "POST":
s.handlePOST(ctx, remoteAddr, lw, req)
case http.MethodGet:
s.handleGET(lw, req)
case http.MethodPost:
s.handlePOST(remoteAddr, lw, req)
default:
http.Error(lw, "Method Not Allowed", http.StatusMethodNotAllowed)
}
}
func (s *apiSrv) handleGET(ctx context.Context, w http.ResponseWriter, req *http.Request) {
reqID := ctx.Value(idKey).(requestID)
func (s *apiSrv) handleGET(w http.ResponseWriter, req *http.Request) {
reqID := req.Context().Value(idKey).(requestID)
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
if err != nil {
@@ -220,16 +214,25 @@ func (s *apiSrv) handleGET(ctx context.Context, w http.ResponseWriter, req *http
lookupRequestsTotal.WithLabelValues("success").Inc()
bs, _ := json.Marshal(announcement{
Seen: time.Unix(0, rec.Seen),
w.Header().Set("Content-Type", "application/json")
var bw io.Writer = w
// Use compression if the client asks for it
if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Encoding", "gzip")
gw := gzip.NewWriter(bw)
defer gw.Close()
bw = gw
}
json.NewEncoder(bw).Encode(announcement{
Seen: time.Unix(0, rec.Seen).Truncate(time.Second),
Addresses: addressStrs(rec.Addresses),
})
w.Header().Set("Content-Type", "application/json")
w.Write(bs)
}
func (s *apiSrv) handlePOST(ctx context.Context, remoteAddr *net.TCPAddr, w http.ResponseWriter, req *http.Request) {
reqID := ctx.Value(idKey).(requestID)
func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req *http.Request) {
reqID := req.Context().Value(idKey).(requestID)
rawCert, err := certificateBytes(req)
if err != nil {
@@ -438,6 +441,9 @@ func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
fixed = append(fixed, uri.String())
}
// Remove duplicate addresses
fixed = stringutil.UniqueTrimmedStrings(fixed)
return fixed
}
+2 -8
View File
@@ -15,6 +15,7 @@ import (
"sort"
"time"
"github.com/syncthing/syncthing/lib/sliceutil"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
@@ -352,14 +353,7 @@ func expire(addrs []DatabaseAddress, now int64) []DatabaseAddress {
i := 0
for i < len(addrs) {
if addrs[i].Expires < now {
// This item is expired. Replace it with the last in the list
// (noop if we are at the last item).
addrs[i] = addrs[len(addrs)-1]
// Wipe the last item of the list to release references to
// strings and stuff.
addrs[len(addrs)-1] = DatabaseAddress{}
// Shorten the slice.
addrs = addrs[:len(addrs)-1]
addrs = sliceutil.RemoveAndZero(addrs, i)
continue
}
i++
+1 -1
View File
@@ -185,7 +185,7 @@ func TestFilter(t *testing.T) {
},
{
a: []DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "c", Expires: 5}, {Address: "d", Expires: 15}, {Address: "e", Expires: 5}},
b: []DatabaseAddress{{Address: "d", Expires: 15}, {Address: "b", Expires: 15}}, // gets reordered
b: []DatabaseAddress{{Address: "b", Expires: 15}, {Address: "d", Expires: 15}},
},
}
+29 -6
View File
@@ -64,9 +64,7 @@ var levelDBOptions = &opt.Options{
WriteBuffer: 32 << 20, // default 4<<20
}
var (
debug = false
)
var debug = false
func main() {
var listen string
@@ -76,20 +74,26 @@ func main() {
var replicationPeers string
var certFile string
var keyFile string
var replCertFile string
var replKeyFile string
var useHTTP bool
var largeDB bool
log.SetOutput(os.Stdout)
log.SetFlags(0)
flag.StringVar(&certFile, "cert", "./cert.pem", "Certificate file")
flag.StringVar(&keyFile, "key", "./key.pem", "Key file")
flag.StringVar(&dir, "db-dir", "./discovery.db", "Database directory")
flag.BoolVar(&debug, "debug", false, "Print debug output")
flag.BoolVar(&useHTTP, "http", false, "Listen on HTTP (behind an HTTPS proxy)")
flag.StringVar(&listen, "listen", ":8443", "Listen address")
flag.StringVar(&keyFile, "key", "./key.pem", "Key file")
flag.StringVar(&metricsListen, "metrics-listen", "", "Metrics listen address")
flag.StringVar(&replicationPeers, "replicate", "", "Replication peers, id@address, comma separated")
flag.StringVar(&replicationListen, "replication-listen", ":19200", "Replication listen address")
flag.StringVar(&replCertFile, "replication-cert", "", "Certificate file for replication")
flag.StringVar(&replKeyFile, "replication-key", "", "Key file for replication")
flag.BoolVar(&largeDB, "large-db", false, "Use larger database settings")
showVersion := flag.Bool("version", false, "Show version")
flag.Parse()
@@ -98,6 +102,15 @@ func main() {
return
}
if largeDB {
levelDBOptions.BlockCacheCapacity = 64 << 20
levelDBOptions.BlockSize = 64 << 10
levelDBOptions.CompactionTableSize = 16 << 20
levelDBOptions.CompactionTableSizeMultiplier = 2.0
levelDBOptions.WriteBuffer = 64 << 20
levelDBOptions.CompactionL0Trigger = 8
}
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if os.IsNotExist(err) {
log.Println("Failed to load keypair. Generating one, this might take a while...")
@@ -111,6 +124,16 @@ func main() {
devID := protocol.NewDeviceID(cert.Certificate[0])
log.Println("Server device ID is", devID)
replCert := cert
if replCertFile != "" && replKeyFile != "" {
replCert, err = tls.LoadX509KeyPair(replCertFile, replKeyFile)
if err != nil {
log.Fatalln("Failed to load replication keypair:", err)
}
}
replDevID := protocol.NewDeviceID(replCert.Certificate[0])
log.Println("Replication device ID is", replDevID)
// Parse the replication specs, if any.
var allowedReplicationPeers []protocol.DeviceID
var replicationDestinations []string
@@ -165,14 +188,14 @@ func main() {
// Start any replication senders.
var repl replicationMultiplexer
for _, dst := range replicationDestinations {
rs := newReplicationSender(dst, cert, allowedReplicationPeers)
rs := newReplicationSender(dst, replCert, allowedReplicationPeers)
main.Add(rs)
repl = append(repl, rs)
}
// If we have replication configured, start the replication listener.
if len(allowedReplicationPeers) > 0 {
rl := newReplicationListener(replicationListen, cert, allowedReplicationPeers, db)
rl := newReplicationListener(replicationListen, replCert, allowedReplicationPeers, db)
main.Add(rl)
}
+13 -13
View File
@@ -21,7 +21,7 @@ require (
github.com/gogo/protobuf v1.3.2
github.com/golang/snappy v0.0.4 // indirect
github.com/greatroar/blobloom v0.7.2
github.com/hashicorp/golang-lru/v2 v2.0.5
github.com/hashicorp/golang-lru/v2 v2.0.6
github.com/jackpal/gateway v1.0.10
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
@@ -38,23 +38,23 @@ require (
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/quic-go/quic-go v0.38.0
github.com/quic-go/quic-go v0.38.1
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.7
github.com/shirou/gopsutil/v3 v3.23.8
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.14
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/crypto v0.13.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0
golang.org/x/sys v0.11.0
golang.org/x/text v0.12.0
golang.org/x/net v0.15.0
golang.org/x/sys v0.12.0
golang.org/x/text v0.13.0
golang.org/x/time v0.3.0
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846
golang.org/x/tools v0.13.0
google.golang.org/protobuf v1.31.0
)
@@ -65,14 +65,14 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
)
+30 -30
View File
@@ -80,12 +80,12 @@ 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-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb h1:LCMfzVg3sflxTs4UvuP4D8CkoZnfHLe2qzqgDn/4OHs=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
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.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM=
github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -126,20 +126,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.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
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.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b h1:vab8deKC4QoIfm9fJM59iuNz1ELGsuLoYYpiF+pHiG8=
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA=
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -158,10 +158,10 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -169,8 +169,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.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE=
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -189,8 +189,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/thejerf/suture/v4 v4.0.2 h1:VxIH/J8uYvqJY1+9fxi5GBfGRkRZ/jlSOP6x9HijFQc=
github.com/thejerf/suture/v4 v4.0.2/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
@@ -207,10 +207,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
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=
@@ -232,8 +232,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
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=
@@ -269,12 +269,12 @@ 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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -285,8 +285,8 @@ 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/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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=
@@ -297,8 +297,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
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=
-28
View File
@@ -8,7 +8,6 @@
*/
body {
padding-bottom: 70px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
overflow-y: scroll;
}
@@ -398,25 +397,6 @@ ul.three-columns li, ul.two-columns li {
width: 100%;
}
/** Footer nav on small devices **/
@media (max-width: 1199px) {
/* Stay at the end of the page, with space reserved for the footer
usually taking up two rows. */
html {
position: relative;
min-height: 100%;
}
body {
padding-bottom: 60px;
}
.navbar-fixed-bottom {
position: absolute;
}
}
@media (max-width: 768px) {
/* Layout after the normal contents, as this is when the footer switches
to a vertical layout. */
@@ -429,10 +409,6 @@ ul.three-columns li, ul.two-columns li {
margin: 3.25px -15px;
}
.navbar-fixed-bottom {
position: relative;
}
.navbar-nav .open .dropdown-menu {
position: absolute;
left: auto;
@@ -476,10 +452,6 @@ ul.three-columns li, ul.two-columns li {
.navbar-nav .open .dropdown-menu > li > a {
padding: 12px 15px 12px 25px;
}
.navbar-fixed-bottom li {
width: 100%;
}
}
.tab-content {
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Външна команда управлява версиите. Тя трябва да премахне файла от синхронизираната папка. Ако в пътя до приложението има интервали, то той трябва да бъде поставен в кавички.",
"Anonymous Usage Reporting": "Анонимно отчитане на употреба",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?",
"Applied to LAN": "Приложено към LAN",
"Apply": "Прилагане",
"Are you sure you want to override all remote changes?": "Сигурни ли сте, че желаете да отмените всички промени, направени отдалечено?",
"Are you sure you want to permanently delete all these files?": "Сигурни ли сте, че желаете всички тези файлове да бъдат безвъзвратно премахнати?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Вътрешно използвани пътища:",
"Introduced By": "Предложено от",
"Introducer": "Поръчител",
"Introduction": "Въведение",
"Inversion of the given condition (i.e. do not exclude)": "Обръща значението на условието (напр. да не се отхвърля)",
"Keep Versions": "Пазени версии",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Отменяне",
"Revert Local Changes": "Отменяне на местни промени",
"Save": "Запазване",
"Saving changes": "Запазване на промени",
"Scan Time Remaining": "Оставащо време до обхождане",
"Scanning": "Обхождане",
"See external versioning help for supported templated command line parameters.": "Прочетете ръководството за външното управление на версии, за да се запознаете с шаблонните параметри.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing очаква опити за установяване на връзка от други устройства на следните мрежови адреси:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing не слуша за опити за установяване на връзка от други устройства. Вероятно работят само изходящите връзки от това устройство.",
"Syncthing is restarting.": "Syncthing се рестартира.",
"Syncthing is saving changes.": "Syncthing запазва промените.",
"Syncthing is upgrading.": "Syncthing се обновява.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing вече поддържа автоматично докладване на сривове на разработчиците. Тази възможност е включена по подразбиране.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Изглежда, че Syncthing не работи или няма достъп до интернет. Извършва се повторен опит…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Die Versionskontrolle erfolgt über einen externen Befehl. Die Datei aus dem freigegebenen Ordner muss entfernen werden. Wenn der Pfad der Anwendung Leerzeichen enthält, sollte dieser in Anführungszeichen stehen.",
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
"Applied to LAN": "Angewendet auf LAN",
"Apply": "Anwenden",
"Are you sure you want to override all remote changes?": "Sollen wirklich alle entfernten Änderungen überschrieben werden?",
"Are you sure you want to permanently delete all these files?": "Sollen all diese Dateien wirklich dauerhaft gelöscht werden?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Intern verwendete Pfade:",
"Introduced By": "Verteilt von",
"Introducer": "Verteilergerät",
"Introduction": "Einführung",
"Inversion of the given condition (i.e. do not exclude)": "Umkehrung der angegebenen Bedingung (d. h. schließe nicht aus)",
"Keep Versions": "Versionen erhalten",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Zurücksetzen",
"Revert Local Changes": "Lokale Änderungen zurücksetzen",
"Save": "Speichern",
"Saving changes": "Änderungen werden gespeichert",
"Scan Time Remaining": "Verbleibende Scanzeit",
"Scanning": "Scannen",
"See external versioning help for supported templated command line parameters.": "Siehe Hilfe zur externen Versionierung für unterstützte Befehlszeilenparameter.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing lauscht an den folgenden Netzwerkadressen auf Verbindungsversuche von anderen Geräten:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lauscht nicht auf Verbindungsversuche von anderen Geräten auf irgendeiner Adresse. Nur von diesem Gerät ausgehende Verbindungen können funktionieren.",
"Syncthing is restarting.": "Syncthing wird neu gestartet.",
"Syncthing is saving changes.": "Syncthing speichert Änderungen.",
"Syncthing is upgrading.": "Syncthing wird aktualisiert.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit deiner Internetverbindung. Versuche erneut…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.",
"Anonymous Usage Reporting": "Anonymous Usage Reporting",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
"Applied to LAN": "Applied to LAN",
"Apply": "Apply",
"Are you sure you want to override all remote changes?": "Are you sure you want to override all remote changes?",
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Internally used paths:",
"Introduced By": "Introduced By",
"Introducer": "Introducer",
"Introduction": "Introduction",
"Inversion of the given condition (i.e. do not exclude)": "Inversion of the given condition (i.e. do not exclude)",
"Keep Versions": "Keep Versions",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Revert",
"Revert Local Changes": "Revert Local Changes",
"Save": "Save",
"Saving changes": "Saving changes",
"Scan Time Remaining": "Scan Time Remaining",
"Scanning": "Scanning",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing is listening on the following network addresses for connection attempts from other devices:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is saving changes.": "Syncthing is saving changes.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
+9
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.",
"Anonymous Usage Reporting": "Anonymous Usage Reporting",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
"Applied to LAN": "Applied to LAN",
"Apply": "Apply",
"Are you sure you want to override all remote changes?": "Are you sure you want to override all remote changes?",
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
@@ -64,6 +65,7 @@
"Configured": "Configured",
"Connected (Unused)": "Connected (Unused)",
"Connection Error": "Connection Error",
"Connection Management": "Connection Management",
"Connection Type": "Connection Type",
"Connections": "Connections",
"Connections via relays might be rate limited by the relay": "Connections via relays might be rate limited by the relay",
@@ -205,6 +207,7 @@
"Internally used paths:": "Internally used paths:",
"Introduced By": "Introduced By",
"Introducer": "Introducer",
"Introduction": "Introduction",
"Inversion of the given condition (i.e. do not exclude)": "Inversion of the given condition (i.e. do not exclude)",
"Keep Versions": "Keep Versions",
"LDAP": "LDAP",
@@ -257,6 +260,7 @@
"No upgrades": "No upgrades",
"Not shared": "Not shared",
"Notice": "Notice",
"Number of Connections": "Number of Connections",
"OK": "OK",
"Off": "Off",
"Oldest First": "Oldest First",
@@ -324,6 +328,7 @@
"Revert": "Revert",
"Revert Local Changes": "Revert Local Changes",
"Save": "Save",
"Saving changes": "Saving changes",
"Scan Time Remaining": "Scan Time Remaining",
"Scanning": "Scanning",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
@@ -390,6 +395,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing is listening on the following network addresses for connection attempts from other devices:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is saving changes.": "Syncthing is saving changes.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
@@ -422,11 +428,13 @@
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
"The maximum age must be a number and cannot be blank.": "The maximum age must be a number and cannot be blank.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "The maximum time to keep a version (in days, set to 0 to keep versions forever).",
"The number of connections must be a non-negative number.": "The number of connections must be a non-negative number.",
"The number of days must be a number and cannot be blank.": "The number of days must be a number and cannot be blank.",
"The number of days to keep files in the trash can. Zero means forever.": "The number of days to keep files in the trash can. Zero means forever.",
"The number of old versions to keep, per file.": "The number of old versions to keep, per file.",
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit is applied to the accumulated traffic of all connections to this device.": "The rate limit is applied to the accumulated traffic of all connections to this device.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The remote device has paused this folder.": "The remote device has paused this folder.",
@@ -495,6 +503,7 @@
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.",
"Yes": "Yes",
"Yesterday": "Yesterday",
"You can also copy and paste the text into a new message manually.": "You can also copy and paste the text into a new message manually.",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Un comando externo maneja las versiones. Tienes que eliminar el archivo de la carpeta compartida. Si la ruta a la aplicación contiene espacios, ésta debe estar entre comillas.",
"Anonymous Usage Reporting": "Informe anónimo de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe de uso anónimo a cambiado. ¿Desearía usar el nuevo formato?",
"Applied to LAN": "Aplicado a la LAN",
"Apply": "Solicitar",
"Are you sure you want to override all remote changes?": "¿Está seguro(a) de que desea sobreescribir todos los cambios remotos?",
"Are you sure you want to permanently delete all these files?": "¿Está seguro de que desea eliminar permanente todos estos archivos?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Rutas de uso interno:",
"Introduced By": "Introducido por",
"Introducer": "Presentador",
"Introduction": "Introducción",
"Inversion of the given condition (i.e. do not exclude)": "Inversión de la condición dada (por ejemplo, \"no excluir\")",
"Keep Versions": "Mantener versiones",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Revertir",
"Revert Local Changes": "Revertir Cambios Locales",
"Save": "Guardar",
"Saving changes": "Guardar los cambios",
"Scan Time Remaining": "Tiempo Restante de Escaneo",
"Scanning": "Analizando",
"See external versioning help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing está a la escucha en las siguientes direcciones de red en busca de intentos de conexión de otros dispositivos:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing no está a la escucha de intentos de conexión de otros dispositivos en ninguna dirección. Solo pueden funcionar las conexiones salientes de este dispositivo.",
"Syncthing is restarting.": "Syncthing se está reiniciando.",
"Syncthing is saving changes.": "La sincronización guarda los cambios.",
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora soporta el reportar automáticamente las fallas a los desarrolladores. Esta característica está habilitada por defecto.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Parece que la sincronización no funciona o hay un problema con la conexión a Internet. Intentando lo otra vez…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
"Applied to LAN": "Appliqué au LAN",
"Apply": "Appliquer",
"Are you sure you want to override all remote changes?": "Voulez-vous vraiment écraser tous les changements distants ?",
"Are you sure you want to permanently delete all these files?": "Êtes-vous sûrs de vouloir définitivement supprimer tous ces fichiers ?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Chemins utilisés en interne :",
"Introduced By": "Introduit par",
"Introducer": "Appareil introducteur",
"Introduction": "Introduction",
"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",
@@ -324,6 +326,7 @@
"Revert": "Rétablir",
"Revert Local Changes": "Annuler les modifications locales",
"Save": "Enregistrer",
"Saving changes": "Enregistrement des modifications",
"Scan Time Remaining": "Temps d'analyse restant",
"Scanning": "Analyse",
"See external versioning help for supported templated command line parameters.": "Consulter l'aide de la gestion externe des versions pour les paramètres supportés de modèles de lignes de commande.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing écoute sur les adresses réseau suivantes les tentatives de connexions des autres appareils :",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing n'écoute les tentatives de connexion des autres appareils sur aucune adresse. Seules les connexions sortantes de cet appareil peuvent fonctionner.",
"Syncthing is restarting.": "Syncthing redémarre.",
"Syncthing is saving changes.": "Syncthing enregistre les changements.",
"Syncthing is upgrading.": "Syncthing se met à jour.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing permet maintenant d'envoyer automatiquement aux développeurs des rapports de plantage. Cette fonctionnalité est activée par défaut.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing semble être arrêté, ou il y a un problème avec votre connexion Internet. Nouvelle tentative…",
+6 -2
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
"Applied to LAN": "Applica alla LAN",
"Apply": "Applica",
"Are you sure you want to override all remote changes?": "Sei sicuro di voler sovrascrivere tutte le modifiche remote?",
"Are you sure you want to permanently delete all these files?": "Sei sicuro di voler eliminare definitivamente tutti questi file?",
@@ -170,7 +171,7 @@
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Il tipo di cartella \"{{receiveEncrypted}}\" non può essere modificato dopo aver aggiunto la cartella. È necessario rimuovere la cartella, eliminare o decrittografare i dati sul disco e aggiungere nuovamente la cartella.",
"Folders": "Cartelle",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Per le seguenti cartelle si è verificato un errore durante l'avvio della ricerca delle modifiche. Sarà ripetuto ogni minuto, quindi gli errori potrebbero risolversi presto. Se persistono, prova a risolvere il problema sottostante e chiedi aiuto se non puoi.",
"Forever": "Sempre",
"Forever": "Per sempre",
"Full Rescan Interval (s)": "Intervallo di scansione completa (s)",
"GUI": "Interfaccia Grafica Utente",
"GUI / API HTTPS Certificate": "Certificati HTTPS GUI / API",
@@ -205,6 +206,7 @@
"Internally used paths:": "Percorsi interni utilizzati:",
"Introduced By": "Introdotto da",
"Introducer": "Introduttore",
"Introduction": "Introduzione",
"Inversion of the given condition (i.e. do not exclude)": "Inversione della condizione indicata (ad es. non escludere)",
"Keep Versions": "Versioni Mantenute",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Ripristina",
"Revert Local Changes": "Ripristina le modifiche locali",
"Save": "Salva",
"Saving changes": "Salvataggio delle modifiche",
"Scan Time Remaining": "Tempo di Scansione Rimanente",
"Scanning": "Scansione in corso",
"See external versioning help for supported templated command line parameters.": "Consultare la guida al controllo di versione per i modelli dei parametri di riga di comando supportati.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing è in ascolto sui seguenti indirizzi di rete per i tentativi di connessione da altri dispositivi:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing non è in ascolto di tentativi di connessione da altri dispositivi su qualsiasi indirizzo. Possono funzionare solo le connessioni in uscita da questo dispositivo.",
"Syncthing is restarting.": "Riavvio di Syncthing in corso.",
"Syncthing is saving changes.": "Syncthing sta salvando le modifiche.",
"Syncthing is upgrading.": "Aggiornamento di Syncthing in corso.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ora supporta la segnalazione automaticamente agli sviluppatori degli arresti anomali. Questa funzione è abilitata per impostazione predefinita.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing sembra inattivo, oppure c'è un problema con la tua connessione a Internet. Nuovo tentativo…",
@@ -522,7 +526,7 @@
"seconds": "secondi",
"theme-name-black": "Nero",
"theme-name-dark": "Scuro",
"theme-name-default": "Default",
"theme-name-default": "Predefinito",
"theme-name-light": "Chiaro",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vuole condividere la cartella \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vuole condividere la cartella \"{{folderlabel}}\" ({{folder}}).",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "외부 명령이 파일 버전을 관리합니다. 공유 폴더에서 파일을 삭제해야 합니다. 응용 프로그램의 경로에 공백이 있으면 따옴표로 묶어야 합니다.",
"Anonymous Usage Reporting": "익명 사용 보고",
"Anonymous usage report format has changed. Would you like to move to the new format?": "익명 사용 보고의 형식이 변경되었습니다. 새 형식으로 설정을 변경하시겠습니까?",
"Applied to LAN": "근거리 통신망(LAN)에 적용됨",
"Apply": "적용",
"Are you sure you want to override all remote changes?": "다른 기기의 변경 항목 모두를 정말로 덮어쓰시겠습니까?",
"Are you sure you want to permanently delete all these files?": "이 파일 모두를 정말로 영구 삭제하시겠습니까?",
@@ -205,6 +206,7 @@
"Internally used paths:": "내부적으로 사용되는 경로:",
"Introduced By": "소개한 기기",
"Introducer": "소개자",
"Introduction": "소개",
"Inversion of the given condition (i.e. do not exclude)": "특정한 조건의 반대(즉, 배제하지 않음)",
"Keep Versions": "버전 수",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "되돌리기",
"Revert Local Changes": "현재 기기 변경 항목 되돌리기",
"Save": "저장",
"Saving changes": "변경 사항 저장 중",
"Scan Time Remaining": "남은 탐색 시간",
"Scanning": "탐색",
"See external versioning help for supported templated command line parameters.": "지원하는 견본 명령 매개 변수에 대해서는 외부 파일 버전 관리의 도움말을 참조하십시오.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing이 다른 기기로부터 들어오는 접속 시도를 다음 주소에서 대기 중입니다.",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing이 다른 기기로부터 들어오는 접속 시도를 대기하는 주소가 존재하지 않습니다. 현재 기기에서 전송하는 접속만으로 연결이 이루어질 수 있습니다.",
"Syncthing is restarting.": "Syncthing이 재시작 중입니다.",
"Syncthing is saving changes.": "Syncthing이 변경 사항을 저장 중입니다.",
"Syncthing is upgrading.": "Syncthing이 업데이트 중입니다.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "개발자에게 충돌을 자동으로 보고하는 기능이 Syncthing에 추가되었습니다. 이 기능은 기본값으로 활성화되어 있습니다.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing이 중지되었거나 인터넷 연결에 문제가 발생했습니다. 재시도 중…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map. Als het pad naar de toepassing spaties bevat, moet dit tussen aanhalingstekens geplaatst worden.",
"Anonymous Usage Reporting": "Anonieme gebruikersstatistieken",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wilt u naar het nieuwe formaat overschakelen?",
"Applied to LAN": "Toegepast op LAN",
"Apply": "Toepassen",
"Are you sure you want to override all remote changes?": "Weet u zeker dat u alle externe wijzigingen wilt overschrijven?",
"Are you sure you want to permanently delete all these files?": "Weet u zeker dat u al deze bestanden permanent wilt verwijderen?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Intern gebruikte paden:",
"Introduced By": "Geïntroduceerd door",
"Introducer": "Introductie-apparaat",
"Introduction": "Introductie",
"Inversion of the given condition (i.e. do not exclude)": "Inversie van de gegeven voorwaarde (d.w.z. niet uitsluiten)",
"Keep Versions": "Versies behouden",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Terugdraaien",
"Revert Local Changes": "Lokale wijzigingen terugdraaien",
"Save": "Opslaan",
"Saving changes": "Wijzigingen opslaan",
"Scan Time Remaining": "Resterende scantijd",
"Scanning": "Scannen",
"See external versioning help for supported templated command line parameters.": "Bekijk de documentatie van extern versiebeheer voor ondersteunde sjabloon-opdrachtregelparameters.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing luistert op de volgende netwerkadressen naar verbindingspogingen van andere apparaten:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing luistert op geen enkel adres naar verbindingspogingen van andere apparaten. Alleen uitgaande verbindingen vanaf dit apparaat zouden kunnen werken.",
"Syncthing is restarting.": "Syncthing wordt opnieuw gestart.",
"Syncthing is saving changes.": "Syncthing slaat de wijzigingen op.",
"Syncthing is upgrading.": "Syncthing is aan het bijwerken.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ondersteunt nu automatisch rapporteren van crashes naar de ontwikkelaars. De functie is standaard ingeschakeld.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt gestopt te zijn, of er is een probleem met uw internetverbinding. Opnieuw proberen…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Zewnętrzne polecenie odpowiedzialne jest za wersjonowanie. Musi ono usunąć plik ze współdzielonego folderu. Jeżeli ścieżka do aplikacji zawiera spacje, to powinna ona być zamknięta w cudzysłowie.",
"Anonymous Usage Reporting": "Anonimowe statystyki użycia",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Format anonimowych statystyk użycia uległ zmianie. Czy chcesz przejść na nowy format?",
"Applied to LAN": "Włączone w sieci lokalnej LAN",
"Apply": "Zastosuj",
"Are you sure you want to override all remote changes?": "Czy na pewno chcesz nadpisać wszystkie zmiany zdalne?",
"Are you sure you want to permanently delete all these files?": "Czy na pewno chcesz nieodwracalnie usunąć wszystkie te pliki?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Ścieżki używane wewnętrznie:",
"Introduced By": "Wprowadzony przez",
"Introducer": "Wprowadzający",
"Introduction": "Wprowadzenie",
"Inversion of the given condition (i.e. do not exclude)": "Odwrotność danego warunku (np. nie wykluczaj)",
"Keep Versions": "Zachowuj wersje",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Odrzuć",
"Revert Local Changes": "Odrzuć zmiany lokalne",
"Save": "Zapisz",
"Saving changes": "Zapisywanie zmian",
"Scan Time Remaining": "Pozostały czas skanowania",
"Scanning": "Skanowanie",
"See external versioning help for supported templated command line parameters.": "Zmienne dostępne dla polecenia opisane są w dokumentacji w sekcji Zewnętrzne wersjonowanie plików.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing nasłuchuje prób połączeń z innych urządzeń pod następującymi adresami sieciowymi:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing nie nasłuchuje prób połączeń z innych urządzeń pod żadnym adresem. Tylko połączenia wychodzące z tego urządzenia są w stanie działać.",
"Syncthing is restarting.": "Syncthing jest uruchamiany ponownie.",
"Syncthing is saving changes.": "Syncthing zapisuje zmiany.",
"Syncthing is upgrading.": "Syncthing jest aktualizowany.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing zawiera teraz automatyczne zgłaszanie awarii do autorów. Ta funkcja jest domyślnie włączona.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing wydaje się być wyłączony lub wystąpił problem z połączeniem internetowym. Próbuję ponownie…",
+3
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Um comando externo controla o controle de versão. Tem que remover o arquivo da pasta compartilhada. Se o caminho para o aplicativo contiver espaços, ele deve ser colocado entre aspas.",
"Anonymous Usage Reporting": "Relatórios anônimos de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anônimo de uso mudou. Gostaria de usar o formato novo?",
"Applied to LAN": "Aplicado à LAN",
"Apply": "Aplicar",
"Are you sure you want to override all remote changes?": "Tem a certeza que quer sobrepor todas as alterações remotas?",
"Are you sure you want to permanently delete all these files?": "Deseja realmente excluir todos estes arquivos permanentemente?",
@@ -324,6 +325,7 @@
"Revert": "Reverter",
"Revert Local Changes": "Reverter mudanças locais",
"Save": "Salvar",
"Saving changes": "Salvando alterações",
"Scan Time Remaining": "Tempo de verificação restante",
"Scanning": "Verificando",
"See external versioning help for supported templated command line parameters.": "Consulte a ajuda sobre versionamento externo para modelos de parâmetros de linha de comando aceitos.",
@@ -390,6 +392,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "O Syncthing está escutando nos seguintes endereços de rede para tentativas de conexão de outros dispositivos:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "O Syncthing não está ouvindo tentativas de conexão de outros dispositivos em qualquer endereço. Apenas conexões de saída deste dispositivo podem funcionar.",
"Syncthing is restarting.": "O Syncthing está sendo reiniciado.",
"Syncthing is saving changes.": "O Syncthing está salvando as alterações.",
"Syncthing is upgrading.": "O Syncthing está sendo atualizado.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "O Syncthing agora oferece suporte a relatórios automáticos de falhas para os desenvolvedores. Este recurso é habilitado por padrão.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Parece que o Syncthing está desligado ou há um problema com a sua conexão de internet. Tentando novamente...",
+11 -7
View File
@@ -27,9 +27,10 @@
"Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabética",
"Altered by ignoring deletes.": "Alterada por terem sido ignoradas as eliminações.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Um comando externo controla as versões. Esse comando tem que remover o ficheiro da pasta partilhada. Se o caminho para a aplicação contiver espaços, então terá de o escrever entre aspas.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Um comando externo gere as versões. Esse comando tem que remover o ficheiro da pasta partilhada. Se o caminho para a aplicação contiver espaços, então terá de o escrever entre aspas.",
"Anonymous Usage Reporting": "Enviar relatórios anónimos de utilização",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anónimo de utilização foi alterado. Gostaria de mudar para o novo formato?",
"Applied to LAN": "Aplicado à LAN",
"Apply": "Aplicar",
"Are you sure you want to override all remote changes?": "Tem a certeza que quer sobrepor todas as alterações remotas?",
"Are you sure you want to permanently delete all these files?": "Tem a certeza de que quer eliminar permanentemente todos estes ficheiros?",
@@ -55,7 +56,7 @@
"Cleaning Versions": "Limpando versões",
"Cleanup Interval": "Intervalo entre limpezas",
"Click to see full identification string and QR code.": "Clique para ver a identificação completa e o código QR.",
"Close": "Dispensar",
"Close": "Fechar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentário, quando usado no início de uma linha",
"Compression": "Compressão",
@@ -205,6 +206,7 @@
"Internally used paths:": "Caminhos usados internamente:",
"Introduced By": "Introduzido por",
"Introducer": "Apresentador",
"Introduction": "Apresentação",
"Inversion of the given condition (i.e. do not exclude)": "Inversão de uma dada condição (ou seja, não excluir)",
"Keep Versions": "Manter versões",
"LDAP": "LDAP",
@@ -324,9 +326,10 @@
"Revert": "Reverter",
"Revert Local Changes": "Reverter alterações locais",
"Save": "Gravar",
"Saving changes": "Guardando modificações",
"Scan Time Remaining": "Tempo restante da verificação",
"Scanning": "Verificação de alterações",
"See external versioning help for supported templated command line parameters.": "Veja a ajuda externa sobre gestão de versões para ver os modelos suportados de parâmetros para a linha de comandos.",
"See external versioning help for supported templated command line parameters.": "Veja a ajuda sobre a gestão de versões externa para ver os modelos suportados de parâmetros para a linha de comandos.",
"Select All": "Seleccionar tudo",
"Select a version": "Seleccione uma versão",
"Select additional devices to share this folder with.": "Seleccione outros dispositivos com os quais também pretende partilhar a pasta.",
@@ -368,7 +371,7 @@
"Stable releases and release candidates": "Versões estáveis e versões candidatas a lançamento",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Versões estáveis são adiadas por cerca de duas semanas. Durante esse período são submetidas a testes sob a forma de versões candidatas a lançamento.",
"Stable releases only": "Somente versões estáveis",
"Staggered": "Escalonado",
"Staggered": "Escalonada",
"Staggered File Versioning": "Escalonada",
"Start Browser": "Iniciar navegador",
"Statistics": "Estatísticas",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "O Syncthing está à escuta de tentativas de ligação por parte de outros dispositivos nos seguintes endereços de rede:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "O Syncthing não está à escuta de tentativas de ligação por parte de outros dispositivos em nenhum endereço. Apenas poderão funcionar ligações deste dispositivo para fora.",
"Syncthing is restarting.": "O Syncthing está a reiniciar.",
"Syncthing is saving changes.": "O Syncthing está guardando modificações.",
"Syncthing is upgrading.": "O Syncthing está a actualizar-se.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "O Syncthing agora suporta o envio automático de relatórios de estouro para os programadores. Esta funcionalidade vem inicialmente activada.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "O Syncthing parece estar em baixo, ou então existe um problema com a sua ligação à Internet. Tentando novamente…",
@@ -423,7 +427,7 @@
"The maximum age must be a number and cannot be blank.": "A idade máxima tem que ser um número e não pode estar vazia.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Tempo máximo, em dias, para manter uma versão (use 0 para manter a versão para sempre).",
"The number of days must be a number and cannot be blank.": "O número de dias tem que ser um número e não pode estar em branco.",
"The number of days to keep files in the trash can. Zero means forever.": "O número de dias a manter os ficheiros na reciclagem. Zero significa para sempre.",
"The number of days to keep files in the trash can. Zero means forever.": "O número de dias a manter os ficheiros no caixote do lixo. Zero significa para sempre.",
"The number of old versions to keep, per file.": "O número de versões antigas a manter, por ficheiro.",
"The number of versions must be a number and cannot be blank.": "O número de versões tem que ser um número e não pode estar vazio.",
"The path cannot be blank.": "O caminho não pode estar vazio.",
@@ -446,8 +450,8 @@
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Para se ligar ao dispositivo Syncthing com o nome \"{{devicename}}\", adicione um novo dispositivo remoto do seu lado com este ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Para permitir uma regra, marque a caixa. Para negar uma regra, deixe-a desmarcada.",
"Today": "Hoje",
"Trash Can": "Lixo",
"Trash Can File Versioning": "Reciclagem",
"Trash Can": "Caixote do lixo",
"Trash Can File Versioning": "Caixote do lixo",
"Type": "Tipo",
"UNIX Permissions": "Permissões UNIX",
"Unavailable": "Indisponível",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ett externt kommando hanterar versionen. Det måste ta bort filen från den delade mappen. Om sökvägen till applikationen innehåller mellanslag bör den citeras.",
"Anonymous Usage Reporting": "Anonym användarstatistiksrapportering",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymt användningsrapportformat har ändrats. Vill du flytta till det nya formatet?",
"Applied to LAN": "Applicerad på LAN",
"Apply": "Tillämpa",
"Are you sure you want to override all remote changes?": "Är du säker på att du vill åsidosätta alla fjärrändringar?",
"Are you sure you want to permanently delete all these files?": "Är du säker på att du vill ta bort alla dessa filer permanent?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Internt använda sökvägar:",
"Introduced By": "Introducerad av",
"Introducer": "Introduktör",
"Introduction": "Introduktion",
"Inversion of the given condition (i.e. do not exclude)": "Inversion av det givna tillståndet (d.v.s. exkluderar inte)",
"Keep Versions": "Behåll versioner",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Återgå",
"Revert Local Changes": "Återställ lokala ändringar",
"Save": "Spara",
"Saving changes": "Sparar ändringar",
"Scan Time Remaining": "Återstående skanningstid",
"Scanning": "Skannar",
"See external versioning help for supported templated command line parameters.": "Se hjälp för extern version för stödda mallade kommandoradsparametrar.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing lyssnar på följande nätverksadresser för anslutningsförsök från andra enheter:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lyssnar inte efter anslutningsförsök från andra enheter på någon adress. Endast utgående anslutningar från denna enhet kanske fungerar.",
"Syncthing is restarting.": "Syncthing startar om.",
"Syncthing is saving changes.": "Syncthing sparar ändringar.",
"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ängt, eller så finns det problem med din internetanslutning. Försöker igen…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Harici bir komut sürümlendirmeyi gerçekleştirir. Dosyayı paylaşılan klasörden kaldırmak zorundadır. Eğer uygulama yolu boşluklar içeriyorsa, tırnak içine alınmalıdır.",
"Anonymous Usage Reporting": "İsimsiz Kullanım Bildirme",
"Anonymous usage report format has changed. Would you like to move to the new format?": "İsimsiz kullanım raporu biçimi değişti. Yeni biçime geçmek ister misiniz?",
"Applied to LAN": "LAN'a uygulandı",
"Apply": "Uygula",
"Are you sure you want to override all remote changes?": "Tüm uzak değişiklikleri geçersiz kılmak istediğinize emin misiniz?",
"Are you sure you want to permanently delete all these files?": "Tüm bu dosyaları kalıcı olarak silmek istediğinize emin misiniz?",
@@ -205,6 +206,7 @@
"Internally used paths:": "Dahili olarak kullanılan yollar:",
"Introduced By": "Tanıtan",
"Introducer": "Tanıtıcı",
"Introduction": "Tanıtım",
"Inversion of the given condition (i.e. do not exclude)": "Verilen koşulun tersine çevrilmesi (yani hariç tutmama)",
"Keep Versions": "Sürümleri Tut",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "Geri al",
"Revert Local Changes": "Yerel Değişiklikleri Geri Döndür",
"Save": "Kaydet",
"Saving changes": "Değişiklikler kaydediliyor",
"Scan Time Remaining": "Kalan Tarama Süresi",
"Scanning": "Tarama",
"See external versioning help for supported templated command line parameters.": "Desteklenen şablonlu komut satırı parametreleri için harici sürümlendirme yardımına bakın.",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing, diğer cihazlardan gelen bağlantı girişimleri için aşağıdaki ağ adreslerini dinliyor:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing, herhangi bir adresteki diğer cihazlardan gelen bağlantı girişimlerini dinlemiyor. Bu cihazdan yalnızca giden bağlantılar çalışabilir.",
"Syncthing is restarting.": "Syncthing yeniden başlatılıyor.",
"Syncthing is saving changes.": "Syncthing değişiklikleri kaydediyor.",
"Syncthing is upgrading.": "Syncthing yükseltiliyor.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing artık çökmeleri geliştiricilere otomatik olarak bildirmeyi destekler. Bu özellik varsayılan olarak etkinleştirilmiştir.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing kapalı gibi görünüyor ya da İnternet bağlantınızda bir sorun var. Yeniden deneniyor…",
+4
View File
@@ -30,6 +30,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令接管了版本控制。该外部命令必须自行从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
"Anonymous Usage Reporting": "匿名使用报告",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情况的报告格式已经变更。是否要迁移到新的格式?",
"Applied to LAN": "已应用到局域网",
"Apply": "应用",
"Are you sure you want to override all remote changes?": "您确定要覆盖所有远程更改吗?",
"Are you sure you want to permanently delete all these files?": "确认要永久删除这些文件吗?",
@@ -205,6 +206,7 @@
"Internally used paths:": "内部使用的路径:",
"Introduced By": "介绍自",
"Introducer": "作为中介",
"Introduction": "介绍",
"Inversion of the given condition (i.e. do not exclude)": "反转本条件(即:不排除)",
"Keep Versions": "保留版本数量",
"LDAP": "LDAP",
@@ -324,6 +326,7 @@
"Revert": "还原",
"Revert Local Changes": "恢复本地更改",
"Save": "保存",
"Saving changes": "保存更改中",
"Scan Time Remaining": "扫描剩余时间",
"Scanning": "扫描中",
"See external versioning help for supported templated command line parameters.": "有关受支持的模板命令行参数,请参阅外部版本控制帮助。",
@@ -390,6 +393,7 @@
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing正在监听以下网络地址,以获取来自其他设备的连接尝试:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing 不会在任何地址上侦听来自其他设备的连接尝试。只有来自该设备的传出连接可能有效。",
"Syncthing is restarting.": "Syncthing 正在重启。",
"Syncthing is saving changes.": "Syncthing 正保存更改。",
"Syncthing is upgrading.": "Syncthing 正在升级。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 现在已经支持将崩溃报告自动发送给开发者。该功能默认开启。",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing 似乎关闭了,或者您的网络连接存在故障。重试中…",
+54 -45
View File
@@ -87,36 +87,45 @@
</button>
</li>
<li class="dropdown" language-select></li>
<li>
<a class="navbar-link" href="{{docsURL('intro/gui')}}" target="_blank">
<span class="fas fa-question-circle"></span>
<li class="dropdown action-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="fa fa-question-circle"></span>
<span class="hidden-xs" translate>Help</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a class="navbar-link" href="{{docsURL('intro/gui')}}" target="_blank"><span class="fa fa-fw fa-info-circle"></span>&nbsp;<span translate>Introduction</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a class="navbar-link" href="https://syncthing.net/" target="_blank"><span class="fa fa-fw fa-home"></span>&nbsp;<span translate>Home page</span></a></li>
<li><a class="navbar-link" href="{{docsURL()}}" target="_blank"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Documentation</span></a></li>
<li><a class="navbar-link" href="https://forum.syncthing.net" target="_blank"><span class="fa fa-fw fa-users"></span>&nbsp;<span translate>Support</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases" target="_blank"><span class="fa fa-fw fa-file-text"></span>&nbsp;<span translate>Changelog</span></a></li>
<li><a class="navbar-link" href="https://data.syncthing.net/" target="_blank"><span class="fa fa-fw fa-bar-chart"></span>&nbsp;<span translate>Statistics</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues" target="_blank"><span class="fa fa-fw fa-bug"></span>&nbsp;<span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing" target="_blank"><span class="fa fa-fw fa-file-code-o"></span>&nbsp;<span translate>Source Code</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="about.show()"><span class="fa fa-fw fa-heart"></span>&nbsp;<span translate>About</span></a></li>
</ul>
</li>
<li class="dropdown action-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="fas fa-cog"></span>
<span class="fa fa-cog"></span>
<span class="hidden-xs" translate>Actions</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="" ng-click="showSettings()"><span class="fas fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li><a href="" ng-click="showSettings()"><span class="fa fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fa fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="shutdown()"><span class="fas fa-fw fa-power-off"></span>&nbsp;<span translate>Shutdown</span></a></li>
<li><a href="" ng-click="restart()"><span class="fas fa-fw fa-refresh"></span>&nbsp;<span translate>Restart</span></a></li>
<li><a href="" ng-click="shutdown()"><span class="fa fa-fw fa-power-off"></span>&nbsp;<span translate>Shutdown</span></a></li>
<li><a href="" ng-click="restart()"><span class="fa fa-fw fa-refresh"></span>&nbsp;<span translate>Restart</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li class="visible-xs">
<a href="{{docsURL('intro/gui')}}" target="_blank">
<span class="fas fa-fw fa-question-circle"></span>&nbsp;<span translate>Help</span>
</a>
</li>
<li><a href="" ng-click="about.show()"><span class="far fa-fw fa-heart"></span>&nbsp;<span translate>About</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="advanced()"><span class="fas fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
<li><a href="" ng-click="logging.show()"><span class="far fa-fw fa-file-alt"></span>&nbsp;<span translate>Logs</span></a></li>
<li><a href="" ng-click="advanced()"><span class="fa fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
<li><a href="" ng-click="logging.show()"><span class="fa fa-fw fa-wrench"></span>&nbsp;<span translate>Logs</span></a></li>
<li class="divider" aria-hidden="true" ng-if="config.gui.debugging"></li>
<li><a href="/rest/debug/support" target="_blank" ng-if="config.gui.debugging"><span class="fa fa-user-md"></span>&nbsp;<span translate>Support Bundle</span></a></li>
<li><a href="/rest/debug/support" target="_blank" ng-if="config.gui.debugging"><span class="fa fa-fw fa-user-md"></span>&nbsp;<span translate>Support Bundle</span></a></li>
</ul>
</li>
</ul>
@@ -465,9 +474,10 @@
<a href="" ng-click="showLocalChanged(folder.id, folder.type)">{{model[folder.id].receiveOnlyTotalItems | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{model[folder.id].receiveOnlyChangedBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="folder.type != 'sendreceive'">
<tr>
<th><span class="fas fa-fw fa-folder"></span>&nbsp;<span translate>Folder Type</span></th>
<td class="text-right">
<span ng-if="folder.type == 'sendreceive'" translate>Send &amp; Receive</span>
<span ng-if="folder.type == 'sendonly'" translate>Send Only</span>
<span ng-if="folder.type == 'receiveonly'" translate>Receive Only</span>
<span ng-if="folder.type == 'receiveencrypted'" translate>Receive Encrypted</span>
@@ -512,7 +522,7 @@
</div>
</td>
</tr>
<tr ng-if="folder.order != 'random' && folder.type != 'sendonly'">
<tr ng-if="folder.type != 'sendonly'">
<th><span class="fas fa-fw fa-sort"></span>&nbsp;<span translate>File Pull Order</span></th>
<td class="text-right" ng-switch="folder.order">
<span ng-switch-when="random" translate>Random</span>
@@ -524,7 +534,7 @@
</td>
</tr>
<tr ng-if="folder.versioning.type">
<th><span class="fa fa-fw fa-file"></span>&nbsp;<span translate>File Versioning</span></th>
<th><span class="fa fa-fw fa-files-o"></span>&nbsp;<span translate>File Versioning</span></th>
<td class="text-right">
<span ng-switch="folder.versioning.type">
<span ng-switch-when="trashcan" translate>Trash Can</span>
@@ -533,21 +543,21 @@
<span ng-switch-when="external" tooltip data-original-title="<span class='text-monospace'>{{folder.versioning.params.command}}</span>" translate>External</span>
</span>
<span ng-if="folder.versioning.type != 'external'">
<span ng-if="(folder.versioning.type == 'trashcan' || folder.versioning.type == 'simple') && folder.versioning.params.cleanoutDays != versioningDefaults.trashcanClean" tooltip data-original-title="{{'Clean out after' | translate}}">
&ensp;<span class="fa fa-calendar"></span>&nbsp;{{folder.versioning.params.cleanoutDays * 86400 | duration:"d"}}
<span ng-if="(folder.versioning.type == 'trashcan' || folder.versioning.type == 'simple')" tooltip data-original-title="{{'Clean out after' | translate}}">
&ensp;<span class="fa fa-calendar"></span>&nbsp;<span ng-if="folder.versioning.params.cleanoutDays == 0" translate>Disabled</span><span ng-if="folder.versioning.params.cleanoutDays > 0">{{folder.versioning.params.cleanoutDays * 86400 | duration:"d"}}</span>
</span>
<span ng-if="folder.versioning.type == 'simple' && folder.versioning.params.keep != versioningDefaults.simpleKeep" tooltip data-original-title="{{'Keep Versions' | translate}}">
<span ng-if="folder.versioning.type == 'simple'" tooltip data-original-title="{{'Keep Versions' | translate}}">
&ensp;<span class="fa fa-file-archive-o"></span>&nbsp;{{folder.versioning.params.keep}}
</span>
<span ng-if="folder.versioning.type == 'staggered' && folder.versioning.params.maxAge / 86400 != versioningDefaults.staggeredMaxAge" tooltip data-original-title="{{'Maximum Age' | translate}}">
<span ng-if="folder.versioning.type == 'staggered'" tooltip data-original-title="{{'Maximum Age' | translate}}">
&ensp;<span class="fa fa-calendar"></span>&nbsp;<span ng-if="folder.versioning.params.maxAge == 0" translate>Forever</span><span ng-if="folder.versioning.params.maxAge > 0">{{folder.versioning.params.maxAge | duration}}</span>
</span>
<span ng-if="folder.versioning.cleanupIntervalS != versioningDefaults.cleanupIntervalS" tooltip data-original-title="{{'Cleanup Interval' | translate}}">
<span tooltip data-original-title="{{'Cleanup Interval' | translate}}">
&ensp;<span class="fa fa-recycle"></span>&nbsp;<span ng-if="folder.versioning.cleanupIntervalS == 0" translate>Disabled</span><span ng-if="folder.versioning.cleanupIntervalS > 0">{{folder.versioning.cleanupIntervalS | duration}}</span>
</span>
<!-- Keep the path last, so that it truncates without pushing other information out of the screen. -->
<span ng-if="folder.versioning.fsPath != ''" tooltip data-original-title="{{folder.versioning.fsPath}}">
&ensp;<span class="fa fa-folder-open-o"></span>&nbsp;{{folder.versioning.fsPath}}
<span tooltip data-original-title="{{folder.versioning.fsPath === '' ? '.stversions' : folder.versioning.fsPath}}">
&ensp;<span class="fa fa-folder-open-o"></span>&nbsp;{{folder.versioning.fsPath === '' ? '.stversions' : folder.versioning.fsPath}}
</span>
</span>
</td>
@@ -661,6 +671,9 @@
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{config.options.maxRecvKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{config.options.maxRecvKbps*1024*8 | metric}}bps</span>
<span ng-if="config.options.limitBandwidthInLan">
(<span translate>Applied to LAN</span>)
</span>
</i>
</small>
</a>
@@ -677,6 +690,9 @@
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{config.options.maxSendKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{config.options.maxSendKbps*1024*8 | metric}}bps</span>
<span ng-if="config.options.limitBandwidthInLan">
(<span translate>Applied to LAN</span>)
</span>
</i>
</small>
</a>
@@ -855,16 +871,24 @@
</span>
</td>
</tr>
<tr ng-if="connections[deviceCfg.deviceID].connected">
<th><span class="fas fa-fw fa-random"></span>&nbsp;<span translate>Number of Connections</span></th>
<td class="text-right">
<span ng-if="connections[deviceCfg.deviceID].secondary.length">1 + {{connections[deviceCfg.deviceID].secondary.length | alwaysNumber}}</span>
<span ng-if="!connections[deviceCfg.deviceID].secondary.length">1</span>
</td>
</tr>
<tr ng-if="deviceCfg.allowedNetworks.length > 0">
<th><span class="fas fa-fw fa-filter"></span>&nbsp;<span translate>Allowed Networks</span></th>
<td class="text-right">
<span>{{deviceCfg.allowedNetworks.join(", ")}}</span>
</td>
</tr>
<tr ng-if="deviceCfg.compression != 'metadata'">
<tr>
<th><span class="fas fa-fw fa-compress"></span>&nbsp;<span translate>Compression</span></th>
<td class="text-right">
<span ng-if="deviceCfg.compression == 'always'" translate>All Data</span>
<span ng-if="deviceCfg.compression == 'metadata'" translate>Metadata Only</span>
<span ng-if="deviceCfg.compression == 'never'" translate>Off</span>
</td>
</tr>
@@ -951,27 +975,12 @@
</div> <!-- /container -->
</div> <!-- /ng-cloak -->
<!-- Bottom bar -->
<nav class="navbar navbar-default navbar-fixed-bottom">
<div class="container">
<ul class="nav navbar-nav">
<li><a class="navbar-link" href="https://syncthing.net/" target="_blank"><span class="fas fa-home"></span>&nbsp;<span translate>Home page</span></a></li>
<li><a class="navbar-link" href="{{docsURL()}}" target="_blank"><span class="fas fa-book"></span>&nbsp;<span translate>Documentation</span></a></li>
<li><a class="navbar-link" href="https://forum.syncthing.net" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Support</span></a></li>
<li><a class="navbar-link" href="https://data.syncthing.net/" target="_blank"><span class="fas fa-bar-chart"></span>&nbsp;<span translate>Statistics</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases" target="_blank"><span class="far fa-file-alt"></span>&nbsp;<span translate>Changelog</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues" target="_blank"><span class="fas fa-bug"></span>&nbsp;<span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing" target="_blank"><span class="fas fa-wrench"></span>&nbsp;<span translate>Source Code</span></a></li>
</ul>
</div>
</nav>
<ng-include src="'syncthing/core/networkErrorDialogView.html'"></ng-include>
<ng-include src="'syncthing/core/httpErrorDialogView.html'"></ng-include>
<ng-include src="'syncthing/core/restartingDialogView.html'"></ng-include>
<ng-include src="'syncthing/core/upgradingDialogView.html'"></ng-include>
<ng-include src="'syncthing/core/shutdownDialogView.html'"></ng-include>
<ng-include src="'syncthing/core/savingChangesDialogView.html'"></ng-include>
<ng-include src="'syncthing/device/idqrModalView.html'"></ng-include>
<ng-include src="'syncthing/device/editDeviceModalView.html'"></ng-include>
<ng-include src="'syncthing/device/globalChangesModalView.html'"></ng-include>
@@ -26,7 +26,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Emil Lundberg, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, guangwu, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Emil Lundberg, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, guangwu, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
</div>
</div>
</div>
@@ -1,4 +1,4 @@
<modal id="logViewer" status="default" icon="far fa-file-alt" heading="{{'Logs' | translate}}" large="yes" closeable="yes">
<modal id="logViewer" status="default" icon="fa fa-wrench" heading="{{'Logs' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#log-viewer-log" translate>Log</a></li>
@@ -0,0 +1,8 @@
<modal id="savingChanges" status="info" icon="fas fa-hourglass-half" heading="{{'Saving changes' | translate}}" large="no" closeable="no">
<div class="modal-body">
<p>
<span translate>Syncthing is saving changes.</span>
<span translate>Please wait</span>...
</p>
</div>
</modal>
@@ -1090,7 +1090,7 @@ angular.module('syncthing.core')
}
// Disconnected
if (!unused && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays >= 7) {
if (!unused && $scope.deviceStats[deviceCfg.deviceID] && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays >= 7) {
return status + 'disconnected-inactive';
} else {
return status + 'disconnected';
@@ -1513,13 +1513,17 @@ angular.module('syncthing.core')
};
$scope.saveConfig = function () {
$('#savingChanges').modal();
var cfg = JSON.stringify($scope.config);
var opts = {
headers: {
'Content-Type': 'application/json'
}
};
return $http.put(urlbase + '/config', cfg, opts).finally(refreshConfig).catch($scope.emitHTTPError);
return $http.put(urlbase + '/config', cfg, opts).finally(function () {
refreshConfig();
$('#savingChanges').modal("hide");
}).catch($scope.emitHTTPError);
};
$scope.urVersions = function () {
@@ -12,7 +12,7 @@
<label translate for="deviceID">Device ID</label>
<div class="input-group">
<input ng-if="editingDeviceNew()" name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
<div ng-if="!editingDeviceNew()" class="well well-sm form-control text-monospace" style="height: auto;" select-on-click>{{currentDevice.deviceID}}</div>
<div ng-if="!editingDeviceNew()" class="well well-sm form-control text-monospace select-on-click" style="height: auto;">{{currentDevice.deviceID}}</div>
<div id="shareDeviceIdButtons" class="input-group-btn">
<button data-container="body" type="button" class="btn btn-default" ng-click="copyToClipboard($event, currentDevice.deviceID)" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Copy' | translate }}">
<span class="fa fa-lg fa-clone"></span>
@@ -137,11 +137,25 @@
</div>
</div>
</div>
<div class="row form-group">
<div class="col-md-12">
<div class="row">
<div class="col-md-6" ng-class="{'has-error': deviceEditor.numConnections.$invalid && deviceEditor.numConnections.$dirty}">
<label translate>Connection Management</label>
<div class="row">
<span class="col-md-8">
<span translate>Number of Connections</span>
&nbsp;<a href="{{docsURL('advanced/device-numconnections')}}" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Help</span></a>
</span>
<div class="col-md-4">
<input name="numConnections" id="numConnections" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.numConnections" min="0" />
</div>
</div>
<p class="help-block" ng-if="!deviceEditor.numConnections.$valid && deviceEditor.numConnections.$dirty" translate>The number of connections must be a non-negative number.</p>
<p class="help-block" ng-if="deviceEditor.numConnections.$valid || deviceEditor.numConnections.$pristine" translate>When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.</p>
</div>
<div class="col-md-6 form-group">
<label translate>Device rate limits</label>
<div class="row">
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxRecvKbps.$invalid && deviceEditor.maxRecvKbps.$dirty}">
<div class="col-md-12" ng-class="{'has-error': deviceEditor.maxRecvKbps.$invalid && deviceEditor.maxRecvKbps.$dirty}">
<div class="row">
<span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span>
<div class="col-md-4">
@@ -150,7 +164,7 @@
</div>
<p class="help-block" ng-if="!deviceEditor.maxRecvKbps.$valid && deviceEditor.maxRecvKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
</div>
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
<div class="col-md-12" ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
<div class="row">
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
<div class="col-md-4">
@@ -158,6 +172,7 @@
</div>
</div>
<p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
<p class="help-block" ng-if="(deviceEditor.maxSendKbps.$valid || deviceEditor.maxSendKbps.$pristine) && (deviceEditor.maxRecvKbps.$valid || deviceEditor.maxRecvKbps.$pristine)" translate>The rate limit is applied to the accumulated traffic of all connections to this device.</p>
</div>
</div>
</div>
@@ -1,4 +1,3 @@
<style> th, td { padding: 6px; } </style>
<modal id="globalChanges" status="default" icon="fas fa-info-circle" heading="{{'Recent Changes' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<div class="table-responsive">
@@ -1,6 +1,6 @@
<modal id="idqr" status="info" icon="fas fa-qrcode" heading="{{'Device Identification' | translate}} - {{deviceName(currentDevice)}}" large="yes" closeable="yes">
<div class="modal-body text-center">
<div class="well well-sm text-monospace" select-on-click>{{currentDevice.deviceID}}</div>
<div class="well well-sm text-monospace select-on-click">{{currentDevice.deviceID}}</div>
<div ng-if="currentDevice.deviceID">
<img class="img-thumbnail" ng-src="qr/?text={{currentDevice.deviceID}}" height="328" width="328" alt="{{'QR code' | translate}}" />
<div class="btn-group-vertical" style="vertical-align: top;">
@@ -4,7 +4,7 @@
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(folderEditor)">
<li ng-class="{'disabled': currentFolder._editing == 'new-ignores'}" class="active"><a data-toggle="tab" href="{{currentFolder._editing == 'new-ignores' ? '' : '#folder-general'}}"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
<li ng-class="{'disabled': currentFolder._editing == 'new-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'new-ignores' ? '' : '#folder-sharing'}}"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
<li ng-class="{'disabled': currentFolder._editing == 'new-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'new-ignores' ? '' : '#folder-versioning'}}"><span class="fas fa-copy"></span> <span translate>File Versioning</span></a></li>
<li ng-class="{'disabled': currentFolder._editing == 'new-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'new-ignores' ? '' : '#folder-versioning'}}"><span class="fa fa-files-o"></span> <span translate>File Versioning</span></a></li>
<li ng-class="{'disabled': currentFolder._recvEnc}"><a data-toggle="tab" href="{{currentFolder._recvEnc ? '' : '#folder-ignores'}}"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
<li ng-class="{'disabled': currentFolder._editing == 'new-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'new-ignores' ? '' : '#folder-advanced'}}"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
</ul>
+9 -1
View File
@@ -1230,6 +1230,14 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
files = append(files, fileEntry{name: "metrics.txt", data: buf.Bytes()})
// Connection data as JSON
connStats := s.model.ConnectionStats()
if connStatsJSON, err := json.MarshalIndent(connStats, "", " "); err != nil {
l.Warnln("Support bundle: failed to serialize connection-stats.json.txt", err)
} else {
files = append(files, fileEntry{name: "connection-stats.json.txt", data: connStatsJSON})
}
// Heap and CPU Proofs as a pprof extension
var heapBuffer, cpuBuffer bytes.Buffer
filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
@@ -1607,7 +1615,7 @@ func (s *service) getPeerCompletion(w http.ResponseWriter, _ *http.Request) {
for _, folder := range s.cfg.Folders() {
for _, device := range folder.DeviceIDs() {
deviceStr := device.String()
if _, ok := s.model.Connection(device); ok {
if s.model.ConnectedTo(device) {
comp, err := s.model.Completion(device, folder.ID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
+6 -4
View File
@@ -46,8 +46,9 @@ func writeBroadcasts(ctx context.Context, inbox <-chan []byte, port int) error {
intfs, err := net.Interfaces()
if err != nil {
l.Debugln(err)
return err
l.Debugln("Failed to list interfaces:", err)
// net.Interfaces() is broken on Android. see https://github.com/golang/go/issues/40569
// Use the general broadcast address 255.255.255.255 instead.
}
var dsts []net.IP
@@ -58,8 +59,9 @@ func writeBroadcasts(ctx context.Context, inbox <-chan []byte, port int) error {
addrs, err := intf.Addrs()
if err != nil {
l.Debugln(err)
return err
l.Debugln("Failed to list interface addresses:", err)
// Interface discovery might work while retrieving the addresses doesn't. So log the error and carry on.
continue
}
for _, addr := range addrs {
+2 -3
View File
@@ -18,6 +18,8 @@ import (
"time"
)
const Codename = "Gold Grasshopper"
var (
// Injected by build script
Version = "unknown-dev"
@@ -26,9 +28,6 @@ var (
Stamp = "0"
Tags = ""
// Static
Codename = "Fermium Flea"
// Set by init()
Date time.Time
IsRelease bool
+3 -5
View File
@@ -25,6 +25,7 @@ import (
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/netutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sliceutil"
"github.com/syncthing/syncthing/lib/structutil"
)
@@ -564,8 +565,7 @@ func ensureNoUntrustedTrustingSharing(f *FolderConfiguration, devices []FolderDe
}
if devCfg := existingDevices[dev.DeviceID]; devCfg.Untrusted {
l.Warnf("Folder %s (%s) is shared in trusted mode with untrusted device %s (%s); unsharing.", f.ID, f.Label, dev.DeviceID.Short(), devCfg.Name)
copy(devices[i:], devices[i+1:])
devices = devices[:len(devices)-1]
devices = sliceutil.RemoveAndZero(devices, i)
i--
}
}
@@ -601,9 +601,7 @@ func filterURLSchemePrefix(addrs []string, prefix string) []string {
continue
}
if strings.HasPrefix(uri.Scheme, prefix) {
// Remove this entry
copy(addrs[i:], addrs[i+1:])
addrs = addrs[:len(addrs)-1]
addrs = sliceutil.RemoveAndZero(addrs, i)
i--
}
}
+13
View File
@@ -11,6 +11,8 @@ import (
"sort"
)
const defaultNumConnections = 1 // number of connections to use by default; may change in the future.
func (cfg DeviceConfiguration) Copy() DeviceConfiguration {
c := cfg
c.Addresses = make([]string, len(cfg.Addresses))
@@ -49,6 +51,17 @@ func (cfg *DeviceConfiguration) prepare(sharedFolders []string) {
}
}
func (cfg *DeviceConfiguration) NumConnections() int {
switch {
case cfg.RawNumConnections == 0:
return defaultNumConnections
case cfg.RawNumConnections < 0:
return 1
default:
return cfg.RawNumConnections
}
}
func (cfg *DeviceConfiguration) IgnoredFolder(folder string) bool {
for _, ignoredFolder := range cfg.IgnoredFolders {
if ignoredFolder.ID == folder {
+99 -67
View File
@@ -28,7 +28,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type DeviceConfiguration struct {
DeviceID github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"deviceID" xml:"id,attr" nodefault:"true"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name" xml:"name,attr,omitempty"`
Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses" xml:"address,omitempty" default:"dynamic"`
Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses" xml:"address,omitempty"`
Compression protocol.Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression" xml:"compression,attr"`
CertName string `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"certName" xml:"certName,attr,omitempty"`
Introducer bool `protobuf:"varint,6,opt,name=introducer,proto3" json:"introducer" xml:"introducer,attr"`
@@ -44,6 +44,7 @@ type DeviceConfiguration struct {
MaxRequestKiB int `protobuf:"varint,16,opt,name=max_request_kib,json=maxRequestKib,proto3,casttype=int" json:"maxRequestKiB" xml:"maxRequestKiB"`
Untrusted bool `protobuf:"varint,17,opt,name=untrusted,proto3" json:"untrusted" xml:"untrusted"`
RemoteGUIPort int `protobuf:"varint,18,opt,name=remote_gui_port,json=remoteGuiPort,proto3,casttype=int" json:"remoteGUIPort" xml:"remoteGUIPort"`
RawNumConnections int `protobuf:"varint,19,opt,name=num_connections,json=numConnections,proto3,casttype=int" json:"numConnections" xml:"numConnections"`
}
func (m *DeviceConfiguration) Reset() { *m = DeviceConfiguration{} }
@@ -88,72 +89,74 @@ func init() {
}
var fileDescriptor_744b782bd13071dd = []byte{
// 1026 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbf, 0x6f, 0xdb, 0x46,
0x18, 0x15, 0xeb, 0xc4, 0xb6, 0xce, 0x3f, 0x64, 0xd3, 0x88, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35,
0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xa3, 0x2d, 0x50, 0xc6, 0x68, 0x63, 0x18, 0x4d, 0x5c, 0x16,
0x5d, 0xbc, 0xb0, 0x24, 0xef, 0xac, 0x1c, 0x2c, 0xf2, 0x58, 0xf2, 0xa8, 0x58, 0x40, 0xff, 0x80,
0x76, 0x2b, 0x02, 0x74, 0xea, 0x92, 0xf6, 0xdf, 0xe8, 0xd0, 0xd5, 0x9b, 0x35, 0x16, 0x1d, 0x0e,
0x88, 0xbd, 0x71, 0x29, 0xc0, 0x31, 0x53, 0x71, 0x77, 0x14, 0x45, 0xca, 0x51, 0x50, 0xa0, 0x1b,
0xef, 0xbd, 0x77, 0xef, 0xdd, 0xf7, 0xe9, 0xbb, 0x13, 0xe8, 0x0c, 0x88, 0xbb, 0xeb, 0xd1, 0xe0,
0x94, 0xf4, 0x77, 0x11, 0x1e, 0x12, 0x0f, 0xab, 0x45, 0x12, 0x39, 0x8c, 0xd0, 0xa0, 0x17, 0x46,
0x94, 0x51, 0x7d, 0x51, 0x81, 0x3b, 0xdb, 0x42, 0x2d, 0x21, 0x8f, 0x0e, 0x76, 0x5d, 0x1c, 0x2a,
0x7e, 0xe7, 0x5e, 0xc9, 0x85, 0xba, 0x31, 0x8e, 0x86, 0x18, 0xe5, 0x54, 0x1d, 0x9f, 0x33, 0xf5,
0xd9, 0xfe, 0x67, 0x03, 0x6c, 0x1d, 0xc8, 0x8c, 0xc7, 0xe5, 0x0c, 0xfd, 0x4f, 0x0d, 0xd4, 0x55,
0xb6, 0x4d, 0x90, 0xa1, 0xb5, 0xb4, 0xee, 0xaa, 0xf9, 0x9b, 0x76, 0xc1, 0x61, 0xed, 0x6f, 0x0e,
0x3f, 0xee, 0x13, 0xf6, 0x3c, 0x71, 0x7b, 0x1e, 0xf5, 0x77, 0xe3, 0x51, 0xe0, 0xb1, 0xe7, 0x24,
0xe8, 0x97, 0xbe, 0xca, 0x27, 0xea, 0x29, 0xf7, 0xc3, 0x83, 0x2b, 0x0e, 0x97, 0x27, 0xdf, 0x29,
0x87, 0xcb, 0x28, 0xff, 0xce, 0x38, 0x6c, 0x9e, 0xfb, 0x83, 0xfd, 0x36, 0x41, 0x0f, 0x1d, 0xc6,
0xa2, 0x76, 0x2b, 0xa0, 0x08, 0x9f, 0x3a, 0xc9, 0x80, 0xed, 0xb7, 0x59, 0x94, 0xe0, 0x76, 0x7a,
0xd9, 0x59, 0xca, 0xc9, 0xec, 0xb2, 0x53, 0x6c, 0xfc, 0x71, 0xdc, 0xd1, 0x5e, 0x8e, 0x3b, 0x85,
0xe9, 0xab, 0x71, 0x47, 0xb3, 0x26, 0x2c, 0xd2, 0x8f, 0xc1, 0xad, 0xc0, 0xf1, 0xb1, 0xf1, 0x5e,
0x4b, 0xeb, 0xd6, 0xcd, 0x4f, 0x52, 0x0e, 0xe5, 0x3a, 0xe3, 0xf0, 0x9e, 0x8c, 0x13, 0x0b, 0xe9,
0xf9, 0x90, 0xfa, 0x84, 0x61, 0x3f, 0x64, 0x23, 0x91, 0xb4, 0xf5, 0x16, 0xdc, 0x92, 0x3b, 0xf5,
0x73, 0x50, 0x77, 0x10, 0x8a, 0x70, 0x1c, 0xe3, 0xd8, 0x58, 0x68, 0x2d, 0x74, 0xeb, 0xe6, 0x49,
0xca, 0xe1, 0x14, 0xcc, 0x38, 0x7c, 0x20, 0xbd, 0x73, 0xa4, 0xe4, 0xdc, 0x2a, 0x4a, 0x42, 0xa3,
0xc0, 0xf1, 0x89, 0x27, 0xb2, 0x36, 0x6f, 0xe8, 0xde, 0x5c, 0x76, 0x96, 0x72, 0x81, 0x35, 0xf5,
0xd5, 0x87, 0x60, 0xc5, 0xa3, 0x7e, 0x28, 0x56, 0x84, 0x06, 0xc6, 0xad, 0x96, 0xd6, 0x5d, 0xdf,
0xbb, 0xd3, 0x2b, 0x7a, 0xfc, 0x78, 0x4a, 0x9a, 0x9f, 0xa6, 0x1c, 0x96, 0xd5, 0x19, 0x87, 0xdb,
0xf2, 0x50, 0x25, 0x4c, 0x35, 0x3a, 0xbd, 0xec, 0x6c, 0xcc, 0x82, 0x56, 0x79, 0xab, 0x8e, 0x41,
0xdd, 0xc3, 0x11, 0xb3, 0x65, 0x23, 0x6f, 0xcb, 0x46, 0x3e, 0x11, 0xbf, 0x9d, 0x00, 0x9f, 0xaa,
0x66, 0xde, 0x57, 0xde, 0x39, 0xf0, 0x96, 0x86, 0xde, 0x9d, 0xc3, 0x59, 0x85, 0x8b, 0x7e, 0x02,
0x00, 0x09, 0x58, 0x44, 0x51, 0xe2, 0xe1, 0xc8, 0x58, 0x6c, 0x69, 0xdd, 0x65, 0x73, 0x3f, 0xe5,
0xb0, 0x84, 0x66, 0x1c, 0xde, 0x51, 0x53, 0x52, 0x40, 0x45, 0x11, 0x8d, 0x19, 0xcc, 0x2a, 0xed,
0xd3, 0x7f, 0xd7, 0xc0, 0x4e, 0x7c, 0x46, 0x42, 0x7b, 0x82, 0x89, 0xf1, 0xb6, 0x23, 0xec, 0xd3,
0xa1, 0x33, 0x88, 0x8d, 0x25, 0x19, 0x86, 0x52, 0x0e, 0x0d, 0xa1, 0x3a, 0x2c, 0x89, 0xac, 0x5c,
0x93, 0x71, 0xf8, 0xbe, 0x8c, 0x9e, 0x27, 0x28, 0x0e, 0x72, 0xff, 0x9d, 0x0a, 0x6b, 0x6e, 0x82,
0xfe, 0x87, 0x06, 0xd6, 0x8a, 0x33, 0x23, 0xdb, 0x1d, 0x19, 0xcb, 0xf2, 0xc6, 0xfd, 0xf2, 0xbf,
0x6e, 0x5c, 0xca, 0xe1, 0xea, 0xd4, 0xd5, 0x1c, 0x65, 0x1c, 0x76, 0xab, 0x3d, 0x44, 0xe6, 0x68,
0xfe, 0x9d, 0xdb, 0xbc, 0x21, 0x13, 0x37, 0x4e, 0xde, 0xb2, 0x8a, 0xad, 0xbe, 0x07, 0x16, 0x43,
0x27, 0x89, 0x31, 0x32, 0xea, 0xb2, 0x9b, 0x3b, 0x29, 0x87, 0x39, 0x92, 0x71, 0xb8, 0x2a, 0x23,
0xd5, 0xb2, 0x6d, 0xe5, 0xb8, 0xfe, 0x03, 0xd8, 0x70, 0x06, 0x03, 0xfa, 0x02, 0x23, 0x3b, 0xc0,
0xec, 0x05, 0x8d, 0xce, 0x62, 0x03, 0xc8, 0x2b, 0xf5, 0x75, 0xca, 0x61, 0x23, 0xe7, 0x9e, 0xe6,
0x54, 0xf1, 0x46, 0x54, 0xf1, 0xea, 0xa0, 0x19, 0xf3, 0x48, 0x6b, 0xd6, 0x4e, 0xff, 0x0e, 0x6c,
0x39, 0x09, 0xa3, 0xb6, 0xe3, 0x79, 0x38, 0x64, 0xf6, 0x29, 0x1d, 0x20, 0x1c, 0xc5, 0xc6, 0x8a,
0x3c, 0xfe, 0x87, 0x29, 0x87, 0x9b, 0x82, 0xfe, 0x5c, 0xb2, 0x5f, 0x28, 0x32, 0xe3, 0xf0, 0xae,
0x3a, 0xc2, 0x2c, 0xd3, 0xb6, 0x6e, 0xaa, 0xf5, 0x67, 0x60, 0xcd, 0x77, 0xce, 0xed, 0x18, 0x07,
0xc8, 0x3e, 0x73, 0xc3, 0xd8, 0x58, 0x6d, 0x69, 0xdd, 0xdb, 0xe6, 0x07, 0xe2, 0x72, 0xfa, 0xce,
0xf9, 0x37, 0x38, 0x40, 0x47, 0x6e, 0x28, 0x5c, 0x37, 0xa5, 0x6b, 0x09, 0x6b, 0xbf, 0xe1, 0x70,
0x81, 0x04, 0xcc, 0x2a, 0x0b, 0x27, 0x86, 0x11, 0xf6, 0x86, 0xca, 0x70, 0xad, 0x62, 0x68, 0x61,
0x6f, 0x38, 0x6b, 0x38, 0xc1, 0x2a, 0x86, 0x13, 0x50, 0x0f, 0x40, 0x83, 0xf4, 0x03, 0x1a, 0x61,
0x54, 0xd4, 0xbf, 0xde, 0x5a, 0xe8, 0xae, 0xec, 0x6d, 0xf7, 0xd4, 0xbf, 0x46, 0xef, 0x59, 0xfe,
0xaf, 0xa1, 0x6a, 0x32, 0x1f, 0x89, 0x59, 0x4c, 0x39, 0x5c, 0xcf, 0xb7, 0x4d, 0x1b, 0xb3, 0xa5,
0xa6, 0xaa, 0x0c, 0xb7, 0xad, 0x19, 0x99, 0xfe, 0x93, 0x06, 0x1a, 0x21, 0x0e, 0x10, 0x09, 0xfa,
0x45, 0x60, 0xe3, 0x9d, 0x81, 0x4f, 0x44, 0xe0, 0x15, 0x87, 0xc6, 0x01, 0x0e, 0x23, 0xec, 0x39,
0x0c, 0xa3, 0x63, 0x65, 0x90, 0x7b, 0xa6, 0x1c, 0x6a, 0x8f, 0x8a, 0x37, 0x28, 0x2c, 0x73, 0xa5,
0xd1, 0x30, 0x34, 0x6b, 0xbd, 0xc2, 0xc5, 0xfa, 0xaf, 0x1a, 0x68, 0xa8, 0x6e, 0x7e, 0x9f, 0xe0,
0x98, 0xd9, 0x67, 0xc4, 0x35, 0x36, 0x64, 0x3f, 0xe3, 0x2b, 0x0e, 0xd7, 0xbe, 0x12, 0x6d, 0x92,
0xcc, 0x11, 0x31, 0x53, 0x0e, 0xd7, 0xfc, 0x32, 0x50, 0x14, 0x5c, 0x41, 0x27, 0x4d, 0x4e, 0x2f,
0x3b, 0x33, 0xf2, 0x59, 0xe0, 0xe5, 0xb8, 0x53, 0x4d, 0xb0, 0x2a, 0xbc, 0xab, 0x7f, 0x06, 0xea,
0x49, 0xc0, 0xa2, 0x24, 0x66, 0x18, 0x19, 0x9b, 0x72, 0x26, 0x5b, 0xe2, 0x7f, 0xa6, 0x00, 0x33,
0x0e, 0x1b, 0xf2, 0x04, 0x05, 0xd2, 0xb6, 0xa6, 0xac, 0xac, 0x4e, 0x3c, 0x70, 0x0c, 0xdb, 0xfd,
0x84, 0xd8, 0x21, 0x8d, 0x98, 0xa1, 0x4f, 0xab, 0xb3, 0x24, 0xf5, 0xe5, 0xb7, 0x87, 0xc7, 0x34,
0x62, 0xa2, 0xba, 0xa8, 0x0c, 0x14, 0xd5, 0x55, 0xd0, 0x72, 0x75, 0x55, 0xf9, 0x2c, 0x20, 0xaa,
0xab, 0x24, 0x58, 0x13, 0x3e, 0x21, 0x62, 0x69, 0x1e, 0x5d, 0xbc, 0x6e, 0xd6, 0xc6, 0xaf, 0x9b,
0xb5, 0x8b, 0xab, 0xa6, 0x36, 0xbe, 0x6a, 0x6a, 0x3f, 0x5f, 0x37, 0x6b, 0xaf, 0xae, 0x9b, 0xda,
0xf8, 0xba, 0x59, 0xfb, 0xeb, 0xba, 0x59, 0x3b, 0x79, 0xf0, 0x1f, 0x1e, 0x3b, 0x35, 0x31, 0xee,
0xa2, 0x7c, 0xf4, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x4a, 0x4f, 0x60, 0x33, 0x09,
0x00, 0x00,
// 1057 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x41, 0x6f, 0xe3, 0x44,
0x14, 0x8e, 0xe9, 0x6e, 0xb7, 0x99, 0x6d, 0x9b, 0xc6, 0x65, 0xbb, 0xde, 0x4a, 0x9b, 0x89, 0x42,
0x0e, 0x41, 0xec, 0xa6, 0xa8, 0x70, 0xaa, 0x00, 0x89, 0xb4, 0x82, 0xad, 0x2a, 0xba, 0x65, 0x10,
0x97, 0xdd, 0x83, 0x71, 0x3c, 0xd3, 0xac, 0xd5, 0x78, 0xc6, 0xd8, 0xe3, 0xb4, 0x95, 0x38, 0x72,
0x80, 0x1b, 0xaa, 0xc4, 0x89, 0xcb, 0xc2, 0xdf, 0xe0, 0xc0, 0xb5, 0xb7, 0xe6, 0x08, 0x1c, 0x46,
0xda, 0xf4, 0xe6, 0xa3, 0x8f, 0x9c, 0xd0, 0x8c, 0x1d, 0xc7, 0x76, 0x37, 0x2b, 0x24, 0x6e, 0x9e,
0xef, 0x7b, 0xf3, 0x7d, 0xf3, 0x9e, 0xdf, 0x9b, 0x01, 0xed, 0xa1, 0xd3, 0xdf, 0xb2, 0x19, 0x3d,
0x76, 0x06, 0x5b, 0x98, 0x8c, 0x1c, 0x9b, 0x24, 0x8b, 0xd0, 0xb7, 0xb8, 0xc3, 0x68, 0xd7, 0xf3,
0x19, 0x67, 0xfa, 0x62, 0x02, 0x6e, 0x6e, 0xc8, 0x68, 0x05, 0xd9, 0x6c, 0xb8, 0xd5, 0x27, 0x5e,
0xc2, 0x6f, 0x3e, 0xc8, 0xa9, 0xb0, 0x7e, 0x40, 0xfc, 0x11, 0xc1, 0x29, 0x55, 0x25, 0x67, 0x3c,
0xf9, 0x6c, 0xfd, 0x55, 0x07, 0xeb, 0x7b, 0xca, 0x63, 0x37, 0xef, 0xa1, 0xff, 0xa1, 0x81, 0x6a,
0xe2, 0x6d, 0x3a, 0xd8, 0xd0, 0x9a, 0x5a, 0x67, 0xb9, 0xf7, 0xab, 0x76, 0x29, 0x60, 0xe5, 0x6f,
0x01, 0x3f, 0x1c, 0x38, 0xfc, 0x45, 0xd8, 0xef, 0xda, 0xcc, 0xdd, 0x0a, 0xce, 0xa9, 0xcd, 0x5f,
0x38, 0x74, 0x90, 0xfb, 0xca, 0x9f, 0xa8, 0x9b, 0xa8, 0xef, 0xef, 0x4d, 0x04, 0x5c, 0x9a, 0x7e,
0x47, 0x02, 0x2e, 0xe1, 0xf4, 0x3b, 0x16, 0xb0, 0x71, 0xe6, 0x0e, 0x77, 0x5a, 0x0e, 0x7e, 0x64,
0x71, 0xee, 0xb7, 0x9a, 0x94, 0x61, 0x72, 0x6c, 0x85, 0x43, 0xbe, 0xd3, 0xe2, 0x7e, 0x48, 0x5a,
0xd1, 0x55, 0xfb, 0x4e, 0x4a, 0xc6, 0x57, 0xed, 0x6c, 0xe3, 0x0f, 0xe3, 0xb6, 0x76, 0x31, 0x6e,
0x67, 0xa2, 0x2f, 0xc7, 0x6d, 0x0d, 0x4d, 0x59, 0xac, 0x1f, 0x81, 0x5b, 0xd4, 0x72, 0x89, 0xf1,
0x56, 0x53, 0xeb, 0x54, 0x7b, 0x1f, 0x45, 0x02, 0xaa, 0x75, 0x2c, 0xe0, 0x03, 0x65, 0x27, 0x17,
0x4a, 0xf3, 0x11, 0x73, 0x1d, 0x4e, 0x5c, 0x8f, 0x9f, 0x4b, 0xa7, 0xf5, 0xd7, 0xe0, 0x48, 0xed,
0xd4, 0x9f, 0x83, 0xaa, 0x85, 0xb1, 0x4f, 0x82, 0x80, 0x04, 0xc6, 0x42, 0x73, 0xa1, 0x53, 0xed,
0x7d, 0x1c, 0x09, 0x38, 0x03, 0x63, 0x01, 0xef, 0x2b, 0xed, 0x14, 0x29, 0x2a, 0xd7, 0x6f, 0xa0,
0x68, 0xb6, 0x55, 0x1f, 0x81, 0xbb, 0x36, 0x73, 0x3d, 0xb9, 0x72, 0x18, 0x35, 0x6e, 0x35, 0xb5,
0xce, 0xea, 0xf6, 0xbd, 0x6e, 0x56, 0xc6, 0xdd, 0x19, 0xa9, 0x5c, 0xf3, 0xd1, 0xb1, 0x80, 0x1b,
0xca, 0x37, 0x87, 0x25, 0xb5, 0x8c, 0xae, 0xda, 0x6b, 0x65, 0x10, 0xe5, 0xb7, 0xea, 0x04, 0x54,
0x6d, 0xe2, 0x73, 0x53, 0xd5, 0xea, 0xb6, 0xaa, 0xd5, 0x13, 0xf9, 0x7b, 0x24, 0x78, 0x98, 0xd4,
0xeb, 0x61, 0xa2, 0x9d, 0x02, 0xaf, 0xa9, 0xd9, 0xfd, 0x39, 0x1c, 0xca, 0x54, 0xf4, 0x67, 0x00,
0x38, 0x94, 0xfb, 0x0c, 0x87, 0x36, 0xf1, 0x8d, 0xc5, 0xa6, 0xd6, 0x59, 0xea, 0xed, 0x44, 0x02,
0xe6, 0xd0, 0x58, 0xc0, 0x7b, 0x49, 0x23, 0x64, 0x50, 0x96, 0x44, 0xad, 0x84, 0xa1, 0xdc, 0x3e,
0xfd, 0x37, 0x0d, 0x6c, 0x06, 0x27, 0x8e, 0x67, 0x4e, 0x31, 0xd9, 0xc1, 0xa6, 0x4f, 0x5c, 0x36,
0xb2, 0x86, 0x81, 0x71, 0x47, 0x99, 0xe1, 0x48, 0x40, 0x43, 0x46, 0xed, 0xe7, 0x82, 0x50, 0x1a,
0x13, 0x0b, 0xf8, 0x8e, 0xb2, 0x9e, 0x17, 0x90, 0x1d, 0xe4, 0xe1, 0x1b, 0x23, 0xd0, 0x5c, 0x07,
0xfd, 0x77, 0x0d, 0xac, 0x64, 0x67, 0xc6, 0x66, 0xff, 0xdc, 0x58, 0x52, 0x43, 0xf5, 0xf3, 0xff,
0x1a, 0xaa, 0x48, 0xc0, 0xe5, 0x99, 0x6a, 0xef, 0x3c, 0x16, 0xb0, 0x53, 0xac, 0x21, 0xee, 0x9d,
0xcf, 0x1f, 0xab, 0xfa, 0x8d, 0x30, 0x39, 0x54, 0x6a, 0x90, 0x0a, 0xb2, 0xfa, 0x36, 0x58, 0xf4,
0xac, 0x30, 0x20, 0xd8, 0xa8, 0xaa, 0x6a, 0x6e, 0x46, 0x02, 0xa6, 0x48, 0x2c, 0xe0, 0xb2, 0xb2,
0x4c, 0x96, 0x2d, 0x94, 0xe2, 0xfa, 0x77, 0x60, 0xcd, 0x1a, 0x0e, 0xd9, 0x29, 0xc1, 0x26, 0x25,
0xfc, 0x94, 0xf9, 0x27, 0x81, 0x01, 0xd4, 0xd4, 0x7c, 0x19, 0x09, 0x58, 0x4b, 0xb9, 0xc3, 0x94,
0xca, 0xae, 0x81, 0x22, 0x5e, 0x6c, 0x34, 0x63, 0x1e, 0x89, 0xca, 0x72, 0xfa, 0x37, 0x60, 0xdd,
0x0a, 0x39, 0x33, 0x2d, 0xdb, 0x26, 0x1e, 0x37, 0x8f, 0xd9, 0x10, 0x13, 0x3f, 0x30, 0xee, 0xaa,
0xe3, 0xbf, 0x1f, 0x09, 0x58, 0x97, 0xf4, 0xa7, 0x8a, 0xfd, 0x2c, 0x21, 0x67, 0xe3, 0x5b, 0x66,
0x5a, 0xe8, 0x66, 0xb4, 0xfe, 0x14, 0xac, 0xb8, 0xd6, 0x99, 0x19, 0x10, 0x8a, 0xcd, 0x93, 0xbe,
0x17, 0x18, 0xcb, 0x4d, 0xad, 0x73, 0xbb, 0xf7, 0x9e, 0x1c, 0x4e, 0xd7, 0x3a, 0xfb, 0x8a, 0x50,
0x7c, 0xd0, 0xf7, 0xa4, 0x6a, 0x5d, 0xa9, 0xe6, 0xb0, 0xd6, 0x3f, 0x02, 0x2e, 0x38, 0x94, 0xa3,
0x7c, 0xe0, 0x54, 0xd0, 0x27, 0xf6, 0x28, 0x11, 0x5c, 0x29, 0x08, 0x22, 0x62, 0x8f, 0xca, 0x82,
0x53, 0xac, 0x20, 0x38, 0x05, 0x75, 0x0a, 0x6a, 0xce, 0x80, 0x32, 0x9f, 0xe0, 0x2c, 0xff, 0xd5,
0xe6, 0x42, 0xe7, 0xee, 0xf6, 0x46, 0x37, 0x79, 0x18, 0xba, 0x4f, 0xd3, 0x87, 0x21, 0xc9, 0xa9,
0xf7, 0x58, 0xf6, 0x62, 0x24, 0xe0, 0x6a, 0xba, 0x6d, 0x56, 0x98, 0xf5, 0xa4, 0xab, 0xf2, 0x70,
0x0b, 0x95, 0xc2, 0xf4, 0x1f, 0x35, 0x50, 0xf3, 0x08, 0xc5, 0x0e, 0x1d, 0x64, 0x86, 0xb5, 0x37,
0x1a, 0x3e, 0x91, 0x86, 0x13, 0x01, 0x8d, 0x3d, 0xe2, 0xf9, 0xc4, 0xb6, 0x38, 0xc1, 0x47, 0x89,
0x40, 0xaa, 0x19, 0x09, 0xa8, 0x3d, 0xce, 0xee, 0x20, 0x2f, 0xcf, 0xe5, 0x5a, 0xc3, 0xd0, 0xd0,
0x6a, 0x81, 0x0b, 0xf4, 0x5f, 0x34, 0x50, 0x4b, 0xaa, 0xf9, 0x6d, 0x48, 0x02, 0x6e, 0x9e, 0x38,
0x7d, 0x63, 0x4d, 0xd5, 0x33, 0x98, 0x08, 0xb8, 0xf2, 0x85, 0x2c, 0x93, 0x62, 0x0e, 0x9c, 0x5e,
0x24, 0xe0, 0x8a, 0x9b, 0x07, 0xb2, 0x84, 0x0b, 0xe8, 0xb4, 0xc8, 0xd1, 0x55, 0xbb, 0x14, 0x5e,
0x06, 0x2e, 0xc6, 0xed, 0xa2, 0x03, 0x2a, 0xf0, 0x7d, 0xfd, 0x13, 0x50, 0x0d, 0x29, 0xf7, 0xc3,
0x80, 0x13, 0x6c, 0xd4, 0x55, 0x4f, 0x36, 0xe5, 0x53, 0x92, 0x81, 0xb1, 0x80, 0x35, 0x75, 0x82,
0x0c, 0x69, 0xa1, 0x19, 0xab, 0xb2, 0x93, 0x17, 0x1c, 0x27, 0xe6, 0x20, 0x74, 0x4c, 0x8f, 0xf9,
0xdc, 0xd0, 0x67, 0xd9, 0x21, 0x45, 0x7d, 0xfe, 0xf5, 0xfe, 0x11, 0xf3, 0xb9, 0xcc, 0xce, 0xcf,
0x03, 0x59, 0x76, 0x05, 0x34, 0x9f, 0x5d, 0x31, 0xbc, 0x0c, 0xc8, 0xec, 0x0a, 0x0e, 0x68, 0xca,
0x87, 0x8e, 0x5c, 0xea, 0xdf, 0x6b, 0xa0, 0x46, 0x43, 0xd7, 0xb4, 0x19, 0xa5, 0x44, 0x5d, 0x83,
0x81, 0xb1, 0xae, 0x4e, 0xf7, 0x7c, 0x22, 0x60, 0x1d, 0x59, 0xa7, 0x87, 0xa1, 0xbb, 0x3b, 0x23,
0x65, 0xc7, 0xd1, 0x02, 0x12, 0x0b, 0xf8, 0x76, 0xf2, 0x4a, 0x17, 0xe0, 0xe9, 0x19, 0x2f, 0xc6,
0xed, 0x9b, 0x2a, 0xa8, 0xa4, 0xd1, 0x3b, 0xb8, 0x7c, 0xd5, 0xa8, 0x8c, 0x5f, 0x35, 0x2a, 0x97,
0x93, 0x86, 0x36, 0x9e, 0x34, 0xb4, 0x9f, 0xae, 0x1b, 0x95, 0x97, 0xd7, 0x0d, 0x6d, 0x7c, 0xdd,
0xa8, 0xfc, 0x79, 0xdd, 0xa8, 0x3c, 0x7b, 0xf7, 0x3f, 0xdc, 0xb9, 0x49, 0xe3, 0xf6, 0x17, 0xd5,
0xdd, 0xfb, 0xc1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x75, 0x19, 0xf5, 0x92, 0x9d, 0x09, 0x00,
0x00,
}
func (m *DeviceConfiguration) Marshal() (dAtA []byte, err error) {
@@ -176,6 +179,13 @@ func (m *DeviceConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.RawNumConnections != 0 {
i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.RawNumConnections))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x98
}
if m.RemoteGUIPort != 0 {
i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.RemoteGUIPort))
i--
@@ -423,6 +433,9 @@ func (m *DeviceConfiguration) ProtoSize() (n int) {
if m.RemoteGUIPort != 0 {
n += 2 + sovDeviceconfiguration(uint64(m.RemoteGUIPort))
}
if m.RawNumConnections != 0 {
n += 2 + sovDeviceconfiguration(uint64(m.RawNumConnections))
}
return n
}
@@ -918,6 +931,25 @@ func (m *DeviceConfiguration) Unmarshal(dAtA []byte) error {
break
}
}
case 19:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RawNumConnections", wireType)
}
m.RawNumConnections = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowDeviceconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RawNumConnections |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipDeviceconfiguration(dAtA[iNdEx:])
+2 -3
View File
@@ -20,6 +20,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sliceutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/thejerf/suture/v4"
)
@@ -198,9 +199,7 @@ func (w *wrapper) Unsubscribe(c Committer) {
w.mut.Lock()
for i := range w.subs {
if w.subs[i] == c {
copy(w.subs[i:], w.subs[i+1:])
w.subs[len(w.subs)-1] = nil
w.subs = w.subs[:len(w.subs)-1]
w.subs = sliceutil.RemoveAndZero(w.subs, i)
break
}
}
+3 -1
View File
@@ -91,6 +91,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
if isLocal {
priority = d.lanPriority
}
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, isLocal, priority), nil
}
@@ -108,9 +109,10 @@ func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Confi
commonDialer: commonDialer{
reconnectInterval: time.Duration(quicInterval) * time.Second,
tlsCfg: tlsCfg,
lanChecker: lanChecker,
lanPriority: opts.ConnectionPriorityQUICLAN,
wanPriority: opts.ConnectionPriorityQUICWAN,
lanChecker: lanChecker,
allowsMultiConns: true,
},
registry: registry,
}
+3 -1
View File
@@ -105,7 +105,9 @@ func (t *quicListener) serve(ctx context.Context) error {
defer quicTransport.Close()
svc := stun.New(t.cfg, t, &transportPacketConn{tran: quicTransport}, tracer)
go svc.Serve(ctx)
stunCtx, cancel := context.WithCancel(ctx)
defer cancel()
go svc.Serve(stunCtx)
t.registry.Register(t.uri.Scheme, quicTransport)
defer t.registry.Unregister(t.uri.Scheme, quicTransport)
+2 -3
View File
@@ -12,6 +12,7 @@ package registry
import (
"strings"
"github.com/syncthing/syncthing/lib/sliceutil"
"github.com/syncthing/syncthing/lib/sync"
)
@@ -41,9 +42,7 @@ func (r *Registry) Unregister(scheme string, item interface{}) {
candidates := r.available[scheme]
for i, existingItem := range candidates {
if existingItem == item {
candidates[i] = candidates[len(candidates)-1]
candidates[len(candidates)-1] = nil
r.available[scheme] = candidates[:len(candidates)-1]
r.available[scheme] = sliceutil.RemoveAndZero(candidates, i)
break
}
}
+247 -34
View File
@@ -11,10 +11,14 @@ package connections
import (
"context"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/base32"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"net"
"net/url"
@@ -23,8 +27,10 @@ import (
stdsync "sync"
"time"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/discover"
@@ -33,6 +39,7 @@ import (
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/semaphore"
"github.com/syncthing/syncthing/lib/sliceutil"
"github.com/syncthing/syncthing/lib/stringutil"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
@@ -66,12 +73,14 @@ var (
errDeviceIgnored = errors.New("device is ignored")
errConnLimitReached = errors.New("connection limit reached")
errDevicePaused = errors.New("device is paused")
// A connection is being closed to make space for better ones
errReplacingConnection = errors.New("replacing connection")
)
const (
perDeviceWarningIntv = 15 * time.Minute
tlsHandshakeTimeout = 10 * time.Second
minConnectionReplaceAge = 10 * time.Second
minConnectionLoopSleep = 5 * time.Second
stdConnectionLoopSleep = time.Minute
worstDialerPriority = math.MaxInt32
@@ -79,6 +88,7 @@ const (
shortLivedConnectionThreshold = 5 * time.Second
dialMaxParallel = 64
dialMaxParallelPerDevice = 8
maxNumConnections = 128 // the maximum number of connections we maintain to any given device
)
// From go/src/crypto/tls/cipher_suites.go
@@ -150,6 +160,7 @@ type connWithHello struct {
type service struct {
*suture.Supervisor
connectionStatusHandler
deviceConnectionTracker
cfg config.Wrapper
myID protocol.DeviceID
@@ -281,21 +292,43 @@ func (s *service) handleConns(ctx context.Context) error {
_ = c.SetDeadline(time.Now().Add(20 * time.Second))
go func() {
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
// Exchange Hello messages with the peer.
outgoing := s.helloForDevice(remoteID)
incoming, err := protocol.ExchangeHello(c, outgoing)
// The timestamps are used to create the connection ID.
c.connectionID = newConnectionID(outgoing.Timestamp, incoming.Timestamp)
select {
case s.hellos <- &connWithHello{c, hello, err, remoteID, remoteCert}:
case s.hellos <- &connWithHello{c, incoming, err, remoteID, remoteCert}:
case <-ctx.Done():
}
}()
}
}
func (s *service) helloForDevice(remoteID protocol.DeviceID) protocol.Hello {
hello := protocol.Hello{
ClientName: "syncthing",
ClientVersion: build.Version,
Timestamp: time.Now().UnixNano(),
}
if cfg, ok := s.cfg.Device(remoteID); ok {
hello.NumConnections = cfg.NumConnections()
// Set our name (from the config of our device ID) only if we
// already know about the other side device ID.
if myCfg, ok := s.cfg.Device(s.myID); ok {
hello.DeviceName = myCfg.Name
}
}
return hello
}
func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalConn) error {
if s.cfg.IgnoredDevice(remoteID) {
return errDeviceIgnored
}
if max := s.cfg.Options().ConnectionLimitMax; max > 0 && s.model.NumConnections() >= max {
if max := s.cfg.Options().ConnectionLimitMax; max > 0 && s.numConnectedDevices() >= max {
// We're not allowed to accept any more connections.
return errConnLimitReached
}
@@ -315,31 +348,26 @@ func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalCon
return errNetworkNotAllowed
}
// Lower priority is better, just like nice etc.
if ct, ok := s.model.Connection(remoteID); ok {
if ct.Priority() > c.priority || time.Since(ct.Statistics().StartedAt) > minConnectionReplaceAge {
l.Debugf("Switching connections %s (existing: %s new: %s)", remoteID, ct, c)
} else {
// We should not already be connected to the other party. TODO: This
// could use some better handling. If the old connection is dead but
// hasn't timed out yet we may want to drop *that* connection and keep
// this one. But in case we are two devices connecting to each other
// in parallel we don't want to do that or we end up with no
// connections still established...
return errDeviceAlreadyConnected
}
currentConns := s.numConnectionsForDevice(cfg.DeviceID)
desiredConns := s.desiredConnectionsToDevice(cfg.DeviceID)
worstPrio := s.worstConnectionPriority(remoteID)
ourUpgradeThreshold := c.priority + s.cfg.Options().ConnectionPriorityUpgradeThreshold
if currentConns >= desiredConns && ourUpgradeThreshold >= worstPrio {
l.Debugf("Not accepting connection to %s at %s: already have %d connections, desire %d", remoteID, c, currentConns, desiredConns)
return errDeviceAlreadyConnected
}
return nil
}
func (s *service) handleHellos(ctx context.Context) error {
var c internalConn
var hello protocol.Hello
var err error
var remoteID protocol.DeviceID
var remoteCert *x509.Certificate
for {
var c internalConn
var hello protocol.Hello
var err error
var remoteID protocol.DeviceID
var remoteCert *x509.Certificate
select {
case <-ctx.Done():
return ctx.Err()
@@ -416,15 +444,17 @@ func (s *service) handleHellos(ctx context.Context) error {
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), s.keyGen)
s.accountAddedConnection(protoConn, hello, s.cfg.Options().ConnectionPriorityUpgradeThreshold)
go func() {
<-protoConn.Closed()
s.accountRemovedConnection(protoConn)
s.dialNowDevicesMut.Lock()
s.dialNowDevices[remoteID] = struct{}{}
s.scheduleDialNow()
s.dialNowDevicesMut.Unlock()
}()
l.Infof("Established secure connection to %s at %s", remoteID, c)
l.Infof("Established secure connection to %s at %s", remoteID.Short(), c)
s.model.AddConnection(protoConn, hello)
continue
@@ -518,7 +548,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
allowAdditional := 0 // no limit
connectionLimit := cfg.Options.LowestConnectionLimit()
if connectionLimit > 0 {
current := s.model.NumConnections()
current := s.numConnectedDevices()
allowAdditional = connectionLimit - current
if allowAdditional <= 0 {
l.Debugf("Skipping dial because we've reached the connection limit, current %d >= limit %d", current, connectionLimit)
@@ -545,19 +575,20 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
// See if we are already connected and, if so, what our cutoff is
// for dialer priority.
priorityCutoff := worstDialerPriority
connection, connected := s.model.Connection(deviceCfg.DeviceID)
if connected {
if currentConns := s.numConnectionsForDevice(deviceCfg.DeviceID); currentConns > 0 {
// Set the priority cutoff to the current connection's priority,
// so that we don't attempt any dialers with worse priority.
priorityCutoff = connection.Priority()
priorityCutoff = s.worstConnectionPriority(deviceCfg.DeviceID)
// Reduce the priority cutoff by the upgrade threshold, so that
// we don't attempt dialers that aren't considered a worthy upgrade.
priorityCutoff -= cfg.Options.ConnectionPriorityUpgradeThreshold
if bestDialerPriority >= priorityCutoff {
if bestDialerPriority >= priorityCutoff && currentConns >= s.desiredConnectionsToDevice(deviceCfg.DeviceID) {
// Our best dialer is not any better than what we already
// have, so nothing to do here.
// have, and we already have the desired number of
// connections to this device,so nothing to do here.
l.Debugf("Skipping dial to %s because we already have %d connections and our best dialer is not better than %d", deviceCfg.DeviceID.Short(), currentConns, priorityCutoff)
continue
}
}
@@ -625,14 +656,14 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
deviceID := deviceCfg.DeviceID
addrs := s.resolveDeviceAddrs(ctx, deviceCfg)
l.Debugln("Resolved device", deviceID, "addresses:", addrs)
l.Debugln("Resolved device", deviceID.Short(), "addresses:", addrs)
dialTargets := make([]dialTarget, 0, len(addrs))
for _, addr := range addrs {
// Use both device and address, as you might have two devices connected
// to the same relay
if !initial && nextDialAt.get(deviceID, addr).After(now) {
l.Debugf("Not dialing %s via %v as it's not time yet", deviceID, addr)
l.Debugf("Not dialing %s via %v as it's not time yet", deviceID.Short(), addr)
continue
}
@@ -669,8 +700,17 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg, s.registry, s.lanChecker)
priority := dialer.Priority(uri.Host)
if priority >= priorityCutoff {
l.Debugf("Not dialing using %s as priority is not better than current connection (%d >= %d)", dialerFactory, priority, priorityCutoff)
currentConns := s.numConnectionsForDevice(deviceCfg.DeviceID)
if priority > priorityCutoff {
l.Debugf("Not dialing %s at %s using %s as priority is worse than current connection (%d > %d)", deviceID.Short(), addr, dialerFactory, priority, priorityCutoff)
continue
}
if currentConns > 0 && !dialer.AllowsMultiConns() {
l.Debugf("Not dialing %s at %s using %s as it does not allow multiple connections and we already have a connection", deviceID.Short(), addr, dialerFactory)
continue
}
if currentConns >= s.desiredConnectionsToDevice(deviceCfg.DeviceID) && priority == priorityCutoff {
l.Debugf("Not dialing %s at %s using %s as priority is equal and we already have %d/%d connections", deviceID.Short(), addr, dialerFactory, currentConns, deviceCfg.NumConnections)
continue
}
@@ -742,6 +782,10 @@ func (s *lanChecker) isLAN(addr net.Addr) bool {
return true
}
if ip.IsLinkLocalUnicast() {
return true
}
for _, lan := range s.cfg.Options().AlwaysLocalNets {
_, ipnet, err := net.ParseCIDR(lan)
if err != nil {
@@ -753,7 +797,14 @@ func (s *lanChecker) isLAN(addr net.Addr) bool {
}
}
lans, _ := osutil.GetLans()
lans, err := osutil.GetLans()
if err != nil {
l.Debugln("Failed to retrieve interface IPs:", err)
priv := ip.IsPrivate()
l.Debugf("Assuming isLAN=%v for IP %v", priv, ip)
return priv
}
for _, lan := range lans {
if lan.Contains(ip) {
return true
@@ -1272,3 +1323,165 @@ func (r nextDialRegistry) sleepDurationAndCleanup(now time.Time) time.Duration {
}
return sleep
}
func (s *service) desiredConnectionsToDevice(deviceID protocol.DeviceID) int {
cfg, ok := s.cfg.Device(deviceID)
if !ok {
// We want no connections to an unknown device.
return 0
}
otherSide := s.wantConnectionsForDevice(deviceID)
thisSide := cfg.NumConnections()
switch {
case otherSide <= 0:
// The other side doesn't support multiple connections, or we
// haven't yet connected to them so we don't know what they support
// or not. Use a single connection until we know better.
return 1
case otherSide == 1:
// The other side supports multiple connections, but only wants
// one. We should honour that.
return 1
case thisSide == 1:
// We want only one connection, so we should honour that.
return 1
// Finally, we allow negotiation and use the higher of the two values,
// while keeping at or below the max allowed value.
default:
return min(max(thisSide, otherSide), maxNumConnections)
}
}
// The deviceConnectionTracker keeps track of how many devices we are
// connected to and how many connections we have to each device. It also
// tracks how many connections they are willing to use.
type deviceConnectionTracker struct {
connectionsMut stdsync.Mutex
connections map[protocol.DeviceID][]protocol.Connection // current connections
wantConnections map[protocol.DeviceID]int // number of connections they want
}
func (c *deviceConnectionTracker) accountAddedConnection(conn protocol.Connection, h protocol.Hello, upgradeThreshold int) {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
// Lazily initialize the maps
if c.connections == nil {
c.connections = make(map[protocol.DeviceID][]protocol.Connection)
c.wantConnections = make(map[protocol.DeviceID]int)
}
// Add the connection to the list of current connections and remember
// how many total connections they want
d := conn.DeviceID()
c.connections[d] = append(c.connections[d], conn)
c.wantConnections[d] = int(h.NumConnections)
l.Debugf("Added connection for %s (now %d), they want %d connections", d.Short(), len(c.connections[d]), h.NumConnections)
// Close any connections we no longer want to retain.
c.closeWorsePriorityConnectionsLocked(d, conn.Priority()-upgradeThreshold)
}
func (c *deviceConnectionTracker) accountRemovedConnection(conn protocol.Connection) {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
d := conn.DeviceID()
cid := conn.ConnectionID()
// Remove the connection from the list of current connections
for i, conn := range c.connections[d] {
if conn.ConnectionID() == cid {
c.connections[d] = sliceutil.RemoveAndZero(c.connections[d], i)
break
}
}
// Clean up if required
if len(c.connections[d]) == 0 {
delete(c.connections, d)
delete(c.wantConnections, d)
}
l.Debugf("Removed connection for %s (now %d)", d.Short(), c.connections[d])
}
func (c *deviceConnectionTracker) numConnectionsForDevice(d protocol.DeviceID) int {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
return len(c.connections[d])
}
func (c *deviceConnectionTracker) wantConnectionsForDevice(d protocol.DeviceID) int {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
return c.wantConnections[d]
}
func (c *deviceConnectionTracker) numConnectedDevices() int {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
return len(c.connections)
}
func (c *deviceConnectionTracker) worstConnectionPriority(d protocol.DeviceID) int {
c.connectionsMut.Lock()
defer c.connectionsMut.Unlock()
if len(c.connections[d]) == 0 {
return math.MaxInt // worst possible priority
}
worstPriority := c.connections[d][0].Priority()
for _, conn := range c.connections[d][1:] {
if p := conn.Priority(); p > worstPriority {
worstPriority = p
}
}
return worstPriority
}
// closeWorsePriorityConnectionsLocked closes all connections to the given
// device that are worse than the cutoff priority. Must be called with the
// lock held.
func (c *deviceConnectionTracker) closeWorsePriorityConnectionsLocked(d protocol.DeviceID, cutoff int) {
for _, conn := range c.connections[d] {
if p := conn.Priority(); p > cutoff {
l.Debugf("Closing connection %s to %s with priority %d (cutoff %d)", conn, d.Short(), p, cutoff)
go conn.Close(errReplacingConnection)
}
}
}
// newConnectionID generates a connection ID. The connection ID is designed
// to be unique for each connection and chronologically sortable. It is
// based on the sum of two timestamps: when we think the connection was
// started, and when the other side thinks the connection was started. We
// then add some random data for good measure. This way, even if the other
// side does some funny business with the timestamp, we will get no worse
// than random connection IDs.
func newConnectionID(t0, t1 int64) string {
var buf [16]byte // 8 bytes timestamp, 8 bytes random
binary.BigEndian.PutUint64(buf[:], uint64(t0+t1))
_, _ = io.ReadFull(rand.Reader, buf[8:])
enc := base32.HexEncoding.WithPadding(base32.NoPadding)
// We encode the two parts separately and concatenate the results. The
// reason for this is that the timestamp (64 bits) doesn't precisely
// align to the base32 encoding (5 bits per character), so we'd get a
// character in the middle that is a mix of bits from the timestamp and
// from the random. We want the timestamp part deterministic.
return enc.EncodeToString(buf[:8]) + enc.EncodeToString(buf[8:])
}
// temporary implementations of min and max, to be removed once we can use
// Go 1.21 builtins. :)
func min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
+14 -5
View File
@@ -42,6 +42,7 @@ type internalConn struct {
isLocal bool
priority int
establishedAt time.Time
connectionID string // set after Hello exchange
}
type connType int
@@ -88,12 +89,13 @@ func (t connType) Transport() string {
}
func newInternalConn(tc tlsConn, connType connType, isLocal bool, priority int) internalConn {
now := time.Now()
return internalConn{
tlsConn: tc,
connType: connType,
isLocal: isLocal,
priority: priority,
establishedAt: time.Now().Truncate(time.Second),
establishedAt: now.Truncate(time.Second),
}
}
@@ -138,12 +140,16 @@ func (c internalConn) EstablishedAt() time.Time {
return c.establishedAt
}
func (c internalConn) ConnectionID() string {
return c.connectionID
}
func (c internalConn) String() string {
t := "WAN"
if c.isLocal {
t = "LAN"
}
return fmt.Sprintf("%s-%s/%s/%s/%s-P%d", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto(), t, c.Priority())
return fmt.Sprintf("%s-%s/%s/%s/%s-P%d-%s", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto(), t, c.Priority(), c.connectionID)
}
type dialerFactory interface {
@@ -160,6 +166,7 @@ type commonDialer struct {
lanChecker *lanChecker
lanPriority int
wanPriority int
allowsMultiConns bool
}
func (d *commonDialer) RedialFrequency() time.Duration {
@@ -173,10 +180,15 @@ func (d *commonDialer) Priority(host string) int {
return d.wanPriority
}
func (d *commonDialer) AllowsMultiConns() bool {
return d.allowsMultiConns
}
type genericDialer interface {
Dial(context.Context, protocol.DeviceID, *url.URL) (internalConn, error)
RedialFrequency() time.Duration
Priority(host string) int
AllowsMultiConns() bool
}
type listenerFactory interface {
@@ -212,10 +224,7 @@ type genericListener interface {
type Model interface {
protocol.Model
AddConnection(conn protocol.Connection, hello protocol.Hello)
NumConnections() int
Connection(remoteID protocol.DeviceID) (protocol.Connection, bool)
OnHello(protocol.DeviceID, net.Addr, protocol.Hello) error
GetHello(protocol.DeviceID) protocol.HelloIntf
DeviceStatistics() (map[protocol.DeviceID]stats.DeviceStatistics, error)
}
+3 -1
View File
@@ -62,6 +62,7 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
if isLocal {
priority = d.lanPriority
}
return newInternalConn(tc, connTypeTCPClient, isLocal, priority), nil
}
@@ -73,9 +74,10 @@ func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
lanChecker: lanChecker,
lanPriority: opts.ConnectionPriorityTCPLAN,
wanPriority: opts.ConnectionPriorityTCPWAN,
lanChecker: lanChecker,
allowsMultiConns: true,
},
registry: registry,
}
+3 -3
View File
@@ -178,10 +178,10 @@ func (t *tcpListener) WANAddresses() []*url.URL {
// For every address with a specified IP, add one without an IP,
// just in case the specified IP is still internal (router behind DMZ).
if len(addr.IP) != 0 && !addr.IP.IsUnspecified() {
uri = *t.uri
zeroUri := *t.uri
addr.IP = nil
uri.Host = addr.String()
uris = append(uris, &uri)
zeroUri.Host = addr.String()
uris = append(uris, &zeroUri)
}
}
}
+21 -11
View File
@@ -25,6 +25,7 @@ import (
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/net/http2"
)
type globalClient struct {
@@ -107,14 +108,16 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLo
}
var announceClient httpClient = &contextClient{&http.Client{
Timeout: requestTimeout,
Transport: &http.Transport{
DialContext: dialContext,
Proxy: http.ProxyFromEnvironment,
Transport: http2EnabledTransport(&http.Transport{
DialContext: dialContext,
Proxy: http.ProxyFromEnvironment,
DisableKeepAlives: true, // announcements are few and far between, so don't keep the connection open
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opts.insecure,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
},
},
}),
}}
if opts.id != "" {
announceClient = newIDCheckingHTTPClient(announceClient, devID)
@@ -124,13 +127,15 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLo
// certificate here, so lets not include it. May be insecure if requested.
var queryClient httpClient = &contextClient{&http.Client{
Timeout: requestTimeout,
Transport: &http.Transport{
DialContext: dialer.DialContext,
Proxy: http.ProxyFromEnvironment,
Transport: http2EnabledTransport(&http.Transport{
DialContext: dialer.DialContext,
Proxy: http.ProxyFromEnvironment,
IdleConnTimeout: time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opts.insecure,
MinVersion: tls.VersionTLS12,
},
},
}),
}}
if opts.id != "" {
queryClient = newIDCheckingHTTPClient(queryClient, devID)
@@ -176,7 +181,7 @@ func (c *globalClient) Lookup(ctx context.Context, device protocol.DeviceID) (ad
l.Debugln("globalClient.Lookup", qURL, err)
return nil, err
}
if resp.StatusCode != 200 {
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
l.Debugln("globalClient.Lookup", qURL, resp.Status)
err := errors.New(resp.Status)
@@ -437,7 +442,7 @@ type contextClient struct {
}
func (c *contextClient) Get(ctx context.Context, url string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
@@ -445,7 +450,7 @@ func (c *contextClient) Get(ctx context.Context, url string) (*http.Response, er
}
func (c *contextClient) Post(ctx context.Context, url, ctype string, data io.Reader) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, "POST", url, data)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, data)
if err != nil {
return nil, err
}
@@ -464,3 +469,8 @@ func ipv4Identity(port int) string {
func ipv6Identity(addr string) string {
return fmt.Sprintf("IPv6 local multicast discovery on address %s", addr)
}
func http2EnabledTransport(t *http.Transport) *http.Transport {
_ = http2.ConfigureTransport(t)
return t
}
+7 -12
View File
@@ -110,11 +110,8 @@ func (c *localClient) Error() error {
func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, bool) {
addrs := c.addrList.AllAddresses()
// The list of all addresses can include unspecified addresses intended
// for a discovery server to complete, based on the packet source. We
// don't do that for local discovery, so filter out addresses that are
// usable as-is.
addrs = filterUnspecifiedLocal(addrs)
// remove all addresses which are not dialable
addrs = filterUndialableLocal(addrs)
// do not leak relay tokens to discovery
addrs = sanitizeRelayAddresses(addrs)
@@ -266,7 +263,7 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
continue
}
u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
l.Debugf("discover: Reconstructed URL is %#v", u)
l.Debugf("discover: Reconstructed URL is %v", u)
validAddresses = append(validAddresses, u.String())
l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr, u.String())
} else {
@@ -292,9 +289,9 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
return isNewDevice
}
// filterUnspecifiedLocal returns the list of addresses after removing any
// unspecified, localhost, multicast, broadcast or port-zero addresses.
func filterUnspecifiedLocal(addrs []string) []string {
// filterUndialableLocal returns the list of addresses after removing any
// localhost, multicast, broadcast or port-zero addresses.
func filterUndialableLocal(addrs []string) []string {
filtered := addrs[:0]
for _, addr := range addrs {
u, err := url.Parse(addr)
@@ -310,9 +307,7 @@ func filterUnspecifiedLocal(addrs []string) []string {
switch {
case len(tcpAddr.IP) == 0:
case tcpAddr.Port == 0:
case tcpAddr.IP.IsUnspecified():
case !tcpAddr.IP.IsGlobalUnicast() && !tcpAddr.IP.IsLinkLocalUnicast():
default:
case tcpAddr.IP.IsGlobalUnicast(), tcpAddr.IP.IsLinkLocalUnicast(), tcpAddr.IP.IsUnspecified():
filtered = append(filtered, addr)
}
}
+7 -5
View File
@@ -91,14 +91,14 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
}
}
func TestFilterUnspecified(t *testing.T) {
func TestFilterUndialable(t *testing.T) {
addrs := []string{
"quic://[2001:db8::1]:22000", // OK
"tcp://192.0.2.42:22000", // OK
"quic://[2001:db8::1]:0", // remove, port zero
"tcp://192.0.2.42:0", // remove, port zero
"quic://[::]:22000", // remove, unspecified
"tcp://0.0.0.0:22000", // remove, unspecified
"quic://[::]:22000", // OK
"tcp://0.0.0.0:22000", // OK
"tcp://[2001:db8::1]", // remove, no port
"tcp://192.0.2.42", // remove, no port
"tcp://foo:bar", // remove, host/port does not resolve
@@ -112,11 +112,13 @@ func TestFilterUnspecified(t *testing.T) {
exp := []string{
"quic://[2001:db8::1]:22000",
"tcp://192.0.2.42:22000",
"quic://[::]:22000",
"tcp://0.0.0.0:22000",
"tcp://[fe80::9ef:dff1:b332:5e56]:55681",
}
res := filterUnspecifiedLocal(addrs)
res := filterUndialableLocal(addrs)
if fmt.Sprint(res) != fmt.Sprint(exp) {
t.Log(res)
t.Error("filterUnspecified returned invalid addresses")
t.Error("filterUndialableLocal returned invalid addresses")
}
}
+1
View File
@@ -34,6 +34,7 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection {
return f.fileData[name], nil
})
f.DeviceIDReturns(id)
f.ConnectionIDReturns(rand.String(16))
f.CloseCalls(func(err error) {
f.closeOnce.Do(func() {
close(f.closed)
+3 -2
View File
@@ -530,14 +530,15 @@ func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.Cancel
cfg.Folders = []config.FolderConfiguration{fcfg}
replace(t, w, cfg)
m := newModel(t, w, myID, "syncthing", "dev", nil)
m := newModel(t, w, myID, nil)
m.ServeBackground()
<-m.started
must(t, m.ScanFolder("ro"))
m.fmut.RLock()
defer m.fmut.RUnlock()
f := m.folderRunners["ro"].(*receiveOnlyFolder)
r, _ := m.folderRunners.Get("ro")
f := r.(*receiveOnlyFolder)
return m, f, cancel
}
+1 -1
View File
@@ -507,7 +507,7 @@ nextFile:
devices := snap.Availability(fileName)
for _, dev := range devices {
if _, ok := f.model.Connection(dev); ok {
if f.model.ConnectedTo(dev) {
// Handle the file normally, by copying and pulling, etc.
f.handleFile(fi, snap, copyChan)
continue nextFile
+2 -1
View File
@@ -117,7 +117,8 @@ func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testMode
model := setupModel(t, w)
model.cancel()
<-model.stopped
f := model.folderRunners[fcfg.ID].(*sendReceiveFolder)
r, _ := model.folderRunners.Get(fcfg.ID)
f := r.(*sendReceiveFolder)
f.tempPullErrors = make(map[string]string)
f.ctx = context.Background()
+3 -2
View File
@@ -427,7 +427,7 @@ func (r *indexHandlerRegistry) AddIndexInfo(folder string, startInfo *clusterCon
r.mut.Lock()
defer r.mut.Unlock()
if r.indexHandlers.RemoveAndWait(folder, 0) {
if r.indexHandlers.RemoveAndWait(folder, 0) == nil {
l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.conn.DeviceID().Short(), folder)
}
folderState, ok := r.folderStates[folder]
@@ -458,11 +458,12 @@ func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]remoteFolderSta
r.mut.Lock()
defer r.mut.Unlock()
r.indexHandlers.Each(func(folder string, is *indexHandler) {
r.indexHandlers.Each(func(folder string, is *indexHandler) error {
if _, ok := except[folder]; !ok {
r.indexHandlers.RemoveAndWait(folder, 0)
l.Debugf("Removed index handler for device %v and folder %v (removeAllExcept)", r.conn.DeviceID().Short(), folder)
}
return nil
})
for folder := range r.startInfos {
if _, ok := except[folder]; !ok {
+48 -192
View File
@@ -76,18 +76,16 @@ type Model struct {
result1 model.FolderCompletion
result2 error
}
ConnectionStub func(protocol.DeviceID) (protocol.Connection, bool)
connectionMutex sync.RWMutex
connectionArgsForCall []struct {
ConnectedToStub func(protocol.DeviceID) bool
connectedToMutex sync.RWMutex
connectedToArgsForCall []struct {
arg1 protocol.DeviceID
}
connectionReturns struct {
result1 protocol.Connection
result2 bool
connectedToReturns struct {
result1 bool
}
connectionReturnsOnCall map[int]struct {
result1 protocol.Connection
result2 bool
connectedToReturnsOnCall map[int]struct {
result1 bool
}
ConnectionStatsStub func() map[string]interface{}
connectionStatsMutex sync.RWMutex
@@ -262,17 +260,6 @@ type Model struct {
result1 map[string][]versioner.FileVersion
result2 error
}
GetHelloStub func(protocol.DeviceID) protocol.HelloIntf
getHelloMutex sync.RWMutex
getHelloArgsForCall []struct {
arg1 protocol.DeviceID
}
getHelloReturns struct {
result1 protocol.HelloIntf
}
getHelloReturnsOnCall map[int]struct {
result1 protocol.HelloIntf
}
GetMtimeMappingStub func(string, string) (fs.MtimeMapping, error)
getMtimeMappingMutex sync.RWMutex
getMtimeMappingArgsForCall []struct {
@@ -378,16 +365,6 @@ type Model struct {
result3 []db.FileInfoTruncated
result4 error
}
NumConnectionsStub func() int
numConnectionsMutex sync.RWMutex
numConnectionsArgsForCall []struct {
}
numConnectionsReturns struct {
result1 int
}
numConnectionsReturnsOnCall map[int]struct {
result1 int
}
OnHelloStub func(protocol.DeviceID, net.Addr, protocol.Hello) error
onHelloMutex sync.RWMutex
onHelloArgsForCall []struct {
@@ -888,68 +865,65 @@ func (fake *Model) CompletionReturnsOnCall(i int, result1 model.FolderCompletion
}{result1, result2}
}
func (fake *Model) Connection(arg1 protocol.DeviceID) (protocol.Connection, bool) {
fake.connectionMutex.Lock()
ret, specificReturn := fake.connectionReturnsOnCall[len(fake.connectionArgsForCall)]
fake.connectionArgsForCall = append(fake.connectionArgsForCall, struct {
func (fake *Model) ConnectedTo(arg1 protocol.DeviceID) bool {
fake.connectedToMutex.Lock()
ret, specificReturn := fake.connectedToReturnsOnCall[len(fake.connectedToArgsForCall)]
fake.connectedToArgsForCall = append(fake.connectedToArgsForCall, struct {
arg1 protocol.DeviceID
}{arg1})
stub := fake.ConnectionStub
fakeReturns := fake.connectionReturns
fake.recordInvocation("Connection", []interface{}{arg1})
fake.connectionMutex.Unlock()
stub := fake.ConnectedToStub
fakeReturns := fake.connectedToReturns
fake.recordInvocation("ConnectedTo", []interface{}{arg1})
fake.connectedToMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
return ret.result1
}
return fakeReturns.result1, fakeReturns.result2
return fakeReturns.result1
}
func (fake *Model) ConnectionCallCount() int {
fake.connectionMutex.RLock()
defer fake.connectionMutex.RUnlock()
return len(fake.connectionArgsForCall)
func (fake *Model) ConnectedToCallCount() int {
fake.connectedToMutex.RLock()
defer fake.connectedToMutex.RUnlock()
return len(fake.connectedToArgsForCall)
}
func (fake *Model) ConnectionCalls(stub func(protocol.DeviceID) (protocol.Connection, bool)) {
fake.connectionMutex.Lock()
defer fake.connectionMutex.Unlock()
fake.ConnectionStub = stub
func (fake *Model) ConnectedToCalls(stub func(protocol.DeviceID) bool) {
fake.connectedToMutex.Lock()
defer fake.connectedToMutex.Unlock()
fake.ConnectedToStub = stub
}
func (fake *Model) ConnectionArgsForCall(i int) protocol.DeviceID {
fake.connectionMutex.RLock()
defer fake.connectionMutex.RUnlock()
argsForCall := fake.connectionArgsForCall[i]
func (fake *Model) ConnectedToArgsForCall(i int) protocol.DeviceID {
fake.connectedToMutex.RLock()
defer fake.connectedToMutex.RUnlock()
argsForCall := fake.connectedToArgsForCall[i]
return argsForCall.arg1
}
func (fake *Model) ConnectionReturns(result1 protocol.Connection, result2 bool) {
fake.connectionMutex.Lock()
defer fake.connectionMutex.Unlock()
fake.ConnectionStub = nil
fake.connectionReturns = struct {
result1 protocol.Connection
result2 bool
}{result1, result2}
func (fake *Model) ConnectedToReturns(result1 bool) {
fake.connectedToMutex.Lock()
defer fake.connectedToMutex.Unlock()
fake.ConnectedToStub = nil
fake.connectedToReturns = struct {
result1 bool
}{result1}
}
func (fake *Model) ConnectionReturnsOnCall(i int, result1 protocol.Connection, result2 bool) {
fake.connectionMutex.Lock()
defer fake.connectionMutex.Unlock()
fake.ConnectionStub = nil
if fake.connectionReturnsOnCall == nil {
fake.connectionReturnsOnCall = make(map[int]struct {
result1 protocol.Connection
result2 bool
func (fake *Model) ConnectedToReturnsOnCall(i int, result1 bool) {
fake.connectedToMutex.Lock()
defer fake.connectedToMutex.Unlock()
fake.ConnectedToStub = nil
if fake.connectedToReturnsOnCall == nil {
fake.connectedToReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.connectionReturnsOnCall[i] = struct {
result1 protocol.Connection
result2 bool
}{result1, result2}
fake.connectedToReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *Model) ConnectionStats() map[string]interface{} {
@@ -1797,67 +1771,6 @@ func (fake *Model) GetFolderVersionsReturnsOnCall(i int, result1 map[string][]ve
}{result1, result2}
}
func (fake *Model) GetHello(arg1 protocol.DeviceID) protocol.HelloIntf {
fake.getHelloMutex.Lock()
ret, specificReturn := fake.getHelloReturnsOnCall[len(fake.getHelloArgsForCall)]
fake.getHelloArgsForCall = append(fake.getHelloArgsForCall, struct {
arg1 protocol.DeviceID
}{arg1})
stub := fake.GetHelloStub
fakeReturns := fake.getHelloReturns
fake.recordInvocation("GetHello", []interface{}{arg1})
fake.getHelloMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *Model) GetHelloCallCount() int {
fake.getHelloMutex.RLock()
defer fake.getHelloMutex.RUnlock()
return len(fake.getHelloArgsForCall)
}
func (fake *Model) GetHelloCalls(stub func(protocol.DeviceID) protocol.HelloIntf) {
fake.getHelloMutex.Lock()
defer fake.getHelloMutex.Unlock()
fake.GetHelloStub = stub
}
func (fake *Model) GetHelloArgsForCall(i int) protocol.DeviceID {
fake.getHelloMutex.RLock()
defer fake.getHelloMutex.RUnlock()
argsForCall := fake.getHelloArgsForCall[i]
return argsForCall.arg1
}
func (fake *Model) GetHelloReturns(result1 protocol.HelloIntf) {
fake.getHelloMutex.Lock()
defer fake.getHelloMutex.Unlock()
fake.GetHelloStub = nil
fake.getHelloReturns = struct {
result1 protocol.HelloIntf
}{result1}
}
func (fake *Model) GetHelloReturnsOnCall(i int, result1 protocol.HelloIntf) {
fake.getHelloMutex.Lock()
defer fake.getHelloMutex.Unlock()
fake.GetHelloStub = nil
if fake.getHelloReturnsOnCall == nil {
fake.getHelloReturnsOnCall = make(map[int]struct {
result1 protocol.HelloIntf
})
}
fake.getHelloReturnsOnCall[i] = struct {
result1 protocol.HelloIntf
}{result1}
}
func (fake *Model) GetMtimeMapping(arg1 string, arg2 string) (fs.MtimeMapping, error) {
fake.getMtimeMappingMutex.Lock()
ret, specificReturn := fake.getMtimeMappingReturnsOnCall[len(fake.getMtimeMappingArgsForCall)]
@@ -2331,59 +2244,6 @@ func (fake *Model) NeedFolderFilesReturnsOnCall(i int, result1 []db.FileInfoTrun
}{result1, result2, result3, result4}
}
func (fake *Model) NumConnections() int {
fake.numConnectionsMutex.Lock()
ret, specificReturn := fake.numConnectionsReturnsOnCall[len(fake.numConnectionsArgsForCall)]
fake.numConnectionsArgsForCall = append(fake.numConnectionsArgsForCall, struct {
}{})
stub := fake.NumConnectionsStub
fakeReturns := fake.numConnectionsReturns
fake.recordInvocation("NumConnections", []interface{}{})
fake.numConnectionsMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *Model) NumConnectionsCallCount() int {
fake.numConnectionsMutex.RLock()
defer fake.numConnectionsMutex.RUnlock()
return len(fake.numConnectionsArgsForCall)
}
func (fake *Model) NumConnectionsCalls(stub func() int) {
fake.numConnectionsMutex.Lock()
defer fake.numConnectionsMutex.Unlock()
fake.NumConnectionsStub = stub
}
func (fake *Model) NumConnectionsReturns(result1 int) {
fake.numConnectionsMutex.Lock()
defer fake.numConnectionsMutex.Unlock()
fake.NumConnectionsStub = nil
fake.numConnectionsReturns = struct {
result1 int
}{result1}
}
func (fake *Model) NumConnectionsReturnsOnCall(i int, result1 int) {
fake.numConnectionsMutex.Lock()
defer fake.numConnectionsMutex.Unlock()
fake.NumConnectionsStub = nil
if fake.numConnectionsReturnsOnCall == nil {
fake.numConnectionsReturnsOnCall = make(map[int]struct {
result1 int
})
}
fake.numConnectionsReturnsOnCall[i] = struct {
result1 int
}{result1}
}
func (fake *Model) OnHello(arg1 protocol.DeviceID, arg2 net.Addr, arg3 protocol.Hello) error {
fake.onHelloMutex.Lock()
ret, specificReturn := fake.onHelloReturnsOnCall[len(fake.onHelloArgsForCall)]
@@ -3419,8 +3279,8 @@ func (fake *Model) Invocations() map[string][][]interface{} {
defer fake.clusterConfigMutex.RUnlock()
fake.completionMutex.RLock()
defer fake.completionMutex.RUnlock()
fake.connectionMutex.RLock()
defer fake.connectionMutex.RUnlock()
fake.connectedToMutex.RLock()
defer fake.connectedToMutex.RUnlock()
fake.connectionStatsMutex.RLock()
defer fake.connectionStatsMutex.RUnlock()
fake.currentFolderFileMutex.RLock()
@@ -3449,8 +3309,6 @@ func (fake *Model) Invocations() map[string][][]interface{} {
defer fake.folderStatisticsMutex.RUnlock()
fake.getFolderVersionsMutex.RLock()
defer fake.getFolderVersionsMutex.RUnlock()
fake.getHelloMutex.RLock()
defer fake.getHelloMutex.RUnlock()
fake.getMtimeMappingMutex.RLock()
defer fake.getMtimeMappingMutex.RUnlock()
fake.globalDirectoryTreeMutex.RLock()
@@ -3465,8 +3323,6 @@ func (fake *Model) Invocations() map[string][][]interface{} {
defer fake.localChangedFolderFilesMutex.RUnlock()
fake.needFolderFilesMutex.RLock()
defer fake.needFolderFilesMutex.RUnlock()
fake.numConnectionsMutex.RLock()
defer fake.numConnectionsMutex.RUnlock()
fake.onHelloMutex.RLock()
defer fake.onHelloMutex.RUnlock()
fake.overrideMutex.RLock()
+416 -236
View File
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -270,7 +270,7 @@ func TestDeviceRename(t *testing.T) {
cfg, cfgCancel := newConfigWrapper(rawCfg)
defer cfgCancel()
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
m := newModel(t, cfg, myID, nil)
if cfg.Devices()[device1].Name != "" {
t.Errorf("Device already has a name")
@@ -422,7 +422,7 @@ func TestClusterConfig(t *testing.T) {
wrapper, cancel := newConfigWrapper(cfg)
defer cancel()
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
m := newModel(t, wrapper, myID, nil)
m.ServeBackground()
defer cleanupModel(m)
@@ -903,7 +903,7 @@ func TestIssue5063(t *testing.T) {
defer cancel()
m.pmut.Lock()
for _, c := range m.conn {
for _, c := range m.connections {
conn := c.(*fakeConnection)
conn.CloseCalls(func(_ error) {})
defer m.Closed(c, errStopped) // to unblock deferred m.Stop()
@@ -1241,7 +1241,7 @@ func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) {
if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
t.Error("missing folder, or not shared", id)
}
if _, ok := m.folderRunners[id]; ok {
if _, ok := m.folderRunners.Get(id); ok {
t.Fatal("folder running?")
}
@@ -1261,7 +1261,7 @@ func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) {
t.Error("device missing")
}
if _, ok := m.folderRunners[id]; ok {
if _, ok := m.folderRunners.Get(id); ok {
t.Error("folder started")
}
}
@@ -1290,7 +1290,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
t.Error("missing folder, or not shared", id)
}
if _, ok := m.folderRunners[id]; ok {
if _, ok := m.folderRunners.Get(id); ok {
t.Fatal("folder running?")
}
@@ -1310,7 +1310,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
t.Error("device missing")
}
if _, ok := m.folderRunners[id]; ok {
if _, ok := m.folderRunners.Get(id); ok {
t.Error("folder started")
}
}
@@ -1626,7 +1626,7 @@ func TestROScanRecovery(t *testing.T) {
},
})
defer cancel()
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
m := newModel(t, cfg, myID, nil)
set := newFileSet(t, "default", m.db)
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
@@ -1673,7 +1673,7 @@ func TestRWScanRecovery(t *testing.T) {
},
})
defer cancel()
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
m := newModel(t, cfg, myID, nil)
set := newFileSet(t, "default", m.db)
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
@@ -2073,7 +2073,7 @@ func TestIssue4357(t *testing.T) {
// Create a separate wrapper not to pollute other tests.
wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
defer cancel()
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
m := newModel(t, wrapper, myID, nil)
m.ServeBackground()
defer cleanupModel(m)
@@ -2125,7 +2125,7 @@ func TestIssue4357(t *testing.T) {
}
func TestIndexesForUnknownDevicesDropped(t *testing.T) {
m := newModel(t, defaultCfgWrapper, myID, "syncthing", "dev", nil)
m := newModel(t, defaultCfgWrapper, myID, nil)
files := newFileSet(t, "default", m.db)
files.Drop(device1)
@@ -2231,7 +2231,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
t.Error("device still in config")
}
if _, ok := m.conn[device2]; ok {
if _, ok := m.deviceConnIDs[device2]; ok {
t.Error("conn not missing")
}
@@ -2374,7 +2374,7 @@ func TestCustomMarkerName(t *testing.T) {
ffs := fcfg.Filesystem(nil)
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
m := newModel(t, cfg, myID, nil)
set := newFileSet(t, "default", m.db)
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
@@ -2736,7 +2736,7 @@ func TestIssue4094(t *testing.T) {
// Create a separate wrapper not to pollute other tests.
wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
defer cancel()
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
m := newModel(t, wrapper, myID, nil)
m.ServeBackground()
defer cleanupModel(m)
@@ -2971,6 +2971,7 @@ func TestConnCloseOnRestart(t *testing.T) {
br := &testutil.BlockingRW{}
nw := &testutil.NoopRW{}
ci := &protocolmocks.ConnectionInfo{}
ci.ConnectionIDReturns(srand.String(16))
m.AddConnection(protocol.NewConnection(device1, br, nw, testutil.NoopCloser{}, m, ci, protocol.CompressionNever, nil, m.keyGen), protocol.Hello{})
m.pmut.RLock()
if len(m.closed) != 1 {
@@ -3639,6 +3640,7 @@ func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSec
})
m.AddConnection(fc1, protocol.Hello{})
m.AddConnection(fc2, protocol.Hello{})
m.promoteConnections()
// Initial CCs
select {
@@ -3690,7 +3692,7 @@ func TestIssue6961(t *testing.T) {
must(t, err)
waiter.Wait()
// Always recalc/repair when opening a fileset.
m := newModel(t, wcfg, myID, "syncthing", "dev", nil)
m := newModel(t, wcfg, myID, nil)
m.db.Close()
m.db, err = db.NewLowlevel(backend.OpenMemory(), m.evLogger, db.WithRecheckInterval(time.Millisecond))
if err != nil {
@@ -3952,7 +3954,7 @@ func TestCCFolderNotRunning(t *testing.T) {
w, fcfg, wCancel := newDefaultCfgWrapper()
defer wCancel()
tfs := fcfg.Filesystem(nil)
m := newModel(t, w, myID, "syncthing", "dev", nil)
m := newModel(t, w, myID, nil)
defer cleanupModelAndRemoveDir(m, tfs.URI())
// A connection can happen before all the folders are started.
+4 -3
View File
@@ -377,7 +377,8 @@ func TestIssue4841(t *testing.T) {
}
// Setup file from remote that was ignored locally
folder := m.folderRunners[defaultFolderConfig.ID].(*sendReceiveFolder)
runner, _ := m.folderRunners.Get(defaultFolderConfig.ID)
folder := runner.(*sendReceiveFolder)
folder.updateLocals([]protocol.FileInfo{{
Name: "foo",
Type: protocol.FileInfoTypeFile,
@@ -1213,7 +1214,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
// Initialise db with an entry and then stop everything again
must(t, tfs.Mkdir(dir1, 0o777))
m := newModel(t, w, myID, "syncthing", "dev", nil)
m := newModel(t, w, myID, nil)
defer cleanupModelAndRemoveDir(m, tfs.URI())
m.ServeBackground()
m.ScanFolders()
@@ -1222,7 +1223,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, protocol.NewKeyGenerator()).(*model),
model: NewModel(m.cfg, m.id, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model),
evCancel: m.evCancel,
stopped: make(chan struct{}),
}
+27 -9
View File
@@ -16,6 +16,8 @@ import (
"github.com/thejerf/suture/v4"
)
var errSvcNotFound = fmt.Errorf("service not found")
// A serviceMap is a utility map of arbitrary keys to a suture.Service of
// some kind, where adding and removing services ensures they are properly
// started and stopped on the given Supervisor. The serviceMap is itself a
@@ -71,23 +73,39 @@ func (s *serviceMap[K, S]) Remove(k K) (found bool) {
}
// RemoveAndWait removes the service at the given key, stopping it on the
// supervisor. If there is no service at the given key, nothing happens. The
// return value indicates whether a service was removed.
func (s *serviceMap[K, S]) RemoveAndWait(k K, timeout time.Duration) (found bool) {
// supervisor. Returns errSvcNotFound if there is no service at the given
// key, otherwise the return value from the supervisor's RemoveAndWait.
func (s *serviceMap[K, S]) RemoveAndWait(k K, timeout time.Duration) error {
return <-s.RemoveAndWaitChan(k, timeout)
}
// RemoveAndWaitChan removes the service at the given key, stopping it on
// the supervisor. The returned channel will produce precisely one error
// value: either the return value from RemoveAndWait (possibly nil), or
// errSvcNotFound if the service was not found.
func (s *serviceMap[K, S]) RemoveAndWaitChan(k K, timeout time.Duration) <-chan error {
ret := make(chan error, 1)
if tok, ok := s.tokens[k]; ok {
found = true
s.supervisor.RemoveAndWait(tok, timeout)
go func() {
ret <- s.supervisor.RemoveAndWait(tok, timeout)
}()
} else {
ret <- errSvcNotFound
}
delete(s.services, k)
delete(s.tokens, k)
return found
return ret
}
// Each calls the given function for each service in the map.
func (s *serviceMap[K, S]) Each(fn func(K, S)) {
// Each calls the given function for each service in the map. An error from
// fn will stop the iteration and be returned as-is.
func (s *serviceMap[K, S]) Each(fn func(K, S) error) error {
for key, svc := range s.services {
fn(key, svc)
if err := fn(key, svc); err != nil {
return err
}
}
return nil
}
// Suture implementation
+2 -1
View File
@@ -105,10 +105,11 @@ func TestServiceMap(t *testing.T) {
// Remove two of them from within the iterator.
sm.Each(func(k string, v *dummyService) {
sm.Each(func(k string, v *dummyService) error {
if strings.HasPrefix(k, "remove") {
sm.RemoveAndWait(k, 0)
}
return nil
})
// They should have stopped.
+5 -3
View File
@@ -39,7 +39,9 @@ func init() {
device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
device1Conn.DeviceIDReturns(device1)
device1Conn.ConnectionIDReturns(rand.String(16))
device2Conn.DeviceIDReturns(device2)
device2Conn.ConnectionIDReturns(rand.String(16))
cfg := config.New(myID)
cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
@@ -127,7 +129,7 @@ func setupModelWithConnectionFromWrapper(t testing.TB, w config.Wrapper) (*testM
func setupModel(t testing.TB, w config.Wrapper) *testModel {
t.Helper()
m := newModel(t, w, myID, "syncthing", "dev", nil)
m := newModel(t, w, myID, nil)
m.ServeBackground()
<-m.started
@@ -144,14 +146,14 @@ type testModel struct {
stopped chan struct{}
}
func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, protectedFiles []string) *testModel {
func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, protectedFiles []string) *testModel {
t.Helper()
evLogger := events.NewLogger()
ldb, err := db.NewLowlevel(backend.OpenMemory(), evLogger)
if err != nil {
t.Fatal(err)
}
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
m := NewModel(cfg, id, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
ctx, cancel := context.WithCancel(context.Background())
go evLogger.Serve(ctx)
return &testModel{
+299 -203
View File
@@ -211,9 +211,11 @@ func (FileDownloadProgressUpdateType) EnumDescriptor() ([]byte, []int) {
}
type Hello struct {
DeviceName string `protobuf:"bytes,1,opt,name=device_name,json=deviceName,proto3" json:"deviceName" xml:"deviceName"`
ClientName string `protobuf:"bytes,2,opt,name=client_name,json=clientName,proto3" json:"clientName" xml:"clientName"`
ClientVersion string `protobuf:"bytes,3,opt,name=client_version,json=clientVersion,proto3" json:"clientVersion" xml:"clientVersion"`
DeviceName string `protobuf:"bytes,1,opt,name=device_name,json=deviceName,proto3" json:"deviceName" xml:"deviceName"`
ClientName string `protobuf:"bytes,2,opt,name=client_name,json=clientName,proto3" json:"clientName" xml:"clientName"`
ClientVersion string `protobuf:"bytes,3,opt,name=client_version,json=clientVersion,proto3" json:"clientVersion" xml:"clientVersion"`
NumConnections int `protobuf:"varint,4,opt,name=num_connections,json=numConnections,proto3,casttype=int" json:"numConnections" xml:"numConnections"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp" xml:"timestamp"`
}
func (m *Hello) Reset() { *m = Hello{} }
@@ -288,7 +290,8 @@ func (m *Header) XXX_DiscardUnknown() {
var xxx_messageInfo_Header proto.InternalMessageInfo
type ClusterConfig struct {
Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"`
Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"`
Secondary bool `protobuf:"varint,2,opt,name=secondary,proto3" json:"secondary" xml:"secondary"`
}
func (m *ClusterConfig) Reset() { *m = ClusterConfig{} }
@@ -1142,205 +1145,211 @@ func init() {
func init() { proto.RegisterFile("lib/protocol/bep.proto", fileDescriptor_311ef540e10d9705) }
var fileDescriptor_311ef540e10d9705 = []byte{
// 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,
// 3251 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4b, 0x6c, 0x23, 0x47,
0x7a, 0x16, 0x9f, 0xa2, 0x4a, 0x8f, 0xa1, 0x6a, 0x5e, 0x34, 0x67, 0xac, 0x66, 0x6a, 0x67, 0x13,
0x59, 0x9b, 0x1d, 0xaf, 0xb5, 0xde, 0x8d, 0x63, 0x3b, 0x36, 0xc4, 0x87, 0x34, 0x5c, 0x6b, 0x48,
0xb9, 0xa8, 0x19, 0xaf, 0x07, 0x08, 0x88, 0x16, 0xbb, 0x44, 0x35, 0x86, 0xec, 0x66, 0xba, 0x9b,
0x7a, 0x2c, 0x72, 0x49, 0x16, 0x58, 0x2c, 0x74, 0x08, 0x82, 0x3d, 0x05, 0xc1, 0x0a, 0x59, 0xe4,
0x92, 0x5b, 0x80, 0x1c, 0x72, 0xd9, 0x53, 0x8e, 0x73, 0x1c, 0x2c, 0x10, 0x20, 0xc8, 0xa1, 0x01,
0xcf, 0x5c, 0x12, 0xe6, 0xc6, 0x63, 0x0e, 0x41, 0x50, 0x7f, 0x55, 0x57, 0x57, 0xeb, 0xe1, 0x68,
0xec, 0x43, 0x4e, 0xc3, 0xfa, 0xfe, 0xef, 0xff, 0xab, 0xba, 0xea, 0x7f, 0x55, 0x69, 0xd0, 0x9d,
0x81, 0xbd, 0xf7, 0xee, 0xc8, 0x73, 0x03, 0xb7, 0xe7, 0x0e, 0xde, 0xdd, 0x63, 0xa3, 0x87, 0x30,
0xc0, 0x85, 0x08, 0x2b, 0xcf, 0xb1, 0xe3, 0x40, 0x80, 0xe5, 0xef, 0x78, 0x6c, 0xe4, 0xfa, 0x82,
0xbe, 0x37, 0xde, 0x7f, 0xb7, 0xef, 0xf6, 0x5d, 0x18, 0xc0, 0x2f, 0x41, 0x22, 0xff, 0x93, 0x46,
0xb9, 0x47, 0x6c, 0x30, 0x70, 0x71, 0x0d, 0xcd, 0x5b, 0xec, 0xd0, 0xee, 0xb1, 0xae, 0x63, 0x0e,
0x59, 0x29, 0x55, 0x49, 0xad, 0xce, 0x55, 0xc9, 0x24, 0x34, 0x90, 0x80, 0x5b, 0xe6, 0x90, 0x4d,
0x43, 0xa3, 0x78, 0x3c, 0x1c, 0x7c, 0x48, 0x62, 0x88, 0x50, 0x4d, 0xce, 0x8d, 0xf4, 0x06, 0x36,
0x73, 0x02, 0x61, 0x24, 0x1d, 0x1b, 0x11, 0x70, 0xc2, 0x48, 0x0c, 0x11, 0xaa, 0xc9, 0x71, 0x1b,
0x2d, 0x49, 0x23, 0x87, 0xcc, 0xf3, 0x6d, 0xd7, 0x29, 0x65, 0xc0, 0xce, 0xea, 0x24, 0x34, 0x16,
0x85, 0xe4, 0xa9, 0x10, 0x4c, 0x43, 0xe3, 0xa6, 0x66, 0x4a, 0xa2, 0x84, 0x26, 0x59, 0xf8, 0x19,
0xba, 0xe1, 0x8c, 0x87, 0xdd, 0x9e, 0xeb, 0x38, 0xac, 0x17, 0xd8, 0xae, 0xe3, 0x97, 0xb2, 0x95,
0xd4, 0x6a, 0xae, 0xfa, 0xde, 0x24, 0x34, 0x96, 0x9c, 0xf1, 0xb0, 0x16, 0x4b, 0xa6, 0xa1, 0x71,
0x0b, 0x4c, 0x26, 0x61, 0xf2, 0xdf, 0xa1, 0x91, 0xb1, 0x9d, 0x80, 0x9e, 0xa3, 0xe3, 0x4f, 0xd0,
0x5c, 0x60, 0x0f, 0x99, 0x1f, 0x98, 0xc3, 0x51, 0x29, 0x57, 0x49, 0xad, 0x66, 0xaa, 0x95, 0x49,
0x68, 0xc4, 0xe0, 0x34, 0x34, 0x6e, 0x80, 0x41, 0x85, 0x10, 0x1a, 0x4b, 0xc9, 0x3f, 0xa5, 0x50,
0xfe, 0x11, 0x33, 0x2d, 0xe6, 0xe1, 0x0d, 0x94, 0x0d, 0x4e, 0x46, 0x62, 0xeb, 0x97, 0xd6, 0x6f,
0x3f, 0x8c, 0x0e, 0xf5, 0xe1, 0x63, 0xe6, 0xfb, 0x66, 0x9f, 0xed, 0x9e, 0x8c, 0x58, 0xf5, 0xce,
0x24, 0x34, 0x80, 0x36, 0x0d, 0x0d, 0x24, 0xec, 0x9e, 0x8c, 0x18, 0xa1, 0x80, 0x61, 0x0b, 0xcd,
0xf7, 0xdc, 0xe1, 0xc8, 0x63, 0x3e, 0xec, 0x5b, 0x1a, 0x2c, 0xdd, 0xbf, 0x60, 0xa9, 0x16, 0x73,
0xaa, 0x0f, 0x26, 0xa1, 0xa1, 0x2b, 0x4d, 0x43, 0x63, 0x59, 0xec, 0x69, 0x8c, 0x11, 0xaa, 0x33,
0xc8, 0xaf, 0x53, 0x68, 0xb1, 0x36, 0x18, 0xfb, 0x01, 0xf3, 0x6a, 0xae, 0xb3, 0x6f, 0xf7, 0xf1,
0x67, 0x68, 0x76, 0xdf, 0x1d, 0x58, 0xcc, 0xf3, 0x4b, 0xa9, 0x4a, 0x66, 0x75, 0x7e, 0xbd, 0x18,
0xcf, 0xb9, 0x09, 0x82, 0xaa, 0xf1, 0x22, 0x34, 0x66, 0x26, 0xa1, 0x11, 0x11, 0xa7, 0xa1, 0xb1,
0x00, 0xf3, 0x88, 0x31, 0xa1, 0x91, 0x80, 0x6f, 0xa9, 0xcf, 0x7a, 0xae, 0x63, 0x99, 0xde, 0x09,
0x7c, 0x42, 0x41, 0x6c, 0xa9, 0x02, 0xd5, 0x96, 0x2a, 0x84, 0xd0, 0x58, 0x4a, 0x7e, 0x9b, 0x45,
0x79, 0x31, 0x29, 0x7e, 0x88, 0xd2, 0xb6, 0x25, 0x7d, 0x79, 0xe5, 0x55, 0x68, 0xa4, 0x9b, 0xf5,
0x49, 0x68, 0xa4, 0x6d, 0x6b, 0x1a, 0x1a, 0x05, 0x30, 0x61, 0x5b, 0xe4, 0x57, 0x2f, 0x1f, 0xa4,
0x9b, 0x75, 0x9a, 0xb6, 0x2d, 0xfc, 0x10, 0xe5, 0x06, 0xe6, 0x1e, 0x1b, 0x48, 0xcf, 0x2d, 0x4d,
0x42, 0x43, 0x00, 0xd3, 0xd0, 0x98, 0x07, 0x3e, 0x8c, 0x08, 0x15, 0x28, 0xfe, 0x08, 0xcd, 0x79,
0xcc, 0xb4, 0xba, 0xae, 0x33, 0x38, 0x01, 0x2f, 0x2d, 0x54, 0x57, 0x26, 0xa1, 0x51, 0xe0, 0x60,
0xdb, 0x19, 0xf0, 0x95, 0x2e, 0x81, 0x5a, 0x04, 0x10, 0xaa, 0x64, 0xb8, 0x8b, 0xb0, 0xdd, 0x77,
0x5c, 0x8f, 0x75, 0x47, 0xcc, 0x1b, 0xda, 0xb0, 0xb7, 0xc2, 0x33, 0x0b, 0xd5, 0x1f, 0x4c, 0x42,
0x63, 0x59, 0x48, 0x77, 0x62, 0xe1, 0x34, 0x34, 0xee, 0x8a, 0x55, 0x9f, 0x97, 0x10, 0x7a, 0x91,
0x8d, 0x3f, 0x43, 0x8b, 0x72, 0x02, 0x8b, 0x0d, 0x58, 0xc0, 0xc0, 0x3f, 0x0b, 0xd5, 0xdf, 0x9f,
0x84, 0xc6, 0x82, 0x10, 0xd4, 0x01, 0x9f, 0x86, 0x06, 0xd6, 0xcc, 0x0a, 0x90, 0xd0, 0x04, 0x07,
0x5b, 0xe8, 0x96, 0x65, 0xfb, 0xe6, 0xde, 0x80, 0x75, 0x03, 0x36, 0x1c, 0x75, 0x6d, 0xc7, 0x62,
0xc7, 0xcc, 0x2f, 0xe5, 0xc1, 0xe6, 0xfa, 0x24, 0x34, 0xb0, 0x94, 0xef, 0xb2, 0xe1, 0xa8, 0x29,
0xa4, 0xd3, 0xd0, 0x28, 0x89, 0x84, 0x71, 0x41, 0x44, 0xe8, 0x25, 0x7c, 0xbc, 0x8e, 0xf2, 0x23,
0x73, 0xec, 0x33, 0xab, 0x34, 0x0b, 0x76, 0xcb, 0x93, 0xd0, 0x90, 0x88, 0x72, 0x18, 0x31, 0x24,
0x54, 0xe2, 0xdc, 0xf9, 0x44, 0x0a, 0xf2, 0x4b, 0xc5, 0xf3, 0xce, 0x57, 0x07, 0x41, 0xec, 0x7c,
0x92, 0xa8, 0x6c, 0x89, 0x31, 0xa1, 0x91, 0x80, 0xfc, 0x4b, 0x1e, 0xe5, 0x85, 0x12, 0xae, 0x2a,
0xe7, 0x59, 0xa8, 0xae, 0x73, 0x03, 0xff, 0x1e, 0x1a, 0x05, 0x21, 0x6b, 0xd6, 0xaf, 0x72, 0xa6,
0x5f, 0xbe, 0x7c, 0x90, 0xd2, 0x1c, 0x6a, 0x0d, 0x65, 0xb5, 0x4c, 0x08, 0xc1, 0xeb, 0x88, 0x1c,
0x28, 0x82, 0xd7, 0x81, 0xec, 0x07, 0x18, 0xfe, 0x18, 0xcd, 0x99, 0x96, 0xc5, 0x83, 0x8c, 0xf9,
0xa5, 0x4c, 0x25, 0xc3, 0x7d, 0x96, 0xfb, 0xbd, 0x02, 0xa7, 0xa1, 0xb1, 0x08, 0x5a, 0x12, 0x21,
0x34, 0x96, 0xe1, 0x3f, 0x4d, 0x86, 0x7e, 0xf6, 0x7c, 0x12, 0xf9, 0x76, 0x31, 0xcf, 0x3d, 0xbd,
0xc7, 0x3c, 0x99, 0xd7, 0x73, 0x22, 0xa0, 0xb8, 0xa7, 0x73, 0x50, 0x66, 0x75, 0xe1, 0xe9, 0x11,
0x40, 0xa8, 0x92, 0xe1, 0x2d, 0xb4, 0x30, 0x34, 0x8f, 0xbb, 0x3e, 0xfb, 0xb3, 0x31, 0x73, 0x7a,
0x0c, 0x7c, 0x26, 0x23, 0x56, 0x31, 0x34, 0x8f, 0x3b, 0x12, 0x56, 0xab, 0xd0, 0x30, 0x42, 0x75,
0x06, 0xae, 0x22, 0x64, 0x3b, 0x81, 0xe7, 0x5a, 0xe3, 0x1e, 0xf3, 0xa4, 0x8b, 0x40, 0x79, 0x89,
0x51, 0x55, 0x5e, 0x62, 0x88, 0x50, 0x4d, 0x8e, 0xfb, 0xa8, 0x00, 0xbe, 0xdb, 0xb5, 0xad, 0x52,
0xa1, 0x92, 0x5a, 0xcd, 0x56, 0xb7, 0xe5, 0xe1, 0xce, 0x82, 0x17, 0xc2, 0xd9, 0x46, 0x3f, 0xb9,
0xcf, 0x00, 0xbb, 0x69, 0xa9, 0xdd, 0x97, 0x63, 0x9e, 0x37, 0x22, 0xda, 0xdf, 0xc6, 0x3f, 0x69,
0xc4, 0xc7, 0x7f, 0x8e, 0xca, 0xfe, 0x73, 0x9b, 0x47, 0x8a, 0x98, 0x9b, 0x17, 0x8c, 0xae, 0xc7,
0x86, 0xee, 0xa1, 0x39, 0xf0, 0x4b, 0x73, 0xb0, 0xf8, 0x4f, 0x26, 0xa1, 0x51, 0xe2, 0xac, 0xa6,
0x46, 0xa2, 0x92, 0x33, 0x0d, 0x8d, 0x15, 0x91, 0xe7, 0xae, 0x20, 0x10, 0x7a, 0xa5, 0x2e, 0x3e,
0x46, 0x6f, 0x31, 0xa7, 0xe7, 0x9d, 0x8c, 0x60, 0xda, 0x91, 0xe9, 0xfb, 0x47, 0xae, 0x67, 0x75,
0x03, 0xf7, 0x39, 0x73, 0x4a, 0x08, 0x9c, 0xfa, 0xe3, 0x49, 0x68, 0xdc, 0x8d, 0x49, 0x3b, 0x92,
0xb3, 0xcb, 0x29, 0xd3, 0xd0, 0x78, 0x1b, 0xe6, 0xbe, 0x42, 0x4e, 0xe8, 0x55, 0x9a, 0xe4, 0x2f,
0x53, 0x28, 0x07, 0x9b, 0xc1, 0xa3, 0x59, 0x24, 0x75, 0x99, 0x82, 0x21, 0x9a, 0x05, 0x72, 0x21,
0xfd, 0x4b, 0x1c, 0x37, 0x50, 0x6e, 0xdf, 0x1e, 0x30, 0xbf, 0x94, 0x86, 0x58, 0xc6, 0x5a, 0x21,
0xb1, 0x07, 0xac, 0xe9, 0xec, 0xbb, 0xd5, 0x7b, 0x32, 0x9a, 0x05, 0x51, 0xc5, 0x12, 0x1f, 0x11,
0x2a, 0x40, 0xf2, 0xcb, 0x14, 0x9a, 0x87, 0x45, 0x3c, 0x19, 0x59, 0x66, 0xc0, 0xfe, 0x3f, 0x97,
0xf2, 0x8b, 0x45, 0x54, 0x88, 0x14, 0x54, 0x42, 0x48, 0x5d, 0x23, 0x21, 0xac, 0xa1, 0xac, 0x6f,
0xff, 0x8c, 0x41, 0x61, 0xc9, 0x08, 0x2e, 0x1f, 0x2b, 0x2e, 0x1f, 0x10, 0x0a, 0x18, 0xfe, 0x14,
0xa1, 0xa1, 0x6b, 0xd9, 0xfb, 0x36, 0xb3, 0xba, 0xbe, 0xde, 0x88, 0x44, 0x68, 0x47, 0x55, 0x4d,
0x85, 0x10, 0x1a, 0x4b, 0x79, 0xfe, 0x50, 0x06, 0xf6, 0x4e, 0x4a, 0x0b, 0x10, 0x19, 0x1f, 0x47,
0x91, 0xd1, 0x39, 0x70, 0xbd, 0x00, 0xc2, 0x41, 0x4d, 0x53, 0x3d, 0x51, 0xa1, 0x16, 0x43, 0x84,
0x47, 0x82, 0x24, 0x53, 0x8d, 0x8a, 0xb7, 0xd1, 0x6c, 0xd4, 0xcd, 0x71, 0xcf, 0x4f, 0x24, 0xe9,
0xa7, 0xac, 0x17, 0xb8, 0x5e, 0xb5, 0x12, 0x25, 0xe9, 0x43, 0xd5, 0xdd, 0x89, 0x80, 0x3b, 0x8c,
0xfa, 0xba, 0x48, 0x82, 0x3f, 0x44, 0x05, 0x95, 0x4c, 0x10, 0x7c, 0x2b, 0x24, 0x23, 0x3f, 0xce,
0x24, 0x4b, 0xb2, 0x41, 0x88, 0xd2, 0x88, 0x92, 0xe1, 0x9f, 0xa0, 0xfc, 0xde, 0xc0, 0xed, 0x3d,
0x8f, 0xaa, 0xc5, 0xcd, 0x78, 0x21, 0x55, 0x8e, 0xc3, 0xb9, 0xbe, 0x2d, 0xd7, 0x22, 0xa9, 0xaa,
0xfc, 0xc3, 0x90, 0x50, 0x09, 0xf3, 0x56, 0xd5, 0x3f, 0x19, 0x0e, 0x6c, 0xe7, 0x79, 0x37, 0x30,
0xbd, 0x3e, 0x0b, 0x4a, 0xcb, 0x71, 0xab, 0x2a, 0x25, 0xbb, 0x20, 0x50, 0xad, 0x6a, 0x02, 0x25,
0x34, 0xc9, 0xe2, 0x0d, 0xb4, 0x30, 0xdd, 0x3d, 0x30, 0xfd, 0x83, 0x12, 0x86, 0x38, 0x85, 0x0c,
0x27, 0xe0, 0x47, 0xa6, 0x7f, 0xa0, 0xb6, 0x3d, 0x86, 0x08, 0xd5, 0xe4, 0xbc, 0x81, 0x92, 0xb1,
0xc9, 0xac, 0xd2, 0x4d, 0x30, 0x01, 0xae, 0xa0, 0x40, 0xe5, 0x0a, 0x0a, 0x21, 0x34, 0x96, 0xe2,
0xaa, 0x6c, 0x44, 0x45, 0xfb, 0x78, 0xe7, 0xa2, 0xdb, 0x5f, 0xa3, 0x13, 0xdd, 0x44, 0xf3, 0xe7,
0xbb, 0x9a, 0x45, 0x91, 0xf1, 0x47, 0x89, 0x7e, 0x46, 0x64, 0xfc, 0x91, 0xde, 0xc9, 0xe8, 0x0c,
0xfc, 0x13, 0xcd, 0x2d, 0x1d, 0xbf, 0x34, 0x0f, 0x7d, 0xfb, 0x3b, 0xba, 0x1f, 0xb6, 0xfc, 0x0b,
0x7e, 0xd8, 0x8a, 0xfb, 0x75, 0x8d, 0x86, 0xf7, 0x91, 0xd8, 0xa5, 0x2e, 0x44, 0xd5, 0x22, 0x98,
0xda, 0x7a, 0x15, 0x1a, 0x0b, 0xd4, 0x3c, 0x82, 0xa3, 0xef, 0xd8, 0x3f, 0x63, 0x7c, 0xa3, 0xf6,
0xa2, 0x81, 0xda, 0x28, 0x85, 0x44, 0x86, 0x7f, 0xf5, 0xf2, 0x41, 0x42, 0x8d, 0xc6, 0x4a, 0xf8,
0x29, 0x2a, 0x8c, 0x06, 0x66, 0xb0, 0xef, 0x7a, 0xc3, 0xd2, 0x12, 0x38, 0xbb, 0xb6, 0x87, 0x3b,
0x52, 0x52, 0x37, 0x03, 0xb3, 0x4a, 0xa4, 0x9b, 0x29, 0xbe, 0xf2, 0xdc, 0x08, 0x20, 0x54, 0xc9,
0x70, 0x1d, 0xcd, 0x0f, 0xdc, 0x9e, 0x39, 0xe8, 0xee, 0x0f, 0xcc, 0xbe, 0x5f, 0xfa, 0x8f, 0x59,
0xd8, 0x54, 0xf0, 0x0e, 0xc0, 0x37, 0x39, 0xac, 0x36, 0x23, 0x86, 0x08, 0xd5, 0xe4, 0xf8, 0x11,
0x5a, 0x90, 0x61, 0x24, 0x7c, 0xec, 0x3f, 0x67, 0xc1, 0x43, 0xe0, 0x6c, 0xa4, 0x40, 0x7a, 0xd9,
0xb2, 0x1e, 0x7d, 0xc2, 0xcd, 0x74, 0x06, 0xfe, 0x1c, 0xdd, 0xb0, 0x1d, 0xd7, 0x62, 0xdd, 0xde,
0x81, 0xe9, 0xf4, 0x19, 0x3f, 0x9f, 0xc9, 0x2c, 0x44, 0x23, 0xf8, 0x3f, 0xc8, 0x6a, 0x20, 0x82,
0x33, 0xba, 0x29, 0xab, 0xa7, 0x86, 0x12, 0x9a, 0x64, 0xe1, 0x63, 0xa4, 0x95, 0x95, 0x6e, 0xe0,
0x99, 0xf6, 0x80, 0x79, 0xe2, 0xbc, 0xfe, 0x6b, 0x16, 0x0e, 0xec, 0xd3, 0x49, 0x68, 0xdc, 0x8e,
0x39, 0xbb, 0x82, 0x22, 0x0f, 0xeb, 0xde, 0xb9, 0x92, 0xa5, 0x49, 0x95, 0x47, 0x5c, 0xae, 0x8c,
0x7f, 0xcc, 0xbb, 0x48, 0xde, 0xe9, 0x5a, 0xb2, 0xa5, 0xbd, 0x2f, 0xfa, 0x45, 0x80, 0x54, 0x2a,
0x92, 0x63, 0x68, 0x18, 0xe1, 0x17, 0xa6, 0x68, 0xd6, 0x76, 0x0e, 0xcd, 0x81, 0x1d, 0xb5, 0xac,
0x1f, 0xbc, 0x0a, 0x0d, 0x44, 0xcd, 0xa3, 0xa6, 0x40, 0x45, 0x07, 0x01, 0x3f, 0xb5, 0x0e, 0x02,
0xc6, 0xbc, 0x83, 0xd0, 0x98, 0x34, 0xe2, 0xf1, 0xb4, 0xe2, 0xb8, 0x89, 0x5b, 0x41, 0x01, 0x4c,
0xc3, 0xb6, 0x3a, 0x6e, 0xf2, 0x46, 0x20, 0xb6, 0x35, 0x81, 0x12, 0x9a, 0x64, 0x7d, 0x98, 0xfd,
0x9b, 0xdf, 0x18, 0x33, 0xe4, 0xab, 0x14, 0x9a, 0x53, 0x29, 0x8e, 0x57, 0x17, 0x38, 0xff, 0x0c,
0x1c, 0x3f, 0x44, 0xf3, 0x81, 0x38, 0x77, 0x11, 0xcd, 0x07, 0x70, 0xe0, 0x80, 0xf1, 0xea, 0xe9,
0xee, 0xef, 0xfb, 0x2c, 0x80, 0xba, 0x95, 0x11, 0xd5, 0x53, 0x20, 0xaa, 0x7a, 0x8a, 0x21, 0xa1,
0x12, 0xc7, 0xef, 0xc9, 0xea, 0x95, 0x86, 0x63, 0x7b, 0xfb, 0xf2, 0xea, 0x15, 0x1d, 0x8a, 0x28,
0x62, 0x1f, 0xa1, 0xb9, 0x23, 0x66, 0x3e, 0x17, 0x7e, 0x29, 0x52, 0x06, 0xe4, 0x75, 0x0e, 0x4a,
0x9f, 0x14, 0xd1, 0x11, 0x01, 0x84, 0x2a, 0x99, 0xfc, 0xc6, 0x67, 0x28, 0x2f, 0xca, 0x09, 0xde,
0x41, 0x85, 0x9e, 0x3b, 0x76, 0x82, 0xf8, 0x52, 0xba, 0xac, 0x77, 0xc3, 0x20, 0xa9, 0xfe, 0x5e,
0x14, 0x80, 0x11, 0x55, 0x9d, 0x91, 0x04, 0x78, 0x1b, 0x2b, 0x45, 0xe4, 0xe7, 0x29, 0x34, 0x2b,
0x15, 0xf1, 0x23, 0x75, 0x39, 0xc8, 0x56, 0x3f, 0x38, 0x57, 0x25, 0xbf, 0xfe, 0xa2, 0xa9, 0x57,
0x48, 0x79, 0xe7, 0x3c, 0x34, 0x07, 0x63, 0xb1, 0x51, 0x59, 0x71, 0xe7, 0x04, 0x40, 0x15, 0x1d,
0x18, 0x11, 0x2a, 0x50, 0xf2, 0xf3, 0x2c, 0x5a, 0xd0, 0x93, 0x08, 0x4f, 0xd7, 0x63, 0xc7, 0x3e,
0x86, 0xc5, 0x24, 0xba, 0x94, 0x27, 0x8e, 0x7d, 0x0c, 0x69, 0xa6, 0xfc, 0x22, 0x34, 0x52, 0xfc,
0x00, 0x38, 0x4f, 0x1d, 0x00, 0x1f, 0x10, 0x0a, 0x18, 0xfe, 0x1c, 0xcd, 0x1e, 0xd9, 0x8e, 0xe5,
0x1e, 0xf9, 0xb0, 0x8c, 0x79, 0xfd, 0xe6, 0xf0, 0x85, 0x10, 0x80, 0xa5, 0x8a, 0xb4, 0x14, 0xb1,
0xd5, 0x76, 0xc9, 0x31, 0xa1, 0x91, 0x04, 0x6f, 0xa1, 0xdc, 0xc0, 0x76, 0xc6, 0xc7, 0xe0, 0x60,
0x89, 0x32, 0xfb, 0x53, 0x33, 0x08, 0x3c, 0x30, 0x77, 0x5f, 0x9a, 0x13, 0xcc, 0xf8, 0x92, 0xcd,
0x47, 0xfc, 0x92, 0xcd, 0xff, 0xc5, 0x9f, 0xa1, 0xbc, 0x65, 0x7a, 0x47, 0xb6, 0xb8, 0xd4, 0x5c,
0x61, 0x69, 0x45, 0x5a, 0x92, 0xd4, 0xf8, 0x82, 0x07, 0x43, 0x42, 0x25, 0x8e, 0x19, 0x9a, 0xdd,
0xf7, 0x18, 0xdb, 0xf3, 0x2d, 0x68, 0x92, 0xae, 0xb0, 0xf6, 0x63, 0x6e, 0x8d, 0x5f, 0x03, 0x36,
0x3d, 0xc6, 0xaa, 0x1d, 0xb8, 0x06, 0x48, 0x35, 0xf5, 0xc5, 0x72, 0x0c, 0xd7, 0x00, 0x49, 0xa3,
0x11, 0x09, 0x77, 0x51, 0xde, 0x61, 0x01, 0x9f, 0x25, 0x7f, 0xf5, 0x2c, 0xeb, 0x72, 0x96, 0x7c,
0x8b, 0x05, 0x62, 0x12, 0xa9, 0xa4, 0x56, 0x2f, 0x86, 0x7c, 0x0a, 0xc9, 0xa1, 0x92, 0x41, 0x7e,
0x91, 0x46, 0x85, 0xe8, 0x7c, 0x79, 0xf3, 0xe7, 0x1e, 0x39, 0xcc, 0xd3, 0x9f, 0xee, 0xa0, 0xe2,
0x03, 0x2a, 0xaf, 0x67, 0xa2, 0x90, 0x29, 0x84, 0xd0, 0x58, 0xca, 0x0d, 0xf4, 0x3d, 0x77, 0x3c,
0xd2, 0x9f, 0xed, 0xc0, 0x00, 0xa0, 0x09, 0x03, 0x0a, 0x21, 0x34, 0x96, 0xe2, 0x8f, 0x50, 0x66,
0x6c, 0x5b, 0x70, 0xd4, 0xb9, 0xea, 0x3b, 0xaf, 0x42, 0x23, 0xf3, 0x04, 0x22, 0x80, 0xa3, 0xd3,
0xd0, 0x98, 0x13, 0x0e, 0x67, 0x5b, 0x5a, 0xf9, 0xe4, 0x0c, 0xca, 0xe5, 0x5c, 0xb9, 0x6f, 0x5b,
0xf2, 0x4d, 0x0e, 0x94, 0xb7, 0x84, 0x72, 0x5f, 0x53, 0xee, 0x27, 0x95, 0xb7, 0xb8, 0x32, 0xc7,
0x7e, 0x9d, 0x42, 0xf3, 0x9a, 0x87, 0x7e, 0xfb, 0xbd, 0xd8, 0x46, 0x4b, 0xc2, 0x80, 0xed, 0x77,
0xe1, 0x03, 0xe5, 0x1b, 0x14, 0x3c, 0x9b, 0x80, 0xa4, 0xe9, 0x6f, 0x71, 0x5c, 0x3d, 0x9b, 0xe8,
0x20, 0xa1, 0x09, 0x0e, 0xe9, 0xa0, 0x39, 0x75, 0xe0, 0x78, 0x13, 0xe5, 0x8f, 0xf9, 0x20, 0x4a,
0x48, 0x37, 0xce, 0x79, 0x45, 0xdc, 0x76, 0x0a, 0x9a, 0x0a, 0x08, 0x18, 0x12, 0x2a, 0x61, 0xd2,
0x43, 0x39, 0xe0, 0xbf, 0xd1, 0x6d, 0x22, 0x91, 0x67, 0x16, 0xfe, 0xef, 0x3c, 0xf3, 0x17, 0x59,
0x34, 0x4b, 0x79, 0xd3, 0xec, 0x07, 0xf8, 0x47, 0x2a, 0xdb, 0xe5, 0xaa, 0xdf, 0xbd, 0x2a, 0xbd,
0xc5, 0xa7, 0x13, 0xbd, 0x7e, 0xc4, 0x97, 0xae, 0xf4, 0xb5, 0x2f, 0x5d, 0xd1, 0x27, 0x65, 0xae,
0xf1, 0x49, 0x71, 0x59, 0xca, 0xbe, 0x71, 0x59, 0xca, 0x5d, 0xbf, 0x2c, 0x45, 0x95, 0x32, 0x7f,
0x8d, 0x4a, 0xd9, 0x46, 0x4b, 0xfb, 0x9e, 0x3b, 0x84, 0x37, 0x32, 0xd7, 0x33, 0xbd, 0x13, 0xd9,
0x15, 0x40, 0xe9, 0xe6, 0x92, 0xdd, 0x48, 0xa0, 0x4a, 0x77, 0x02, 0x25, 0x34, 0xc9, 0x4a, 0xd6,
0xc4, 0xc2, 0x9b, 0xd5, 0x44, 0xfc, 0x09, 0x2a, 0x88, 0x8e, 0xd7, 0x71, 0xe1, 0xda, 0x95, 0xab,
0x7e, 0x87, 0xa7, 0x32, 0xc0, 0x5a, 0xae, 0x4a, 0x65, 0x72, 0xac, 0x3e, 0x3b, 0x22, 0x90, 0x7f,
0x4c, 0xa1, 0x02, 0x65, 0xfe, 0xc8, 0x75, 0x7c, 0xf6, 0x4d, 0x9d, 0x60, 0x0d, 0x65, 0x2d, 0x33,
0x30, 0xa5, 0xdb, 0xc1, 0xee, 0xf1, 0xb1, 0xda, 0x3d, 0x3e, 0x20, 0x14, 0x30, 0xfc, 0x29, 0xca,
0xf6, 0x5c, 0x4b, 0x1c, 0xfe, 0x92, 0x9e, 0x34, 0x1b, 0x9e, 0xe7, 0x7a, 0x35, 0xd7, 0x92, 0xd7,
0x0e, 0x4e, 0x52, 0x06, 0xf8, 0x80, 0x50, 0xc0, 0xc8, 0x3f, 0xa4, 0x50, 0xb1, 0xee, 0x1e, 0x39,
0x03, 0xd7, 0xb4, 0x76, 0x3c, 0xb7, 0xef, 0x31, 0xdf, 0xff, 0x46, 0x77, 0xff, 0x2e, 0x9a, 0x1d,
0xc3, 0xcb, 0x41, 0x74, 0xfb, 0x7f, 0x90, 0xbc, 0x06, 0x9d, 0x9f, 0x44, 0x3c, 0x33, 0xc4, 0x0f,
0x8d, 0x52, 0x59, 0xd9, 0x17, 0x63, 0x42, 0x23, 0x01, 0xf9, 0xfb, 0x0c, 0x2a, 0x5f, 0x6d, 0x08,
0x0f, 0xd1, 0xbc, 0x60, 0x76, 0xb5, 0xbf, 0x09, 0xac, 0x5e, 0x67, 0x0d, 0x70, 0x39, 0x83, 0x4b,
0xc1, 0x58, 0x8d, 0xd5, 0xa5, 0x20, 0x86, 0x08, 0xd5, 0xe4, 0x6f, 0xf4, 0x4e, 0xa9, 0x5d, 0xe5,
0x33, 0xdf, 0xfe, 0x2a, 0xdf, 0x41, 0x8b, 0xc2, 0x45, 0xa3, 0x07, 0xe5, 0x6c, 0x25, 0xb3, 0x9a,
0xab, 0x3e, 0xe4, 0xd9, 0x76, 0x4f, 0x34, 0xab, 0xd1, 0x53, 0xf2, 0x72, 0xec, 0xac, 0x02, 0x8c,
0xbc, 0xad, 0x38, 0x43, 0x13, 0x5c, 0xbc, 0x99, 0xb8, 0xe9, 0x89, 0x50, 0xff, 0x83, 0x6b, 0xde,
0xec, 0xb4, 0x9b, 0x1c, 0xc9, 0xa3, 0xec, 0x8e, 0xed, 0xf4, 0xc9, 0x47, 0x28, 0x57, 0x1b, 0xb8,
0x3e, 0x64, 0x1c, 0x8f, 0x99, 0xbe, 0xeb, 0xe8, 0xae, 0x24, 0x10, 0x75, 0xd4, 0x62, 0x48, 0xa8,
0xc4, 0xd7, 0x7e, 0x9b, 0x41, 0xf3, 0xda, 0x9f, 0x70, 0xf0, 0x9f, 0xa0, 0x7b, 0x8f, 0x1b, 0x9d,
0xce, 0xc6, 0x56, 0xa3, 0xbb, 0xfb, 0xe5, 0x4e, 0xa3, 0x5b, 0xdb, 0x7e, 0xd2, 0xd9, 0x6d, 0xd0,
0x6e, 0xad, 0xdd, 0xda, 0x6c, 0x6e, 0x15, 0x67, 0xca, 0xf7, 0x4f, 0xcf, 0x2a, 0x25, 0x4d, 0x23,
0xf9, 0xb7, 0x96, 0x3f, 0x44, 0x38, 0xa1, 0xde, 0x6c, 0xd5, 0x1b, 0x3f, 0x2d, 0xa6, 0xca, 0xb7,
0x4e, 0xcf, 0x2a, 0x45, 0x4d, 0x4b, 0x3c, 0xc1, 0xfd, 0x31, 0x7a, 0xeb, 0x22, 0xbb, 0xfb, 0x64,
0xa7, 0xbe, 0xb1, 0xdb, 0x28, 0xa6, 0xcb, 0xe5, 0xd3, 0xb3, 0xca, 0x9d, 0xf3, 0x4a, 0xd2, 0x05,
0x7f, 0x80, 0x6e, 0x25, 0x54, 0x69, 0xe3, 0xf3, 0x27, 0x8d, 0xce, 0x6e, 0x31, 0x53, 0xbe, 0x73,
0x7a, 0x56, 0xc1, 0x9a, 0x56, 0x54, 0x26, 0xd6, 0xd1, 0xed, 0x73, 0x1a, 0x9d, 0x9d, 0x76, 0xab,
0xd3, 0x28, 0x66, 0xcb, 0x77, 0x4f, 0xcf, 0x2a, 0x37, 0x13, 0x2a, 0x32, 0xab, 0xd4, 0xd0, 0x4a,
0x42, 0xa7, 0xde, 0xfe, 0xa2, 0xb5, 0xdd, 0xde, 0xa8, 0x77, 0x77, 0x68, 0x7b, 0x8b, 0x36, 0x3a,
0x9d, 0x62, 0xae, 0x6c, 0x9c, 0x9e, 0x55, 0xee, 0x69, 0xca, 0x17, 0x22, 0x7c, 0x0d, 0x2d, 0x27,
0x8c, 0xec, 0x34, 0x5b, 0x5b, 0xc5, 0x7c, 0xf9, 0xe6, 0xe9, 0x59, 0xe5, 0x86, 0xa6, 0xc7, 0xcf,
0xf2, 0xc2, 0xfe, 0xd5, 0xb6, 0xdb, 0x9d, 0x46, 0x71, 0xf6, 0xc2, 0xfe, 0xc1, 0x81, 0xaf, 0xfd,
0x5d, 0x0a, 0xe1, 0x8b, 0x7f, 0x35, 0xc3, 0x1f, 0xa0, 0x52, 0x64, 0xa4, 0xd6, 0x7e, 0xbc, 0xc3,
0xd7, 0xd9, 0x6c, 0xb7, 0xba, 0xad, 0x76, 0xab, 0x51, 0x9c, 0x49, 0xec, 0xaa, 0xa6, 0xd5, 0x72,
0x1d, 0x86, 0xdb, 0xe8, 0xee, 0x65, 0x9a, 0xdb, 0xcf, 0xde, 0x2f, 0xa6, 0xca, 0xeb, 0xa7, 0x67,
0x95, 0xdb, 0x17, 0x15, 0xb7, 0x9f, 0xbd, 0xff, 0xbb, 0xbf, 0xfa, 0xee, 0xe5, 0x82, 0x35, 0xde,
0x00, 0xe9, 0x4b, 0x7b, 0x0f, 0xdd, 0xd2, 0x0d, 0x3f, 0x6e, 0xec, 0x6e, 0xd4, 0x37, 0x76, 0x37,
0x8a, 0x33, 0xe2, 0x0c, 0x34, 0xea, 0x63, 0x16, 0x98, 0x90, 0x76, 0xbf, 0x87, 0x96, 0x13, 0x5f,
0xd1, 0x78, 0xda, 0xa0, 0x91, 0x47, 0xe9, 0xeb, 0x67, 0x87, 0xcc, 0xc3, 0xdf, 0x47, 0x58, 0x27,
0x6f, 0x6c, 0x7f, 0xb1, 0xf1, 0x65, 0xa7, 0x98, 0x2e, 0xdf, 0x3e, 0x3d, 0xab, 0x2c, 0x6b, 0xec,
0x8d, 0xc1, 0x91, 0x79, 0xe2, 0xaf, 0xfd, 0x73, 0x1a, 0x2d, 0xe8, 0xef, 0x46, 0xf8, 0xfb, 0xe8,
0xe6, 0x66, 0x73, 0x9b, 0x7b, 0xe2, 0x66, 0x5b, 0x9c, 0x00, 0x1f, 0x16, 0x67, 0xc4, 0x74, 0x3a,
0x95, 0xff, 0xc6, 0x7f, 0x84, 0x4a, 0xe7, 0xe8, 0xf5, 0x26, 0x6d, 0xd4, 0x76, 0xdb, 0xf4, 0xcb,
0x62, 0xaa, 0xfc, 0x16, 0xdf, 0x30, 0x5d, 0xa7, 0x6e, 0x7b, 0x90, 0x82, 0x4e, 0xf0, 0x27, 0xe8,
0xde, 0x39, 0xc5, 0xce, 0x97, 0x8f, 0xb7, 0x9b, 0xad, 0xcf, 0xc4, 0x7c, 0xe9, 0xf2, 0xdb, 0xa7,
0x67, 0x95, 0xbb, 0xba, 0x6e, 0x47, 0x3c, 0xc5, 0x71, 0xa8, 0x90, 0xc2, 0x8f, 0x50, 0xe5, 0x0a,
0xfd, 0x78, 0x01, 0x99, 0x32, 0x39, 0x3d, 0xab, 0xdc, 0xbf, 0xc4, 0x88, 0x5a, 0x47, 0x21, 0x85,
0x7f, 0x88, 0xee, 0x5c, 0x6e, 0x29, 0x8a, 0x8b, 0x4b, 0xf4, 0xd7, 0xfe, 0x35, 0x85, 0xe6, 0x54,
0xd5, 0xe3, 0x9b, 0xd6, 0xa0, 0xb4, 0xcd, 0x93, 0x44, 0xbd, 0xd1, 0x6d, 0xb5, 0xbb, 0x30, 0x8a,
0x36, 0x4d, 0xf1, 0x5a, 0x2e, 0xfc, 0xe4, 0x3e, 0xae, 0xd1, 0xb7, 0x1a, 0xad, 0x06, 0x6d, 0xd6,
0xa2, 0x13, 0x55, 0xec, 0x2d, 0xe6, 0x30, 0xcf, 0xee, 0xe1, 0xf7, 0xd1, 0xdd, 0xa4, 0xf1, 0xce,
0x93, 0xda, 0xa3, 0x68, 0x97, 0x60, 0x81, 0xda, 0x04, 0x9d, 0x71, 0xef, 0x00, 0x0e, 0xe6, 0x47,
0x09, 0xad, 0x66, 0xeb, 0xe9, 0xc6, 0x76, 0xb3, 0x2e, 0xb4, 0x32, 0xe5, 0xd2, 0xe9, 0x59, 0xe5,
0x96, 0xd2, 0x92, 0x0f, 0x1c, 0x5c, 0x6d, 0xed, 0x77, 0x29, 0xb4, 0xf2, 0xf5, 0xc5, 0x0b, 0x7f,
0x81, 0xde, 0x81, 0xfd, 0xba, 0x90, 0x0a, 0x64, 0xde, 0x12, 0x7b, 0xb8, 0xb1, 0xb3, 0xd3, 0x68,
0xd5, 0x8b, 0x33, 0xe5, 0xd5, 0xd3, 0xb3, 0xca, 0x83, 0xaf, 0x37, 0xb9, 0x31, 0x1a, 0x31, 0xc7,
0xba, 0xa6, 0xe1, 0xcd, 0x36, 0xdd, 0x6a, 0xec, 0x16, 0x53, 0xd7, 0x31, 0xbc, 0xe9, 0x7a, 0x7d,
0x16, 0x54, 0x1f, 0xbf, 0xf8, 0x6a, 0x65, 0xe6, 0xe5, 0x57, 0x2b, 0x33, 0x2f, 0x5e, 0xad, 0xa4,
0x5e, 0xbe, 0x5a, 0x49, 0xfd, 0xf5, 0xeb, 0x95, 0x99, 0xdf, 0xbc, 0x5e, 0x49, 0xbd, 0x7c, 0xbd,
0x32, 0xf3, 0x6f, 0xaf, 0x57, 0x66, 0x9e, 0x7d, 0xaf, 0x6f, 0x07, 0x07, 0xe3, 0xbd, 0x87, 0x3d,
0x77, 0xf8, 0xae, 0x7f, 0xe2, 0xf4, 0x82, 0x03, 0xdb, 0xe9, 0x6b, 0xbf, 0xf4, 0xff, 0xd9, 0xb1,
0x97, 0x87, 0x5f, 0x3f, 0xfc, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0x55, 0x08, 0x69, 0xf0,
0x21, 0x00, 0x00,
}
func (m *Hello) Marshal() (dAtA []byte, err error) {
@@ -1363,6 +1372,16 @@ func (m *Hello) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.Timestamp != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.Timestamp))
i--
dAtA[i] = 0x28
}
if m.NumConnections != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.NumConnections))
i--
dAtA[i] = 0x20
}
if len(m.ClientVersion) > 0 {
i -= len(m.ClientVersion)
copy(dAtA[i:], m.ClientVersion)
@@ -1440,6 +1459,16 @@ func (m *ClusterConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.Secondary {
i--
if m.Secondary {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if len(m.Folders) > 0 {
for iNdEx := len(m.Folders) - 1; iNdEx >= 0; iNdEx-- {
{
@@ -2612,6 +2641,12 @@ func (m *Hello) ProtoSize() (n int) {
if l > 0 {
n += 1 + l + sovBep(uint64(l))
}
if m.NumConnections != 0 {
n += 1 + sovBep(uint64(m.NumConnections))
}
if m.Timestamp != 0 {
n += 1 + sovBep(uint64(m.Timestamp))
}
return n
}
@@ -2642,6 +2677,9 @@ func (m *ClusterConfig) ProtoSize() (n int) {
n += 1 + l + sovBep(uint64(l))
}
}
if m.Secondary {
n += 2
}
return n
}
@@ -3258,6 +3296,44 @@ func (m *Hello) Unmarshal(dAtA []byte) error {
}
m.ClientVersion = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field NumConnections", wireType)
}
m.NumConnections = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.NumConnections |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
}
m.Timestamp = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Timestamp |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipBep(dAtA[iNdEx:])
@@ -3430,6 +3506,26 @@ func (m *ClusterConfig) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Secondary", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Secondary = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipBep(dAtA[iNdEx:])
+2 -2
View File
@@ -43,12 +43,12 @@ const (
// receives encrypted metadata and requests from the untrusted device, so it
// must decrypt those and answer requests by encrypting the data.
type encryptedModel struct {
model contextLessModel
model rawModel
folderKeys *folderKeyRegistry
keyGen *KeyGenerator
}
func newEncryptedModel(model contextLessModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
func newEncryptedModel(model rawModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
return encryptedModel{
model: model,
folderKeys: folderKeys,
+5 -10
View File
@@ -8,14 +8,6 @@ import (
"io"
)
// The HelloIntf interface is implemented by the version specific hello
// message. It knows its magic number and how to serialize itself to a byte
// buffer.
type HelloIntf interface {
Magic() uint32
Marshal() ([]byte, error)
}
var (
// ErrTooOldVersion is returned by ExchangeHello when the other side
// speaks an older, incompatible version of the protocol.
@@ -25,7 +17,10 @@ var (
ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
)
func ExchangeHello(c io.ReadWriter, h HelloIntf) (Hello, error) {
func ExchangeHello(c io.ReadWriter, h Hello) (Hello, error) {
if h.Timestamp == 0 {
panic("bug: missing timestamp in outgoing hello")
}
if err := writeHello(c, h); err != nil {
return Hello{}, err
}
@@ -80,7 +75,7 @@ func readHello(c io.Reader) (Hello, error) {
return Hello{}, ErrUnknownMagic
}
func writeHello(c io.Writer, h HelloIntf) error {
func writeHello(c io.Writer, h Hello) error {
msg, err := h.Marshal()
if err != nil {
return err
+4 -2
View File
@@ -35,10 +35,11 @@ func TestVersion14Hello(t *testing.T) {
conn := &readWriter{outBuf, inBuf}
send := &Hello{
send := Hello{
DeviceName: "this device",
ClientName: "other client",
ClientVersion: "v0.14.6",
Timestamp: 1234567890,
}
res, err := ExchangeHello(conn, send)
@@ -80,10 +81,11 @@ func TestOldHelloMsgs(t *testing.T) {
conn := &readWriter{outBuf, inBuf}
send := &Hello{
send := Hello{
DeviceName: "this device",
ClientName: "other client",
ClientVersion: "v1.0.0",
Timestamp: 1234567890,
}
_, err := ExchangeHello(conn, send)
@@ -8,6 +8,16 @@ import (
)
type mockedConnectionInfo struct {
ConnectionIDStub func() string
connectionIDMutex sync.RWMutex
connectionIDArgsForCall []struct {
}
connectionIDReturns struct {
result1 string
}
connectionIDReturnsOnCall map[int]struct {
result1 string
}
CryptoStub func() string
cryptoMutex sync.RWMutex
cryptoArgsForCall []struct {
@@ -92,6 +102,59 @@ type mockedConnectionInfo struct {
invocationsMutex sync.RWMutex
}
func (fake *mockedConnectionInfo) ConnectionID() string {
fake.connectionIDMutex.Lock()
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
}{})
stub := fake.ConnectionIDStub
fakeReturns := fake.connectionIDReturns
fake.recordInvocation("ConnectionID", []interface{}{})
fake.connectionIDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *mockedConnectionInfo) ConnectionIDCallCount() int {
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
return len(fake.connectionIDArgsForCall)
}
func (fake *mockedConnectionInfo) ConnectionIDCalls(stub func() string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = stub
}
func (fake *mockedConnectionInfo) ConnectionIDReturns(result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
fake.connectionIDReturns = struct {
result1 string
}{result1}
}
func (fake *mockedConnectionInfo) ConnectionIDReturnsOnCall(i int, result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
if fake.connectionIDReturnsOnCall == nil {
fake.connectionIDReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.connectionIDReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *mockedConnectionInfo) Crypto() string {
fake.cryptoMutex.Lock()
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
@@ -519,6 +582,8 @@ func (fake *mockedConnectionInfo) TypeReturnsOnCall(i int, result1 string) {
func (fake *mockedConnectionInfo) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
fake.cryptoMutex.RLock()
defer fake.cryptoMutex.RUnlock()
fake.establishedAtMutex.RLock()
+65
View File
@@ -31,6 +31,16 @@ type Connection struct {
clusterConfigArgsForCall []struct {
arg1 protocol.ClusterConfig
}
ConnectionIDStub func() string
connectionIDMutex sync.RWMutex
connectionIDArgsForCall []struct {
}
connectionIDReturns struct {
result1 string
}
connectionIDReturnsOnCall map[int]struct {
result1 string
}
CryptoStub func() string
cryptoMutex sync.RWMutex
cryptoArgsForCall []struct {
@@ -315,6 +325,59 @@ func (fake *Connection) ClusterConfigArgsForCall(i int) protocol.ClusterConfig {
return argsForCall.arg1
}
func (fake *Connection) ConnectionID() string {
fake.connectionIDMutex.Lock()
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
}{})
stub := fake.ConnectionIDStub
fakeReturns := fake.connectionIDReturns
fake.recordInvocation("ConnectionID", []interface{}{})
fake.connectionIDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *Connection) ConnectionIDCallCount() int {
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
return len(fake.connectionIDArgsForCall)
}
func (fake *Connection) ConnectionIDCalls(stub func() string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = stub
}
func (fake *Connection) ConnectionIDReturns(result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
fake.connectionIDReturns = struct {
result1 string
}{result1}
}
func (fake *Connection) ConnectionIDReturnsOnCall(i int, result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
if fake.connectionIDReturnsOnCall == nil {
fake.connectionIDReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.connectionIDReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *Connection) Crypto() string {
fake.cryptoMutex.Lock()
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
@@ -1162,6 +1225,8 @@ func (fake *Connection) Invocations() map[string][][]interface{} {
defer fake.closedMutex.RUnlock()
fake.clusterConfigMutex.RLock()
defer fake.clusterConfigMutex.RUnlock()
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
fake.cryptoMutex.RLock()
defer fake.cryptoMutex.RUnlock()
fake.deviceIDMutex.RLock()
+65
View File
@@ -10,6 +10,16 @@ import (
)
type ConnectionInfo struct {
ConnectionIDStub func() string
connectionIDMutex sync.RWMutex
connectionIDArgsForCall []struct {
}
connectionIDReturns struct {
result1 string
}
connectionIDReturnsOnCall map[int]struct {
result1 string
}
CryptoStub func() string
cryptoMutex sync.RWMutex
cryptoArgsForCall []struct {
@@ -94,6 +104,59 @@ type ConnectionInfo struct {
invocationsMutex sync.RWMutex
}
func (fake *ConnectionInfo) ConnectionID() string {
fake.connectionIDMutex.Lock()
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
}{})
stub := fake.ConnectionIDStub
fakeReturns := fake.connectionIDReturns
fake.recordInvocation("ConnectionID", []interface{}{})
fake.connectionIDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *ConnectionInfo) ConnectionIDCallCount() int {
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
return len(fake.connectionIDArgsForCall)
}
func (fake *ConnectionInfo) ConnectionIDCalls(stub func() string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = stub
}
func (fake *ConnectionInfo) ConnectionIDReturns(result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
fake.connectionIDReturns = struct {
result1 string
}{result1}
}
func (fake *ConnectionInfo) ConnectionIDReturnsOnCall(i int, result1 string) {
fake.connectionIDMutex.Lock()
defer fake.connectionIDMutex.Unlock()
fake.ConnectionIDStub = nil
if fake.connectionIDReturnsOnCall == nil {
fake.connectionIDReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.connectionIDReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *ConnectionInfo) Crypto() string {
fake.cryptoMutex.Lock()
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
@@ -521,6 +584,8 @@ func (fake *ConnectionInfo) TypeReturnsOnCall(i int, result1 string) {
func (fake *ConnectionInfo) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.connectionIDMutex.RLock()
defer fake.connectionIDMutex.RUnlock()
fake.cryptoMutex.RLock()
defer fake.cryptoMutex.RUnlock()
fake.establishedAtMutex.RLock()
+5 -5
View File
@@ -9,27 +9,27 @@ package protocol
import "golang.org/x/text/unicode/norm"
func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} }
func makeNative(m rawModel) rawModel { return nativeModel{m} }
type nativeModel struct {
contextLessModel
rawModel
}
func (m nativeModel) Index(folder string, files []FileInfo) error {
for i := range files {
files[i].Name = norm.NFD.String(files[i].Name)
}
return m.contextLessModel.Index(folder, files)
return m.rawModel.Index(folder, files)
}
func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error {
for i := range files {
files[i].Name = norm.NFD.String(files[i].Name)
}
return m.contextLessModel.IndexUpdate(folder, files)
return m.rawModel.IndexUpdate(folder, files)
}
func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
name = norm.NFD.String(name)
return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
return m.rawModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
}
+1 -1
View File
@@ -7,4 +7,4 @@ package protocol
// Normal Unixes uses NFC and slashes, which is the wire format.
func makeNative(m contextLessModel) contextLessModel { return m }
func makeNative(m rawModel) rawModel { return m }
+5 -5
View File
@@ -13,20 +13,20 @@ import (
"strings"
)
func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} }
func makeNative(m rawModel) rawModel { return nativeModel{m} }
type nativeModel struct {
contextLessModel
rawModel
}
func (m nativeModel) Index(folder string, files []FileInfo) error {
files = fixupFiles(files)
return m.contextLessModel.Index(folder, files)
return m.rawModel.Index(folder, files)
}
func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error {
files = fixupFiles(files)
return m.contextLessModel.IndexUpdate(folder, files)
return m.rawModel.IndexUpdate(folder, files)
}
func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
@@ -36,7 +36,7 @@ func (m nativeModel) Request(folder, name string, blockNo, size int32, offset in
}
name = filepath.FromSlash(name)
return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
return m.rawModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
}
func fixupFiles(files []FileInfo) []FileInfo {
+22 -9
View File
@@ -136,9 +136,9 @@ type Model interface {
DownloadProgress(conn Connection, folder string, updates []FileDownloadProgressUpdate) error
}
// contextLessModel is the Model interface, but without the initial
// Connection parameter. Internal use only.
type contextLessModel interface {
// rawModel is the Model interface, but without the initial Connection
// parameter. Internal use only.
type rawModel interface {
Index(folder string, files []FileInfo) error
IndexUpdate(folder string, files []FileInfo) error
Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error)
@@ -177,6 +177,7 @@ type ConnectionInfo interface {
String() string
Crypto() string
EstablishedAt() time.Time
ConnectionID() string
}
type rawConnection struct {
@@ -184,8 +185,9 @@ type rawConnection struct {
deviceID DeviceID
idString string
model contextLessModel
model rawModel
startTime time.Time
started chan struct{}
cr *countingReader
cw *countingWriter
@@ -206,6 +208,7 @@ type rawConnection struct {
closeOnce sync.Once
sendCloseOnce sync.Once
compression Compression
startStopMut sync.Mutex // start and stop must be serialized
loopWG sync.WaitGroup // Need to ensure no leftover routines in testing
}
@@ -263,7 +266,7 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer
return wc
}
func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver contextLessModel, connInfo ConnectionInfo, compress Compression) *rawConnection {
func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver rawModel, connInfo ConnectionInfo, compress Compression) *rawConnection {
idString := deviceID.String()
cr := &countingReader{Reader: reader, idString: idString}
cw := &countingWriter{Writer: writer, idString: idString}
@@ -274,6 +277,7 @@ func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, clo
deviceID: deviceID,
idString: deviceID.String(),
model: receiver,
started: make(chan struct{}),
cr: cr,
cw: cw,
closer: closer,
@@ -292,6 +296,8 @@ func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, clo
// Start creates the goroutines for sending and receiving of messages. It must
// be called exactly once after creating a connection.
func (c *rawConnection) Start() {
c.startStopMut.Lock()
defer c.startStopMut.Unlock()
c.loopWG.Add(5)
go func() {
c.readerLoop()
@@ -315,6 +321,7 @@ func (c *rawConnection) Start() {
c.loopWG.Done()
}()
c.startTime = time.Now().Truncate(time.Second)
close(c.started)
}
func (c *rawConnection) DeviceID() DeviceID {
@@ -959,10 +966,12 @@ func (c *rawConnection) Close(err error) {
// internalClose is called if there is an unexpected error during normal operation.
func (c *rawConnection) internalClose(err error) {
c.startStopMut.Lock()
defer c.startStopMut.Unlock()
c.closeOnce.Do(func() {
l.Debugln("close due to", err)
l.Debugf("close connection to %s at %s due to %v", c.deviceID.Short(), c.ConnectionInfo, err)
if cerr := c.closer.Close(); cerr != nil {
l.Debugln(c.deviceID, "failed to close underlying conn:", cerr)
l.Debugf("failed to close underlying conn %s at %s %v:", c.deviceID.Short(), c.ConnectionInfo, cerr)
}
close(c.closed)
@@ -975,7 +984,11 @@ func (c *rawConnection) internalClose(err error) {
}
c.awaitingMut.Unlock()
<-c.dispatcherLoopStopped
if !c.startTime.IsZero() {
// Wait for the dispatcher loop to exit, if it was started to
// begin with.
<-c.dispatcherLoopStopped
}
c.model.Closed(err)
})
@@ -1108,7 +1121,7 @@ func messageContext(msg message) (string, error) {
// connectionWrappingModel takes the Model interface from the model package,
// which expects the Connection as the first parameter in all methods, and
// wraps it to conform to the protocol.contextLessModel interface.
// wraps it to conform to the rawModel interface.
type connectionWrappingModel struct {
conn Connection
model Model
+16
View File
@@ -0,0 +1,16 @@
// Copyright (C) 2023 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 sliceutil
// RemoveAndZero removes the element at index i from slice s and returns the
// resulting slice. The slice ordering is preserved; the last slice element
// is zeroed before shrinking.
func RemoveAndZero[E any, S ~[]E](s S, i int) S {
copy(s[i:], s[i+1:])
s[len(s)-1] = *new(E)
return s[:len(s)-1]
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (C) 2023 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 sliceutil_test
import (
"testing"
"github.com/syncthing/syncthing/lib/sliceutil"
"golang.org/x/exp/slices"
)
func TestRemoveAndZero(t *testing.T) {
a := []int{1, 2, 3, 4, 5}
b := sliceutil.RemoveAndZero(a, 2)
exp := []int{1, 2, 4, 5}
if !slices.Equal(b, exp) {
t.Errorf("got %v, expected %v", b, exp)
}
for _, e := range a {
if e == 3 {
t.Errorf("element should have been zeroed")
}
}
}
+1 -1
View File
@@ -249,7 +249,7 @@ func (a *App) startup() error {
}
keyGen := protocol.NewKeyGenerator()
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger, keyGen)
m := model.NewModel(a.cfg, a.myID, a.ll, protectedFiles, a.evLogger, keyGen)
if a.opts.DeadlockTimeoutS > 0 {
m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second)
+6 -1
View File
@@ -30,6 +30,7 @@ import (
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/signature"
"golang.org/x/net/http2"
)
const DisabledByCompilation = false
@@ -75,8 +76,12 @@ var insecureHTTP = &http.Client{
},
}
func init() {
_ = http2.ConfigureTransport(insecureHTTP.Transport.(*http.Transport))
}
func insecureGet(url, version string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
+3 -1
View File
@@ -330,7 +330,9 @@ func clean(ctx context.Context, versionsFs fs.Filesystem, toRemove func([]string
}
if err := versionsFs.Walk(".", walkFn); err != nil {
l.Warnln("Versioner: error scanning versions dir", err)
if !errors.Is(err, context.Canceled) {
l.Warnln("Versioner: scanning versions dir:", err)
}
return err
}
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "STDISCOSRV" "1" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS
+40 -4
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "STRELAYSRV" "1" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS
@@ -37,10 +37,10 @@ strelaysrv \- Syncthing Relay Server
.nf
.ft C
strelaysrv [\-debug] [\-ext\-address=<address>] [\-global\-rate=<bytes/s>] [\-keys=<dir>] [\-listen=<listen addr>]
[\-message\-timeout=<duration>] [\-nat] [\-nat\-lease=<duration> [\-nat\-renewal=<duration>]
[\-message\-timeout=<duration>] [\-nat] [\-nat\-lease=<duration>] [\-nat\-renewal=<duration>]
[\-nat\-timeout=<duration>] [\-network\-timeout=<duration>] [\-per\-session\-rate=<bytes/s>]
[\-ping\-interval=<duration>] [\-pools=<pool addresses>] [\-protocol=<string>] [\-provided\-by=<string>]
[\-status\-srv=<listen addr>]
[\-ping\-interval=<duration>] [\-pools=<pool addresses>] [\-pprof] [\-protocol=<string>]
[\-provided\-by=<string>] [\-status\-srv=<listen addr>] [\-token=<string>] [\-version]
.ft P
.fi
.UNINDENT
@@ -132,6 +132,11 @@ a pool, thereby remaining a private relay.
.UNINDENT
.INDENT 0.0
.TP
.B \-pprof
Enable the built in profiling on the status server
.UNINDENT
.INDENT 0.0
.TP
.B \-protocol=<string>
Protocol used for listening. tcp for IPv4 and IPv6, tcp4 for IPv4, tcp6 for IPv6 (default “tcp”).
.UNINDENT
@@ -146,6 +151,16 @@ An optional description about who provides the relay.
Listen address for status service (blank to disable) (default “:22070”).
Status service is used by the relay pool server UI for displaying stats (data transferred, number of clients, etc.)
.UNINDENT
.INDENT 0.0
.TP
.B \-token=<string>
Token to restrict access to the relay (optional). Disables joining any pools.
.UNINDENT
.INDENT 0.0
.TP
.B \-version
Show version
.UNINDENT
.SS Installing
.sp
Go to \fI\%releases\fP <\fBhttps://github.com/syncthing/relaysrv/releases\fP> and
@@ -290,6 +305,27 @@ iptables \-I INPUT \-p tcp \-\-dport 22070 \-j ACCEPT
.UNINDENT
.sp
Please consult Linux distribution documentation to persist firewall rules.
.SH ACCESS CONTROL FOR PRIVATE RELAYS
.sp
New in version 1.22.1.
.sp
Private relays can be configured to only accept connections from peers in possession of a shared secret.
To configure this use the \fB\-token\fP option:
.sp
$ strelaysrv \-token=mySecretToken
.sp
Then configure your Syncthing devices to send the token when joining the relay:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
relay://<host name|IP>[:port]/?id=<relay device ID>&token=mySecretToken
.ft P
.fi
.UNINDENT
.UNINDENT
.SH SEE ALSO
.sp
\fBsyncthing\-relay(7)\fP, \fBsyncthing\-faq(7)\fP,
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-BEP" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS
+10 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH SYNOPSIS
@@ -721,6 +721,7 @@ devices when this is set to \fBtrue\fP\&. See
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<numConnections>0</numConnections>
</device>
<device id="2CYF2WQ\-AKZO2QZ\-JAKWLYD\-AGHMQUM\-BGXUOIS\-GYILW34\-HJG3DUK\-LRRYQAR" name="syno local" compression="metadata" introducer="true" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://192.0.2.1:22001</address>
@@ -732,6 +733,7 @@ devices when this is set to \fBtrue\fP\&. See
<maxRequestKiB>65536</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>8384</remoteGUIPort>
<numConnections>0</numConnections>
</device>
.ft P
.fi
@@ -924,6 +926,12 @@ device then needs an encryption password set, or must already be of the
“receive encrypted” type locally. Refer to the detailed explanation under
untrusted\&.
.UNINDENT
.INDENT 0.0
.TP
.B numConnections
The number of connections to this device. See
/advanced/device\-numconnections for more information.
.UNINDENT
.SH GUI ELEMENT
.INDENT 0.0
.INDENT 3.5
@@ -1545,6 +1553,7 @@ detailed in /advanced/option\-insecure\-allow\-old\-tls\-versions\&.
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<numConnections>0</numConnections>
</device>
<ignores>
<line>!foo2</line>
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp
+1 -1
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" "Aug 18, 2023" "v1.23.7" "Syncthing"
.TH "SYNCTHING" "1" "Sep 07, 2023" "v1.24.0" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS
+2 -1
View File
@@ -10,7 +10,7 @@ import "ext.proto";
message DeviceConfiguration {
bytes device_id = 1 [(ext.goname) = "DeviceID", (ext.xml) = "id,attr", (ext.json) = "deviceID", (ext.device_id) = true, (ext.nodefault) = true];
string name = 2 [(ext.xml) = "name,attr,omitempty"];
repeated string addresses = 3 [(ext.xml) = "address,omitempty", (ext.default) = "dynamic"];
repeated string addresses = 3 [(ext.xml) = "address,omitempty"];
protocol.Compression compression = 4 [(ext.xml) = "compression,attr"];
string cert_name = 5 [(ext.xml) = "certName,attr,omitempty"];
bool introducer = 6 [(ext.xml) = "introducer,attr"];
@@ -26,4 +26,5 @@ message DeviceConfiguration {
int32 max_request_kib = 16 [(ext.goname) = "MaxRequestKiB", (ext.xml) = "maxRequestKiB", (ext.json) = "maxRequestKiB"];
bool untrusted = 17;
int32 remote_gui_port = 18 [(ext.goname) = "RemoteGUIPort", (ext.xml) = "remoteGUIPort", (ext.json) = "remoteGUIPort"];
int32 num_connections = 19 [(ext.goname) = "RawNumConnections"]; // attempt to establish this many connections to the device
}
+7 -4
View File
@@ -8,9 +8,11 @@ import "repos/protobuf/gogoproto/gogo.proto";
// --- Pre-auth ---
message Hello {
string device_name = 1;
string client_name = 2;
string client_version = 3;
string device_name = 1;
string client_name = 2;
string client_version = 3;
int32 num_connections = 4;
int64 timestamp = 5;
}
// --- Header ---
@@ -41,7 +43,8 @@ enum MessageCompression {
// Cluster Config
message ClusterConfig {
repeated Folder folders = 1;
repeated Folder folders = 1;
bool secondary = 2;
}
message Folder {
+93 -64
View File
@@ -1,13 +1,17 @@
<configuration version="32">
<folder id="default" label="" path="s1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" introducedBy=""></device>
<device id="7PBCTLL-JJRYBSA-MOWZRKL-MSDMN4N-4US4OMX-SYEXUS4-HSBGNRY-CZXRXAT" introducedBy=""></device>
<configuration version="37">
<folder id="default" label="" path="s1?files=10000" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>fake</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>1</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
@@ -24,51 +28,21 @@
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>0</maxConcurrentWrites>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>true</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>0</maxSingleEntrySize>
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
</versioning>
<copiers>1</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>0</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>true</junctionsAsDirs>
</folder>
<device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22004</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
</device>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22001</address>
<paused>false</paused>
@@ -76,30 +50,21 @@
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<numConnections>3</numConnections>
</device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" name="s2" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22002</address>
<address>quic://127.0.0.1:22002</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
</device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22003</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
</device>
<device id="7PBCTLL-JJRYBSA-MOWZRKL-MSDMN4N-4US4OMX-SYEXUS4-HSBGNRY-CZXRXAT" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22004</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<numConnections>3</numConnections>
</device>
<gui enabled="true" tls="false" debugging="true">
<address>127.0.0.1:8081</address>
@@ -111,6 +76,7 @@
<ldap></ldap>
<options>
<listenAddress>tcp://127.0.0.1:22001</listenAddress>
<listenAddress>quic://127.0.0.1:22001</listenAddress>
<globalAnnounceServer>default</globalAnnounceServer>
<globalAnnounceEnabled>false</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled>
@@ -132,7 +98,6 @@
<urURL>https://data.syncthing.net/newdata</urURL>
<urPostInsecurely>false</urPostInsecurely>
<urInitialDelayS>1800</urInitialDelayS>
<restartOnWakeup>true</restartOnWakeup>
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH>
@@ -144,7 +109,6 @@
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
<trafficClass>0</trafficClass>
<defaultFolderPath>~</defaultFolderPath>
<setLowPriority>true</setLowPriority>
<maxFolderConcurrency>0</maxFolderConcurrency>
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
@@ -155,5 +119,70 @@
<databaseTuning>auto</databaseTuning>
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
<announceLANAddresses>true</announceLANAddresses>
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
<connectionLimitEnough>0</connectionLimitEnough>
<connectionLimitMax>0</connectionLimitMax>
<insecureAllowOldTLSVersions>false</insecureAllowOldTLSVersions>
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
<connectionPriorityRelay>50</connectionPriorityRelay>
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
</options>
<defaults>
<folder id="" label="" path="~" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>0</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>10</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>false</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>1024</maxSingleEntrySize>
<maxTotalSize>4096</maxTotalSize>
</xattrFilter>
</folder>
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>dynamic</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<numConnections>3</numConnections>
</device>
<ignores></ignores>
</defaults>
</configuration>

Some files were not shown because too many files have changed in this diff Show More