Compare commits

..

27 Commits

Author SHA1 Message Date
Jakob Borg
73c52eafb6 gui: Avoid code generating HTML (#8923) 2023-06-06 09:06:51 +02:00
Jakob Borg
be5961f59b gui: Remove HTML support in tooltips 2023-06-06 09:06:36 +02:00
Jakob Borg
e136d11dce build: Attempt cross compilation for ~all targets, allow it to fail 2023-05-09 10:01:57 +00:00
Ross Smith II
3adfe2f91f lib/fs: Fix root path handling for Windows (fixes #8778)
Co-authored-by: Jakob Borg <jakob@kastelo.net>
2023-05-09 10:01:57 +00:00
Jakob Borg
1103a27337 all: Grand test refactor (fixes #8779, fixes #8799)
This fixes various test issues with Go 1.20.

- Most tests rewritten to use fakefs where possible
- Some tests that were already skipped, or dubious (invasive,
  unmaintainable, unclear what they even tested) have been removed
- Some actual code rewritten to better support testing in fakefs

Co-authored-by: Eric P <eric@kastelo.net>
2023-05-09 10:01:57 +00:00
Alexander Seiler
ddce692f72 all: Correct various typos (#8870) 2023-05-09 08:54:02 +02:00
Syncthing Release Automation
66faea7712 gui, man, authors: Update docs, translations, and contributors 2023-05-08 03:45:35 +00:00
Anthony Goeckner
7e31ec5417 lib/model: Set platform data, incl. copying ownership, for new folders w/ NoPermissions flag (#8883)
Platform data (ownership, xattrs, etc.) is now set correctly for newly-received folders, even if the received folder has the NoPermissions flag.
2023-05-02 11:11:39 +02:00
André Colomb
a4fa764b7d gui: Add Thai (th) translation template. (#8887) 2023-05-02 06:38:29 +00:00
Jakob Borg
7226b8456b build: Produce nightly release builds 2023-05-01 09:42:44 +02:00
Syncthing Release Automation
dae5eab787 gui, man, authors: Update docs, translations, and contributors 2023-05-01 03:45:35 +00:00
K.B.Dharun Krishna
f38e9628a1 build: Bump actions version; fix Node 12 deprecation warning (#8881) 2023-04-29 15:21:46 +02:00
Jakob Borg
43e3b12e29 build: Build Debian packages 2023-04-28 13:22:25 +02:00
Jakob Borg
aa01ff5d50 build: Sign for upgrades 2023-04-28 13:03:25 +02:00
Jakob Borg
63503e0c98 build: Notarize mac builds 2023-04-28 13:03:25 +02:00
dependabot[bot]
947dd0db09 build(deps): bump github.com/quic-go/quic-go from 0.33.0 to 0.34.0 (#8877)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.33.0 to 0.34.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.33.0...v0.34.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-24 19:04:27 +02:00
Syncthing Release Automation
218b6e5193 gui, man, authors: Update docs, translations, and contributors 2023-04-24 03:45:39 +00:00
Eric P
9f131eee6b lib/ignore: Properly handle non-existing included ignore-files (fixes #8764) (#8874)
In the sequence of loading ignores, the error File Does Not Exist is not being considered a fatal  error, since the .stignore file is allowed to not exist. However, included ignore files also tossed that same error in case those do not exist while in those cases it's considered an error and it should lead to the folder stopping. Changing the error when opening an included ignore file to something other than the regular does fix this issue, as in it now works again as described in the Documentation.
2023-04-20 15:00:55 +02:00
Jakob Borg
09efe03e1d lib/connections: Avoid using nil lanChecker
Otherwise it panics when someone calls Priority() on it...
2023-04-19 10:42:25 +02:00
Syncthing Release Automation
0f87607cd5 gui, man, authors: Update docs, translations, and contributors 2023-04-17 03:45:33 +00:00
Jakob Borg
9b660c1959 lib/config, lib/connections: Configurable protocol priority (ref #8626) (#8868)
This makes the various protocol priorities configurable among the other
options. With this, it's possible to prefer QUIC over TCP for WAN
connections, for example. Both sides need to be similarly configured for
this to work properly.

The default priority order remains the same as previously (TCP, QUIC,
Relay, with LAN better than WAN).

To make this happen I made each dialer & listener more priority aware,
and moved the check for whether a connection is LAN or not into the
dialer / listener -- this is the new "lanChecker" type that's passed
around.
2023-04-16 14:54:28 +02:00
Simon Frei
c867a5f5b3 build: Upgrade recli (fixes #8503) (#8871) 2023-04-14 18:46:50 +02:00
Jakob Borg
1886b47031 build: Update dependencies (#8869) 2023-04-11 20:01:42 +02:00
Jakob Borg
f59ffc8ddd lib/model: Improve path generation for auto accepted folders (fixes #8859) (#8860)
- Make sure we don't try to use empty last path components
- Create the directory to "reserve" it once we've decided to use it
2023-04-11 13:07:22 +02:00
Evgeny Kuznetsov
61444960bc docs: fix typo (#8857) 2023-04-10 10:30:11 +00:00
Syncthing Release Automation
30bb8f2116 gui, man, authors: Update docs, translations, and contributors 2023-04-10 03:45:37 +00:00
Jakob Borg
4a8c691aef lib/syncthing: Handle successful global migration (fixes #8851) (#8852)
lib/syncthing: Handle successfull global migration (fixes #8851)
2023-04-05 15:25:55 +02:00
146 changed files with 2886 additions and 2262 deletions

View File

@@ -6,7 +6,7 @@ on:
env:
# The go version to use for builds.
GO_VERSION: "1.19.6"
GO_VERSION: "^1.20.3"
# Optimize compatibility on the slow archictures.
GO386: softfloat
@@ -42,7 +42,7 @@ jobs:
runner: ["windows-latest", "ubuntu-latest", "macos-latest"]
# The oldest version in this list should match what we have in our go.mod.
# Variables don't seem to be supported here, or we could have done something nice.
go: ["1.19"] # Skip Go 1.20 for now, https://github.com/syncthing/syncthing/issues/8799
go: ["1.19", "1.20"]
runs-on: ${{ matrix.runner }}
steps:
- name: Set git to use LF
@@ -57,7 +57,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
cache: true
@@ -84,7 +84,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -98,7 +98,7 @@ jobs:
package-windows:
name: Package for Windows
if: github.event_name == 'push' && github.ref == 'refs/heads/release'
if: github.event_name == 'push' && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-'))
environment: signing
needs:
- build-test
@@ -117,7 +117,7 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -148,7 +148,7 @@ jobs:
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages
name: packages-windows
path: syncthing-windows-*.zip
#
@@ -165,7 +165,7 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -188,7 +188,7 @@ jobs:
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages
name: packages-linux
path: syncthing-linux-*.tar.gz
#
@@ -197,7 +197,7 @@ jobs:
package-macos:
name: Package for macOS
if: github.event_name == 'push' && github.ref == 'refs/heads/release'
if: github.event_name == 'push' && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-'))
environment: signing
needs:
- build-test
@@ -207,7 +207,7 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -282,9 +282,38 @@ jobs:
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages
name: packages-macos
path: syncthing-*.zip
notarize-macos:
name: Notarize for macOS
if: github.event_name == 'push' && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-'))
environment: signing
needs:
- package-macos
runs-on: macos-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: packages-macos
- name: Notarize binaries
run: |
APPSTORECONNECT_API_KEY_PATH="$RUNNER_TEMP/apikey.p8"
echo "$APPSTORECONNECT_API_KEY" | base64 -d -o "$APPSTORECONNECT_API_KEY_PATH"
for file in syncthing-macos-*.zip ; do
xcrun notarytool submit \
-k "$APPSTORECONNECT_API_KEY_PATH" \
-d "$APPSTORECONNECT_API_KEY_ID" \
-i "$APPSTORECONNECT_API_KEY_ISSUER" \
$file
done
env:
APPSTORECONNECT_API_KEY: ${{ secrets.APPSTORECONNECT_API_KEY }}
APPSTORECONNECT_API_KEY_ID: ${{ secrets.APPSTORECONNECT_API_KEY_ID }}
APPSTORECONNECT_API_KEY_ISSUER: ${{ secrets.APPSTORECONNECT_API_KEY_ISSUER }}
#
# Cross compile other unixes
#
@@ -299,7 +328,7 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -320,9 +349,6 @@ jobs:
| grep -v js/ \
| grep -v linux/ \
| grep -v nacl/ \
| grep -v openbsd/arm\$ \
| grep -v openbsd/arm64 \
| grep -v openbsd/mips \
| grep -v plan9/ \
| grep -v windows/ \
)
@@ -330,7 +356,9 @@ jobs:
for plat in $platforms; do
goos="${plat%/*}"
goarch="${plat#*/}"
go run build.go -goos "$goos" -goarch "$goarch" tar
if ! go run build.go -goos "$goos" -goarch "$goarch" tar ; then
echo "*** $plat failed ***"
fi
done
env:
CGO_ENABLED: "0"
@@ -338,7 +366,7 @@ jobs:
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages
name: packages-other
path: syncthing-*.tar.gz
#
@@ -355,7 +383,7 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
@@ -378,5 +406,146 @@ jobs:
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages
name: packages-source
path: syncthing-source-*.tar.gz
#
# Sign binaries for auto upgrade
#
sign-for-upgrade:
name: Sign for upgrade
if: github.event_name == 'push' && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-'))
environment: signing
needs:
- package-windows
- package-linux
- package-macos
- package-cross
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/checkout@v3
with:
repository: syncthing/release-tools
path: tools
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v3
- name: Install signing tool
run: |
go install ./cmd/stsigtool
- name: Sign archives
run: |
export PRIVATE_KEY="$RUNNER_TEMP/privkey.pem"
export PATH="$PATH:$(go env GOPATH)/bin"
echo "$STSIGTOOL_PRIVATE_KEY" | base64 -d > "$PRIVATE_KEY"
mkdir packages
mv packages-*/* packages
pushd packages
"$GITHUB_WORKSPACE/tools/sign-only"
env:
STSIGTOOL_PRIVATE_KEY: ${{ secrets.STSIGTOOL_PRIVATE_KEY }}
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: packages-signed
path: packages/*
#
# Debian
#
package-debian:
name: Package for Debian
needs:
- build-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
- name: Install fpm
run: |
gem install fpm
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-debian-${{ hashFiles('**/go.sum') }}
- name: Package for Debian
run: |
for goarch in amd64 arm64 arm ; do
go run build.go -goos linux -goarch "$goarch" deb
done
env:
BUILD_USER: debian
- name: Archive artifacts
uses: actions/upload-artifact@v3
with:
name: debian-packages
path: "*.deb"
#
# Nightlies
#
publish-nightly:
name: Publish nightly build
if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release-nightly')
environment: signing
needs:
- sign-for-upgrade
- notarize-macos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
repository: syncthing/release-tools
path: tools
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: packages-signed
path: packages
- name: Create release json
run: |
cd packages
"$GITHUB_WORKSPACE/tools/generate-release-json" "$BASE_URL" > nightly.json
env:
BASE_URL: https://syncthing.ams3.digitaloceanspaces.com/nightly/
- name: Push artifacts
uses: docker://docker.io/rclone/rclone:latest
env:
RCLONE_CONFIG_SPACES_TYPE: s3
RCLONE_CONFIG_SPACES_PROVIDER: DigitalOcean
RCLONE_CONFIG_SPACES_ACCESS_KEY_ID: ${{ secrets.SPACES_KEY }}
RCLONE_CONFIG_SPACES_SECRET_ACCESS_KEY: ${{ secrets.SPACES_SECRET }}
RCLONE_CONFIG_SPACES_ENDPOINT: ams3.digitaloceanspaces.com
RCLONE_CONFIG_SPACES_ACL: public-read
with:
args: sync packages spaces:syncthing/nightly

21
.github/workflows/trigger-nightly.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Trigger nightly build & release
on:
workflow_dispatch:
schedule:
# Run nightly build at 01:00 UTC
- cron: '00 01 * * *'
jobs:
trigger-nightly:
runs-on: ubuntu-latest
name: Push to release-nightly to trigger build
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
fetch-depth: 0
- run: |
git push origin main:release-nightly

View File

@@ -10,13 +10,13 @@ jobs:
runs-on: ubuntu-latest
name: Update translations and documentation
steps:
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
- uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
- uses: actions/setup-go@v4
with:
go-version: ^1.18.4
go-version: ^1.19.6
- run: |
set -euo pipefail
git config --global user.name 'Syncthing Release Automation'

View File

@@ -36,6 +36,7 @@ Andrey D (scienmind) <scintertech@cryptolab.net> <scienmind@users.noreply.github
André Colomb (acolomb) <src@andre.colomb.de> <github.com@andre.colomb.de>
andyleap <andyleap@gmail.com>
Anjan Momi <anjan@momi.ca>
Anthony Goeckner <agoeckner@users.noreply.github.com>
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
Antony Male (canton7) <antony.male@gmail.com>
Anur <anurnomeru@163.com>
@@ -167,6 +168,7 @@ Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.gith
jtagcat <git-514635f7@jtag.cat> <git-12dbd862@jtag.cat>
Jörg Thalheim <Mic92@users.noreply.github.com>
Jędrzej Kula <kula.jedrek@gmail.com>
K.B.Dharun Krishna <kbdharunkrishna@gmail.com>
Kalle Laine <pahakalle@protonmail.com>
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
Kebin Liu <lkebin@gmail.com>

View File

@@ -15,9 +15,4 @@ EXPOSE 8080
COPY --from=builder /src/stupgrades /bin/stupgrades
ENTRYPOINT [ \
"/bin/stupgrades", \
"-f", "/nightly.json->https://build.syncthing.net/guestAuth/repository/download/Release_Nightly/.lastSuccessful/nightly.json", \
"-f", "/syncthing-macos/appcast.xml->https://build.syncthing.net/guestAuth/repository/download/SyncthingMacOS_CreateAppcastXml/.lastSuccessful/appcast.xml" \
]
ENTRYPOINT [ "/bin/stupgrades" ]

View File

@@ -1108,10 +1108,14 @@ func getBranchSuffix() string {
branch = parts[len(parts)-1]
switch branch {
case "master", "release", "main":
case "release", "main":
// these are not special
return ""
}
if strings.HasPrefix(branch, "release-") {
// release branches are not special
return ""
}
validBranchRe := regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`)
if !validBranchRe.MatchString(branch) {

View File

@@ -66,7 +66,7 @@ func generateFiles(dir string, files, maxexp int, srcname string) error {
}
func generateOneFile(fd io.ReadSeeker, p1 string, s int64) error {
src := io.LimitReader(&inifiteReader{fd}, s)
src := io.LimitReader(&infiniteReader{fd}, s)
dst, err := os.Create(p1)
if err != nil {
return err
@@ -105,11 +105,11 @@ func readRand(bs []byte) (int, error) {
return len(bs), nil
}
type inifiteReader struct {
type infiniteReader struct {
rd io.ReadSeeker
}
func (i *inifiteReader) Read(bs []byte) (int, error) {
func (i *infiniteReader) Read(bs []byte) (int, error) {
n, err := i.rd.Read(bs)
if err == io.EOF {
err = nil

36
go.mod
View File

@@ -4,7 +4,7 @@ go 1.19
require (
github.com/AudriusButkevicius/pfilter v0.0.11
github.com/AudriusButkevicius/recli v0.0.6
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
github.com/alecthomas/kong v0.7.1
github.com/calmh/incontainer v0.0.0-20221224152218-b3e71b103d7a
github.com/calmh/xdr v1.1.0
@@ -23,7 +23,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/greatroar/blobloom v0.7.2
github.com/hashicorp/golang-lru/v2 v2.0.2
github.com/jackpal/gateway v1.0.7
github.com/jackpal/gateway v1.0.10
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
@@ -39,44 +39,44 @@ require (
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quic-go/quic-go v0.33.0
github.com/quic-go/quic-go v0.34.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/sasha-s/go-deadlock v0.3.1
github.com/shirou/gopsutil/v3 v3.23.2
github.com/shirou/gopsutil/v3 v3.23.3
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.2
github.com/urfave/cli v1.22.12
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.7.0
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0
golang.org/x/sys v0.6.0
golang.org/x/text v0.8.0
golang.org/x/crypto v0.8.0
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0
golang.org/x/text v0.9.0
golang.org/x/time v0.3.0
golang.org/x/tools v0.7.0
google.golang.org/protobuf v1.29.0
golang.org/x/tools v0.8.0
google.golang.org/protobuf v1.30.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
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-20230309165930-d61513b1440d // indirect
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/onsi/ginkgo/v2 v2.9.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.2 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 // indirect
github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
)
// https://github.com/gobwas/glob/pull/55

76
go.sum
View File

@@ -1,7 +1,7 @@
github.com/AudriusButkevicius/pfilter v0.0.11 h1:6emuvqNeH1gGlqkML35pEizyPcaxdAN4JO9sdgwcx78=
github.com/AudriusButkevicius/pfilter v0.0.11/go.mod h1:4eF1UYuEhoycTlr9IOP1sb0lL9u4nfAIouRqt2xJbzM=
github.com/AudriusButkevicius/recli v0.0.6 h1:hY9KH09vIbx0fYpkvdWbvnh67uDiuJEVDGhXlefysDQ=
github.com/AudriusButkevicius/recli v0.0.6/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
@@ -51,8 +51,9 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@@ -80,8 +81,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/greatroar/blobloom v0.7.2 h1:F30MGLHOcb4zr0pwCPTcKdlTM70rEgkf+LzdUPc5ss8=
github.com/greatroar/blobloom v0.7.2/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
@@ -89,8 +90,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
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=
github.com/jackpal/gateway v1.0.7 h1:7tIFeCGmpyrMx9qvT0EgYUi7cxVW48a0mMvnIL17bPM=
github.com/jackpal/gateway v1.0.7/go.mod h1:aRcO0UFKt+MgIZmRmvOmnejdDT4Y1DNiNOsSd1AcIbA=
github.com/jackpal/gateway v1.0.10 h1:7g3fDo4Cd3RnTu6PzAfw6poO4Y81uNxrxFQFsBFSzJM=
github.com/jackpal/gateway v1.0.10/go.mod h1:+uPBgIllrbkwYCAoDkGSZbjvpre/bGYAFCYIcrH+LHs=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
@@ -127,20 +128,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.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
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.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 h1:mDrFjGWmndQXmVx3giRScTbkltpPcnGEWG1GorsuiJ4=
github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU=
github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -159,12 +160,12 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU=
github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
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=
@@ -172,12 +173,15 @@ 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.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -206,15 +210,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -226,8 +230,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
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=
@@ -261,17 +265,17 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -280,8 +284,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
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=
@@ -295,8 +299,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

View File

@@ -185,7 +185,7 @@ td input[type="checkbox"] {
}
/* Wrap long file paths to prevent text overflow. See issue #6268. */
.file-path {
.word-break-all {
word-break: break-all;
}

View File

@@ -143,9 +143,9 @@
"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.": "للمجلدات التالية، حدث خطأ قبل بدء مشاهدة التغييرات. ستتم إعادة المحاولة كل دقيقة، نظرًا لذلك قد تختفي الأخطاء قريبًا. لكن إذا استمرت، فحاول حل المشكلة واطلب المساعدة إذا لم تستطع حل المشكلة.",
"Full Rescan Interval (s)": "مدة أعاده الفحص الكامل (ثانية)",
"GUI": "واجهة المستخدم الرسومية",
"GUI Authentication Password": "كلمة السر ",
"GUI Authentication Password": "كلمة الس",
"GUI Authentication User": "أسم المستخدم لدخول واجهة الرسومية",
"GUI Listen Address": "واجهة الرسومية الاستماع الى العنوان",
"GUI Listen Address": "واجهة الرسومية الاستماع الى العنوان",
"GUI Theme": "شكل الواجه",
"General": "عام",
"Generate": "توليد",
@@ -161,7 +161,7 @@
"Ignored Devices": "الأجهزة المتجاهلة",
"Ignored Folders": "المجلدات المتجاهلة",
"Ignored at": "تجاهل عند",
"Incoming Rate Limit (KiB/s)": "الحد الأقصى البيانات الواردة (KiB/s)",
"Incoming Rate Limit (KiB/s)": "الحد الأقصى البيانات الواردة (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "الإعدادات الغير صحيحه قد تدمر بيانات المجلد وتجعل المزامنة غير صالحه للعمل",
"Introduced By": "عرف بواسطة",
"Introducer": "المعرف",
@@ -325,7 +325,7 @@
"Watch for Changes": "راقب التغييرات",
"Watching for Changes": "جاري مراقبة التغيرات",
"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.": "عند إضافة مجلد جديد ، ضع في الاعتبار أن معرف المجلد يُستخدم لربط المجلدات معًا بين الأجهزة المختلفة. وهي حساسة لحالة الأحرف لذا يجب أن تتطابق تمامًا بين جميع الأجهزة.",
"Yes": "نعم",
"You can also select one of these nearby devices:": "يمكنك أيضا اختيار واحد من الأجهزة القريبة ",
@@ -334,7 +334,7 @@
"You have no ignored devices.": "لا يوجد أجهزة في قائمة التجاهل ",
"You have no ignored folders.": "لا يوجد مجلدات في قائمه التجاهل ",
"You have unsaved changes. Do you really want to discard them?": "الإعدادات لم تحفظ. هل انت متأكد من الإلغاء؟ ",
"You must keep at least one version.": "يجب الاحتفاظ بنسخة واحده على الاقل",
"You must keep at least one version.": "يجب الاحتفاظ بنسخة واحده على الاقل",
"days": "أيام",
"directories": "مجلدات",
"files": "ملفات",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Използва съобщения от файловата система, за да открива променени елементи.",
"User Home": "Папка на потребителя",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Няма зададени потребителско име и парола за достъп до графичния интерфейс. Помислете за създаването им.",
"Using a QUIC connection over LAN": "Използване на връзка чрез QUIC в LAN",
"Using a QUIC connection over WAN": "Използване на връзка чрез QUIC в WAN",
"Using a direct TCP connection over LAN": "Използване на директна свързаност с TCP през местна мрежа",
"Using a direct TCP connection over WAN": "Използване на директна свързаност с TCP през широкодостъпна мрежа",
"Version": "Издание",

View File

@@ -3,16 +3,18 @@
"A negative number of days doesn't make sense.": "Un nombre negatiu de dies no té sentit.",
"A new major version may not be compatible with previous versions.": "Una nova versió major pot ser incompatible amb versions anteriors.",
"API Key": "Clau API",
"About": "Sobre",
"About": "Quant a",
"Action": "Acció",
"Actions": "Accions",
"Add": "Afegir",
"Add Device": "Afegir dispositiu",
"Add Folder": "Afegir carpeta",
"Add Remote Device": "Afegir dispositiu remot",
"Add devices from the introducer to our device list, for mutually shared folders.": "Afegir dispositius des d'un introductor a la llista de dispositius per una compartició de carpetes mútua",
"Active filter rules": "Regles de filtre actiu",
"Add": "Afegeix",
"Add Device": "Afegeix un dispositiu",
"Add Folder": "Afegeix carpeta",
"Add Remote Device": "Afegeix dispositiu remot",
"Add devices from the introducer to our device list, for mutually shared folders.": "Afegiu dispositius de l'introductor a la nostra llista de dispositius, per a carpetes compartides mútuament.",
"Add filter entry": "Afegeix una entrada de filtre",
"Add ignore patterns": "Afegiu patrons per ignorar",
"Add new folder?": "Afegir nova carpeta?",
"Add new folder?": "Vols afegir una carpeta nova?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "A més, s'augmentarà l'interval de reexploració complet (vegades 60, és a dir, el nou predeterminat d'1 h). També podeu configurar-lo manualment per a cada carpeta més tard després de triar No.",
"Address": "Adreça",
"Addresses": "Adreces",
@@ -20,63 +22,143 @@
"Advanced Configuration": "Configuració Avançada",
"All Data": "Totes les dades",
"All Time": "Tot el temps",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Totes les carpetes compartides amb aquest dispositiu han d'estar protegides amb una contrasenya, de manera que totes les dades enviades no es puguin llegir sense la contrasenya proporcionada.",
"Allow Anonymous Usage Reporting?": "Permetre l'enviament anònim d'informes d'ús?",
"Allowed Networks": "Xarxes permeses",
"Alphabetic": "Alfabètic",
"Altered by ignoring deletes.": "S'ha alterat ignorant les supressions.",
"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.": "Una ordre externa gestiona la versió. Ha d'eliminar el fitxer de la carpeta compartida. Si el camí a l'aplicació conté espais, s'ha de citar.",
"Anonymous Usage Reporting": "Informe anònim d'ús",
"Anonymous usage report format has changed. Would you like to move to the new format?": "El format de l'informe d'ús anònim ha canviat. Vols canviar a aquest nou format?",
"Apply": "Aplica",
"Are you sure you want to override all remote changes?": "Esteu segur que voleu anul·lar tots els canvis remots?",
"Are you sure you want to permanently delete all these files?": "Estàs segur que vols esborrar tots aquests fitxers permanentment?",
"Are you sure you want to remove device {%name%}?": "Estàs segur que vols esborrar el dispositiu {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Estàs segur que vols esborrar la carpeta {{label}}?",
"Are you sure you want to restore {%count%} files?": "Estàs segur que vols restaurar {{count}} fitxers?",
"Are you sure you want to revert all local changes?": "Esteu segur que voleu revertir tots els canvis locals?",
"Are you sure you want to upgrade?": "Esteu segur que voleu actualitzar?",
"Authors": "Autors",
"Auto Accept": "Auto Acceptar",
"Automatic Crash Reporting": "Informe automàtic d'incidències",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "L'actualització automàtica permet escollir entre les versions estables i les versions candidates.",
"Automatic upgrades": "Actualitzacions automàtiques",
"Automatic upgrades are always enabled for candidate releases.": "Les actualitzacions automàtiques sempre estan habilitades per a les versions candidates.",
"Automatically create or share folders that this device advertises at the default path.": "Creeu o compartiu automàticament les carpetes que aquest dispositiu anuncia al camí predeterminat.",
"Available debug logging facilities:": "Recursos disponibles per registrar la depuració:",
"Be careful!": "Ves amb compte!",
"Bugs": "Bugs",
"Body:": "Cos de text:",
"Bugs": "Errors (Bugs)",
"Cancel": "Cancel·la",
"Changelog": "Historial de canvis",
"Clean out after": "Netejar després",
"Cleaning Versions": "Netejant versions",
"Cleanup Interval": "Interval de neteja",
"Click to see full identification string and QR code.": "Feu clic per veure la cadena d'identificació completa i el codi QR.",
"Close": "Tancar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentari quan és usat al principi d'una línia",
"Compression": "Compressió",
"Configuration Directory": "Directori de configuració",
"Configuration File": "Fitxer de configuració",
"Configured": "Configurat",
"Connected (Unused)": "Connectat (no utilitzat)",
"Connection Error": "Error de connexió",
"Connection Type": "Tipus de connexió",
"Connections": "Connexions",
"Connections via relays might be rate limited by the relay": "Les connexions mitjançant relés poden estar limitades pel relé",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "La vigilància contínua dels canvis ara està disponible a Syncthing. Això detectarà els canvis al disc i emetrà una exploració només als camins modificats. Els avantatges són que els canvis es propaguen més ràpidament i que es requereixen menys exploracions completes.",
"Copied from elsewhere": "Copiat d'un altre lloc",
"Copied from original": "Copiat de l'original",
"Copied!": "Copiat!",
"Copy": "Copia",
"Copy failed! Try to select and copy manually.": "La còpia ha fallat! Intenta seleccionar i copiar manualment.",
"Currently Shared With Devices": "Actualment compartit amb dispositius",
"Custom Range": "Interval personalitzat",
"Danger!": "Perill!",
"Debugging Facilities": "Recursos de depuració:",
"Database Location": "Ubicació de la base de dades",
"Debugging Facilities": "Utilitats de Depuració",
"Default": "Per defecte",
"Default Configuration": "Configuració predeterminada",
"Default Device": "Dispositiu predeterminat",
"Default Folder": "Carpeta per defecte",
"Default Ignore Patterns": "Ignora els patrons per defecte",
"Defaults": "Per defecte",
"Delete": "Esborrar",
"Delete Unexpected Items": "Suprimeix els elements inesperats",
"Deleted {%file%}": "S'ha suprimit {{file}}",
"Deselect All": "Deselecciona tot",
"Deselect devices to stop sharing this folder with.": "Desseleccioneu els dispositius amb els quals deixar de compartir aquesta carpeta.",
"Deselect folders to stop sharing with this device.": "Desseleccioneu les carpetes per deixar de compartir-les amb aquest dispositiu.",
"Device": "Dispositiu",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositiu \"{{name}}\" ({{device}} a {{address}}) vol connectar-se. Vols afegir un dispositiu nou?",
"Device Certificate": "Certificat del dispositiu",
"Device ID": "ID del dispositiu",
"Device Identification": "Identificació del dispositiu",
"Device Name": "Nom del dispositiu",
"Device is untrusted, enter encryption password": "El dispositiu no és de confiança, introduïu la contrasenya d'encriptació",
"Device rate limits": "Límits de velocitat del dispositiu",
"Device that last modified the item": "Dispositiu que ha modificat el fitxer per última vegada",
"Devices": "Dispositius",
"Disable Crash Reporting": "Desactiva els informes d'error",
"Disabled": "Deshabilitat",
"Disabled periodic scanning and disabled watching for changes": "S'ha desactivat l'exploració periòdica i la vigilància dels canvis",
"Disabled periodic scanning and enabled watching for changes": "S'ha desactivat l'exploració periòdica i s'ha activat la vigilància dels canvis",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "S'ha desactivat l'exploració periòdica i s'ha produït un error en configurar l'observació dels canvis, tornant-ho a provar cada 1 minut:",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desactiva la comparació i la sincronització de permisos de fitxers. Útil en sistemes amb permisos inexistents o personalitzats (per exemple, FAT, exFAT, Synology, Android).",
"Discard": "Descarta",
"Disconnected": "Desconnectat",
"Disconnected (Inactive)": "Desconnectat (inactiu)",
"Disconnected (Unused)": "Desconnectat (no utilitzat)",
"Discovered": "Descobert",
"Discovery": "Descobriment",
"Discovery Failures": "Falles de descobriment",
"Discovery Status": "Estat de descoberta",
"Dismiss": "Descarta",
"Do not add it to the ignore list, so this notification may recur.": "No l'afegiu a la llista d'ignorar, de manera que aquesta notificació pot repetir-se.",
"Do not restore": "No restaurar",
"Do not restore all": "No restaurar-ho tot",
"Do you want to enable watching for changes for all your folders?": "Vols activar la cerca de canvis a totes les teves carpetes?",
"Documentation": "Documentació",
"Download Rate": "Tasca de descarrega",
"Downloaded": "Descarregat",
"Downloading": "Descarregant",
"Edit": "Editar",
"Edit Device": "Editar dispositiu",
"Edit Device Defaults": "Edita els valors predeterminats del dispositiu",
"Edit Folder": "Modificar carpeta",
"Editing {%path%}.": "Modificant {{path}}",
"Edit Folder Defaults": "Edita els valors per defecte de la carpeta",
"Editing {%path%}.": "S'està editant {{path}}.",
"Enable Crash Reporting": "Activa els informes d'error",
"Enable NAT traversal": "Habilita NAT transversal",
"Enable Relaying": "Activa la retransmissió",
"Enabled": "Habilitat",
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Permet enviar atributs ampliats a altres dispositius i aplicar atributs ampliats entrants. Pot requerir l'execució amb privilegis elevats.",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Permet enviar atributs ampliats a altres dispositius, però no aplicar els atributs ampliats entrants. Això pot tenir un impacte significatiu en el rendiment. Sempre activat quan \"Sincronitza els atributs ampliats\" està habilitat.",
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Permet enviar informació de propietat a altres dispositius i aplicar la informació de propietat entrant. Normalment requereix córrer amb privilegis elevats.",
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "Permet enviar informació de propietat a altres dispositius, però no aplicar la informació de propietat entrant. Això pot tenir un impacte significatiu en el rendiment. Sempre activat quan la \"Propietat de sincronització\" està activada.",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduïu un nombre no negatiu (per exemple \"2,35\") i seleccioneu una unitat. Els percentatges formen part de la mida total del disc.",
"Enter a non-privileged port number (1024 - 65535).": "Introduïu un número de port no privilegiat (1024 - 65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduïu adreces separades per comes (\"tcp://ip:port\", \"tcp://host:port\") o \"dinàmiques\" per fer la descoberta automàtica de l'adreça.",
"Enter ignore patterns, one per line.": "Introduex patrons a ignorar, un per línia.",
"Enter up to three octal digits.": "Introduïu fins a tres dígits octals.",
"Error": "Error",
"Extended Attributes": "Atributs ampliats",
"Extended Attributes Filter": "Filtre d'atributs estès",
"External": "Extern",
"External File Versioning": "Versionat de fitxers extern",
"Failed Items": "Elements fallats",
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
"Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.",
"Failed to setup, retrying": "No s'ha pogut configurar, s'està tornant a provar",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "S'espera que no es pugui connectar als servidors IPv6 si no hi ha connectivitat IPv6.",
"File Pull Order": "Ordre d'agafar fitxers",
"File Versioning": "Versionat de Fitxers",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Els fitxers es mouen al directori .stversions quan se substitueixen o se suprimeixen mitjançant Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Els fitxers es mouen a versions amb segell de data en un directori .stversions quan Syncthing se substitueix o se suprimeix.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Els fitxers estan protegits de canvis fets per altres dispositius, però els canvis fets en aquest dispositiu seran enviats a la resta del cluster.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Els fitxers se sincronitzen des del clúster, però els canvis fets localment no s'enviaran a altres dispositius.",
"Filesystem Watcher Errors": "Errors de l'observador del sistema de fitxers",
"Filter by date": "Filtrar per data",
"Filter by name": "Filtrar per nom",
"Folder": "Carpeta",
@@ -84,38 +166,84 @@
"Folder Label": "Etiqueta de la carpeta",
"Folder Path": "Camí de carpeta",
"Folder Type": "Tipus de carpeta",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "El tipus de carpeta \"{{receiveEncrypted}}\" només es pot definir quan s'afegeix una carpeta nova.",
"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.": "El tipus de carpeta \"{{receiveEncrypted}}\" no es pot canviar després d'afegir la carpeta. Heu d'eliminar la carpeta, suprimir o desxifrar les dades del disc i tornar a afegir la carpeta.",
"Folders": "Carpetes",
"GUI": "GUI",
"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.": "A les carpetes següents s'ha produït un error en començar a buscar canvis. Es tornarà a provar cada minut, de manera que els errors poden desaparèixer aviat. Si persisteixen, intenteu solucionar el problema subjacent i demaneu ajuda si no podeu.",
"Forever": "Per sempre",
"Full Rescan Interval (s)": "Interval(s) de reexploració completa",
"GUI": "GUI (Interfície Gràfica d'Usuari)",
"GUI / API HTTPS Certificate": "Certificat HTTPS GUI / API",
"GUI Authentication Password": "Contrasenya d'autenticació GUI",
"GUI Authentication User": "Usuari d'autenticació GUI",
"GUI Authentication: Set User and Password": "Autenticació de la GUI: defineix l'usuari i la contrasenya",
"GUI Listen Address": "Adreça d'escolta de la GUI",
"GUI Override Directory": "Directori de substitució de la GUI",
"GUI Theme": "Tema de la GUI",
"General": "General",
"Generate": "Generar",
"Global Discovery": "Descobriment Global",
"Global Discovery Servers": "Servidors de Descobriment Global",
"Global State": "Estat global",
"Help": "Ajuda",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Suggeriment: només s'han detectat regles de denegació mentre el valor predeterminat és denegació. Penseu a afegir \"permet qualsevol\" com a darrera regla.",
"Home page": "Pàgina d'inici",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Tanmateix, la vostra configuració actual indica que potser no voleu que estigui activada. Hem desactivat l'informe automàtic d'errors.",
"Identification": "Identificació",
"If untrusted, enter encryption password": "Si no és de confiança, introduïu la contrasenya de xifratge",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si voleu evitar que altres usuaris d'aquest ordinador accedeixin a Syncthing i a través d'ell els vostres fitxers, penseu a configurar l'autenticació.",
"Ignore": "Ignorar",
"Ignore Patterns": "Patrons d'ignoració",
"Ignore Permissions": "Ignora Permisos",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Els patrons per ignorar només es poden afegir després de crear la carpeta. Si està marcat, un camp d'entrada per introduir patrons d'ignorar es presentarà després de desar.",
"Ignored Devices": "Dispositius ignorats",
"Ignored Folders": "Carpetes ignorades",
"Ignored at": "Ignorat a",
"Included Software": "Programari inclòs",
"Incoming Rate Limit (KiB/s)": "Límit de velocitat d'entrada (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configuració incorrecta pot malmetre els continguts de la teva carpeta i que Syncthing esdevingui inoperatiu.",
"Internally used paths:": "Camins utilitzats internament:",
"Introduced By": "Introduït per",
"Introducer": "Introductor",
"Inversion of the given condition (i.e. do not exclude)": "Inversió del patrò introduït",
"Keep Versions": "Mantenir Versions",
"LDAP": "LDAP",
"Largest First": "Més gran primer",
"Last 30 Days": "Últims 30 dies",
"Last 7 Days": "Últims 7 dies",
"Last Month": "Últim mes",
"Last Scan": "Últim escaneig",
"Last seen": "Vist per última vegada",
"Latest Change": "Últim canvi",
"Learn more": "Aprèn més",
"Learn more at {%url%}": "Més informació a {{url}}",
"Limit": "Límit",
"Listener Failures": "Errors en l'escolta",
"Listener Status": "Estat de l'oient",
"Listeners": "Oients",
"Loading data...": "Carregant dades...",
"Loading...": "Carregant...",
"Local Additions": "Addicions locals",
"Local Discovery": "Descobriment Local",
"Local State": "Estat local",
"Local State (Total)": "Estat local (Total)",
"Locally Changed Items": "Elements canviats localment",
"Log": "Registre",
"Log File": "Fitxer de registre",
"Log tailing paused. Scroll to the bottom to continue.": "S'ha posat en pausa el seguiment del registre. Desplaceu-vos cap a la part inferior per continuar.",
"Logs": "Registres",
"Major Upgrade": "Actualització major",
"Mass actions": "Accions massives",
"Maximum Age": "Antiguitat Màxima",
"Maximum single entry size": "Mida màxima d'entrada única",
"Maximum total size": "Mida total màxima",
"Metadata Only": "Només metadades",
"Minimum Free Disk Space": "Espai de disc lliure mínim",
"Mod. Device": "Mod. Dispositiu",
"Mod. Time": "Mod. Data",
"More than a month ago": "Fa més d'un mes",
"More than a week ago": "Fa més d'una setmana",
"More than a year ago": "Fa més d'un any",
"Move to top of queue": "Moure al primer de la cua",
"Multi level wildcard (matches multiple directory levels)": "Caràcter comodí de nivell múltiple (aparella en carpetes de nivells múltiples)",
"Never": "Mai",
@@ -124,76 +252,174 @@
"Newest First": "Més nou primer",
"No": "No",
"No File Versioning": "Sense Versionat de Fitxer",
"No files will be deleted as a result of this operation.": "No se suprimirà cap fitxer com a resultat d'aquesta operació.",
"No rules set": "No hi ha regles establertes",
"No upgrades": "No hi ha actualitzacions",
"Not shared": "No compartit",
"Notice": "Avís",
"OK": "OK",
"OK": "D'acord",
"Off": "Desactivar",
"Oldest First": "Més antic primer",
"Optional descriptive label for the folder. Can be different on each device.": "Etiqueta descriptiva opcional per a la carpeta. Pot ser diferent a cada dispositiu.",
"Options": "Opcions",
"Out of Sync": "Fora de sincronia",
"Out of Sync Items": "Arxius encara no sincronitzats",
"Outgoing Rate Limit (KiB/s)": "Límit de velocitat de sortida (KiB/s)",
"Override": "Sobreescriu",
"Override Changes": "Sobreescriure Canvis",
"Ownership": "Propietat",
"Path": "Ruta",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Ruta de la carpeta a l'equip local. Si no existeix serà creada. El caràcter (~) es pot fer servir com a drecera de",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Ruta on s'han d'emmagatzemar les versions (deixeu buit per al directori predeterminat .stversions a la carpeta compartida).",
"Paths": "Rutes",
"Pause": "Pausa",
"Pause All": "Posa-ho tot en pausa",
"Paused": "Pausat",
"Paused (Unused)": "En pausa (no utilitzat)",
"Pending changes": "Canvis pendents",
"Periodic scanning at given interval and disabled watching for changes": "Escaneig periòdic a un interval determinat i vigilància desactivada dels canvis",
"Periodic scanning at given interval and enabled watching for changes": "Escaneig periòdic a un interval determinat i vigilància activada dels canvis",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "L'escaneig periòdic determinat a un interval ha fallat i no s'ha pogut configurar l'observació dels canvis, tornant-ho a provar cada 1 minut:",
"Permanently add it to the ignore list, suppressing further notifications.": "Afegeix-lo permanentment a la llista d'ignorar, suprimint més notificacions.",
"Please consult the release notes before performing a major upgrade.": "Si us plau consulta les notes de llançament abans de realitzar una actualització major.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Si us plau, estableix un usuari i contrasenya al GUI a través del quadre de diàleg de configuració.",
"Please wait": "Si-us-plau espera",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix que indica que el fitxer es pot suprimir si s'impedeix l'eliminació del directori",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix que indica que el patró s'ha de fer coincidir sense distinció de majúscules i minúscules",
"Preparing to Sync": "S'està preparant per a la sincronització",
"Preview": "Vista prèvia",
"Preview Usage Report": "Vista Prèvia de l'Informe d'Ús",
"QR code": "Codi QR",
"QUIC LAN": "Connexió QUIC LAN",
"QUIC WAN": "Connexió QUIC WAN",
"QUIC connections are in most cases considered suboptimal": "En la majoria dels casos, les connexions QUIC es consideren subòptimes",
"Quick guide to supported patterns": "Guia ràpida per als possibles patrons",
"Random": "Aleatori",
"Receive Encrypted": "Rebre xifrat",
"Receive Only": "Només rebre",
"Received data is already encrypted": "Les dades rebudes ja estan xifrades",
"Recent Changes": "Canvis recents",
"Reduced by ignore patterns": "Reduït per ignorar patrons",
"Relay LAN": "Relé LAN",
"Relay WAN": "Relé WAN",
"Release Notes": "Notes de llançament",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Els candidats a la versió final contenen les últimes funcions i correccions. Són similars a les versions tradicionals de Syncthing quinzenals.",
"Remote Devices": "Dispositius remots",
"Remote GUI": "GUI remota",
"Remove": "Esborrar",
"Remove Device": "Elimina el dispositiu",
"Remove Folder": "Elimina la carpeta",
"Required identifier for the folder. Must be the same on all cluster devices.": "Identificador obligatori per a la carpeta. Ha de ser el mateix en tots els dispositius del clúster.",
"Rescan": "Re-escanejar",
"Rescan All": "Re-escanejar tot",
"Rescans": "Escaneja de nou",
"Restart": "Reiniciar",
"Restart Needed": "És Necessari Reiniciar",
"Restarting": "Reiniciant",
"Restore": "Restaura",
"Restore Versions": "Restaura versions",
"Resume": "Reprendre",
"Resume All": "Reprèn tot",
"Reused": "Reutilitzat",
"Revert": "Reverteix",
"Revert Local Changes": "Reverteix els canvis locals",
"Save": "Guardar",
"Scan Time Remaining": "Temps d'escanejat restant",
"Scanning": "Escanejant",
"See external versioning help for supported templated command line parameters.": "Consulteu l'ajuda de versions externa per als paràmetres de línia d'ordres de plantilla compatibles.",
"Select All": "Selecciona tot",
"Select a version": "Seleccioneu una versió",
"Select additional devices to share this folder with.": "Seleccioneu dispositius addicionals amb els quals compartir aquesta carpeta.",
"Select additional folders to share with this device.": "Seleccioneu carpetes addicionals per compartir amb aquest dispositiu.",
"Select latest version": "Seleccioneu la darrera versió",
"Select oldest version": "Seleccioneu la versió més antiga",
"Send & Receive": "Enviar i rebre",
"Send Extended Attributes": "Envia atributs ampliats",
"Send Only": "Només enviar",
"Send Ownership": "Envia la propietat",
"Set Ignores on Added Folder": "Estableix filtres per ignorar a la carpeta afegida",
"Settings": "Preferències",
"Share": "Compartir",
"Share Folder": "Compartir carpeta",
"Share by Email": "Comparteix per correu electrònic",
"Share by SMS": "Comparteix per SMS",
"Share this folder?": "Compartir aquesta carpeta?",
"Shared Folders": "Carpetes compartides",
"Shared With": "Compartir Amb",
"Sharing": "Compartint",
"Show ID": "Mostrar ID",
"Show QR": "Mostra QR",
"Show detailed discovery status": "Mostra l'estat detallat del descobriment",
"Show detailed listener status": "Mostra l'estat detallat de l'oient",
"Show diff with previous version": "Mostra la diferència amb la versió anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en comptes del ID del Node en l'estat del cluster. Serà advertit als altres dispositius com un nom opcional per defecte.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en comptes del ID del Node en l'estat del cluster. S'actualitzarà al nom del dispositiu si es deixa buit.",
"Shutdown": "Apagar",
"Shutdown Complete": "Apagat complet",
"Simple": "Simple",
"Simple File Versioning": "Versionat de Fitxers Senzill",
"Single level wildcard (matches within a directory only)": "Caràcter comodí de nivell singular (aparella sóls en una carpeta)",
"Size": "Mida",
"Smallest First": "Més petit primer",
"Some discovery methods could not be established for finding other devices or announcing this device:": "No s'han pogut establir alguns mètodes de descoberta per trobar altres dispositius o anunciar aquest dispositiu:",
"Some items could not be restored:": "Alguns elements no s'han pogut restaurar:",
"Some listening addresses could not be enabled to accept connections:": "Algunes adreces d'escolta no s'han pogut habilitar per acceptar connexions:",
"Source Code": "Codi Font",
"Stable releases and release candidates": "Alliberaments estables i candidats al llançament",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Els llançaments estables es retarden unes dues setmanes. Durant aquest temps passen per proves com a candidats al llançament.",
"Stable releases only": "Només versions estables",
"Staggered": "Esglaonat",
"Staggered File Versioning": "Versionat de Fitxers Esglaonat",
"Start Browser": "Arrancar Navegador",
"Statistics": "Estadístiques",
"Stopped": "Aturat",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Emmagatzema i sincronitza només dades encriptades. Les carpetes de tots els dispositius connectats s'han de configurar amb la mateixa contrasenya o també ser del tipus \"{{receiveEncrypted}}\".",
"Subject:": "Assumpte:",
"Support": "Suport",
"Support Bundle": "Paquet de suport",
"Sync Extended Attributes": "Sincronitza els atributs ampliats",
"Sync Ownership": "Sincronitza la propietat",
"Sync Protocol Listen Addresses": "Adreça d'escolta del Protocol Sync",
"Sync Status": "Estat de sincronització",
"Syncing": "Synthing",
"Syncthing device ID for \"{%devicename%}\"": "ID del dispositiu Syncthing amb el nom \"{{devicename}}\"",
"Syncthing has been shut down.": "S'ha aturat el synthing.",
"Syncthing includes the following software or portions thereof:": "Syncthing inclou el següent programari o parts dels mateixos:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing és un programari lliure i de codi obert amb llicència MPL v2.0.",
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing és un programa de sincronització contínua de fitxers. Sincronitza fitxers entre dos o més ordinadors en temps real, protegit de manera segura de mirades indiscretes. Les vostres dades són només les vostres i mereixeu triar on s'emmagatzemen, si es comparteixen amb un tercer i com es transmeten per Internet.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "La sincronització està escoltant a les adreces de xarxa següents els intents de connexió des d'altres dispositius:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "La sincronització no és escoltar els intents de connexió d'altres dispositius a cap adreça. Només poden funcionar les connexions sortints d'aquest dispositiu.",
"Syncthing is restarting.": "Reiniciant syncthing.",
"Syncthing is upgrading.": "Actualitzant syncthing.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Synthing sembla parat, o hi ha algun problema amb la connexió a Internet. Reintentant...",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ara admet la notificació automàtica d'errors als desenvolupadors. Aquesta funció està activada per defecte.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Sembla que Syncthing no està funcionant o hi ha un problema amb la connexió a Internet. S'està tornant a provar…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Sembla ser que Syncthing està tinguent problemes per processar la teva petició. Si us plau, refresca la pàgina o reinicia Syncthing si el problema persisteix.",
"TCP LAN": "Connexió TCP LAN",
"TCP WAN": "Connexió TCP WAN",
"Take me back": "Porta'm enrere",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Les opcions d'inici substitueixen l'adreça de la GUI. Els canvis aquí no tindran efecte mentre la substitució estigui vigent.",
"The Syncthing Authors": "Els autors de Syncthing",
"The Syncthing admin interface is configured to allow remote access without a password.": "La interfície d'administració de Syncthing està configurada per permetre l'accés remot sense contrasenya.",
"The aggregated statistics are publicly available at the URL below.": "Les estadístiques agregades estan disponibles públicament a l'URL següent.",
"The cleanup interval cannot be blank.": "L'interval de neteja no pot estar en blanc.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuració s'ha guardar però no s'ha activat. S'ha de reiniciar el synthing per activar la nova configuració.",
"The device ID cannot be blank.": "El ID del dispositiu no pot estar en blanc.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "L'identificador del dispositiu que cal introduir aquí es pot trobar al diàleg \"Accions > Mostra l'ID\" de l'altre dispositiu. Els espais i els guions són opcionals (ignorats).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "L'informe d'ús encriptat s'envia diàriament. Es fa servir per rastrejar plataformes habituals, mides de carpetes i versions de l'aplicació. Si es canvia el conjunt de dades reportades es demanarà amb aquest diàleg de nou.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "El ID del dispositiu introduït no sembla vàlid. Hauria de tenir 52 o 56 caràcters amb lletres i números, els espais i les barres son opcionals.",
"The folder ID cannot be blank.": "El ID del dispositiu no pot estar en blanc.",
"The folder ID must be unique.": "El ID de la carpeta ha de ser únic.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "El contingut de la carpeta d'altres dispositius se sobreescriurà per ser idèntic al d'aquest dispositiu. Els fitxers no presents aquí se suprimiran en altres dispositius.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "El contingut de la carpeta d'aquest dispositiu se sobreescriurà per ser idèntic al d'altres dispositius. Els fitxers recentment afegits aquí se suprimiran.",
"The folder path cannot be blank.": "El camí a la carpeta no pot estar en blanc.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Es fan servir els següents intervals: per la primera hora es manté una versió cada 30 segons, pel primer dia es manté una versió cada hora, pel primer cada 30 dies es manté una versió cada dia, fins el màxim d'antiguitat es manté una versió cada setmana.",
"The following items could not be synchronized.": "Els següents elements no es poden sincronitzar.",
"The following items were changed locally.": "Els elements següents s'han canviat localment.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Els mètodes següents s'utilitzen per descobrir altres dispositius a la xarxa i anunciar que aquest dispositiu serà trobat per altres:",
"The following text will automatically be inserted into a new message.": "El text següent s'inserirà automàticament en un missatge nou.",
"The following unexpected items were found.": "S'han trobat els següents elements inesperats.",
"The interval must be a positive number of seconds.": "L'interval ha de ser un nombre positiu de segons.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'interval, en segons, per executar la neteja al directori de versions. Zero per desactivar la neteja periòdica.",
"The maximum age must be a number and cannot be blank.": "La màxima antiguitat ha de ser un número i no pot estar en blanc.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Temps màxim en mantenir una versió (en dies, si es deixa en 0 es mantenen les versions per sempre).",
"The number of days must be a number and cannot be blank.": "El nombre de dies ha de ser un número i no pot estar en blanc.",
@@ -202,45 +428,102 @@
"The number of versions must be a number and cannot be blank.": "El nombre de versions ha de ser un número i no es pot deixar en blanc.",
"The path cannot be blank.": "El camí no pot estar en blanc.",
"The rate limit must be a non-negative number (0: no limit)": "El límit de velocitat ha de ser un nombre positiu (0: sense límit)",
"The remote device has not accepted sharing this folder.": "El dispositiu remot no ha acceptat compartir aquesta carpeta.",
"The remote device has paused this folder.": "El dispositiu remot ha posat en pausa aquesta carpeta.",
"The rescan interval must be a non-negative number of seconds.": "El interval de re-escaneig ha der ser un nombre positiu de segons.",
"There are no devices to share this folder with.": "No hi ha dispositius amb qui compartir aquesta carpeta",
"There are no devices to share this folder with.": "No hi ha cap dispositiu per compartir aquesta carpeta.",
"There are no file versions to restore.": "No hi ha versions de fitxers per restaurar.",
"There are no folders to share with this device.": "No hi ha carpetes que es puguin compartir amb aquest dispositiu",
"They are retried automatically and will be synced when the error is resolved.": "Són reintentats automàticament i seran sincronitzats quan l'error estigui resolt.",
"This Device": "Aquest dispositiu",
"This Month": "Aquest mes",
"This can easily give hackers access to read and change any files on your computer.": "Això pot donar facilment accés a hackers per llegir i canviar qualsevol fitxer del teu ordinador.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Aquest dispositiu no pot detectar automàticament altres dispositius ni anunciar la seva pròpia adreça perquè altres puguin trobar. Només es poden connectar dispositius amb adreces configurades estàticament.",
"This is a major version upgrade.": "Aquesta és una actualització de versió major.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Aquesta configuració controla l'espai lliure necessari al disc d'inici (és a dir, la base de dades d'índex).",
"Time": "Temps",
"Time the item was last modified": "Hora de la darrera modificació de l'element",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Per connectar-vos amb el dispositiu de sincronització anomenat \"{{devicename}}\", afegiu un nou dispositiu remot amb aquest identificador:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Per permetre una regla, marqueu la casella de selecció. Per denegar una regla, deixeu-la sense marcar.",
"Today": "Avui",
"Trash Can": "Paperera",
"Trash Can File Versioning": "Paperera de versionat de fitxers",
"Twitter": "Twitter",
"Type": "Tipus",
"UNIX Permissions": "Permisos UNIX",
"Unavailable": "No disponible",
"Unavailable/Disabled by administrator or maintainer": "No disponible/Desactivat per l'administrador o el responsable",
"Undecided (will prompt)": "No decidit (es preguntarà)",
"Unexpected Items": "Elements inesperats",
"Unexpected items have been found in this folder.": "S'han trobat elements inesperats en aquesta carpeta.",
"Unignore": "No ignorar",
"Unknown": "Desconegut",
"Unshared": "No compartit",
"Unshared Devices": "Dispositius no compartits",
"Unshared Folders": "Carpetes no compartides",
"Untrusted": "No fiable",
"Up to Date": "Actualitzat",
"Updated {%file%}": "S'ha actualitzat {{file}}",
"Upgrade": "Actualització",
"Upgrade To {%version%}": "Actualitzar a {{version}}",
"Upgrading": "Actualitzant",
"Upload Rate": "Tasca de Pujada",
"Uptime": "Temps funcionant",
"Usage reporting is always enabled for candidate releases.": "Els informes d'ús sempre estan activats per a les versions candidates.",
"Use HTTPS for GUI": "Utilitzar HTTPS pel GUI",
"Use notifications from the filesystem to detect changed items.": "Utilitzeu les notificacions del sistema de fitxers per detectar elements canviats.",
"User Home": "Carpeta d'inici de l'usuari",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "El nom d'usuari/contrasenya no s'ha establert per a l'autenticació de la GUI. Penseu en configurar-lo.",
"Using a direct TCP connection over LAN": "Utilitzant una connexió TCP directa per LAN",
"Using a direct TCP connection over WAN": "Utilitzant una connexió TCP directa a través de WAN",
"Version": "Versió",
"Versions": "Versions",
"Versions Path": "Carpeta de les Versions",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Les versions son automàticament eliminades si son més antigues que el màxim d'antiguitat o si excedeixen del nombre de fitxers permesos en un interval.",
"Waiting to Clean": "Esperant per netejar",
"Waiting to Scan": "Esperant per escanejar",
"Waiting to Sync": "Esperant per sincronitzar",
"Warning": "Avís",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Advertència, aquest camí és un directori principal d'una carpeta existent \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Advertència, aquest camí és un directori principal d'una carpeta existent \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Avís, aquest camí és un subdirectori d'una carpeta existent \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Advertència, aquest camí és un subdirectori d'una carpeta existent \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Avís: si feu servir un observador extern com {{syncthingInotify}}, hauríeu d'assegurar-vos que estigui desactivat.",
"Watch for Changes": "Vigilar els Canvis",
"Watching for Changes": "Vigilant els Canvis",
"Watching for changes discovers most changes without periodic scanning.": "Observant els canvis descobreix la majoria dels canvis sense escanejar periòdicament.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quan s'afegeix un nou dispositiu, recorda que aquest dispositiu tambè s'ha d'afegir a l'altre banda.",
"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.": "Quan s'afegeix una nova carpeta recorda que el ID d'aquesta s'utilitza per lligar repositoris entre els dispositius. Es distingeix entre majúscules i minúscules i ha de ser exactament iguals entre tots els dispositius.",
"Yes": "Si",
"Yesterday": "Ahir",
"You can also copy and paste the text into a new message manually.": "També podeu copiar i enganxar el text en un missatge nou manualment.",
"You can also select one of these nearby devices:": "També pots escollir un d'aquests dispositius pròxims:",
"You can change your choice at any time in the Settings dialog.": "Pots canviar la teva elecció en qualsevol moment al quadre de preferències.",
"You can read more about the two release channels at the link below.": "Podeu llegir més sobre els dos canals de llançament a l'enllaç següent.",
"You have no ignored devices.": "No teniu cap dispositiu ignorat.",
"You have no ignored folders.": "No tens carpetes compartides.",
"You have unsaved changes. Do you really want to discard them?": "Tens canvis no desats. Realment les voleu descartar?",
"You must keep at least one version.": "Has de mantenir com a mínim una versió.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Mai no hauríeu d'afegir ni canviar res localment a una carpeta \"{{receiveEncrypted}}\".",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "La vostra aplicació SMS s'hauria d'obrir per permetre't triar el destinatari i enviar-lo des del teu propi número.",
"Your email app should open to let you choose the recipient and send it from your own address.": "La vostra aplicació de correu electrònic s'hauria d'obrir per permetre-vos triar el destinatari i enviar-lo des de la vostra adreça.",
"days": "dies",
"deleted": "esborrat",
"deny": "denegar",
"directories": "directoris",
"file": "fitxer",
"files": "fitxers",
"folder": "carpeta",
"full documentation": "documentació sencera",
"items": "Elements",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartir la carpeta \"{{folder}}\"."
"modified": "modificat",
"permit": "permís",
"seconds": "segons",
"theme-name-black": "Negre",
"theme-name-dark": "Fosc",
"theme-name-default": "Per defecte",
"theme-name-light": "Clar",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartir la carpeta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vol compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} podria tornar a introduir aquest dispositiu."
}

View File

@@ -6,11 +6,13 @@
"About": "Sobre",
"Action": "Acció",
"Actions": "Accions",
"Active filter rules": "Regles de filtre actiu",
"Add": "Afegir",
"Add Device": "Afegir dispositiu",
"Add Folder": "Afegir carpeta",
"Add Remote Device": "Afegir Dispositiu Remot.",
"Add devices from the introducer to our device list, for mutually shared folders.": "Afegir dispositius des-de l'introductor a la nostra llista de dispositius, per a tindre carpetes compartides mútuament",
"Add Remote Device": "Afegeix un dispositiu remot",
"Add devices from the introducer to our device list, for mutually shared folders.": "Afegiu dispositius de l'introductor a la nostra llista de dispositius, per a carpetes compartides mútuament.",
"Add filter entry": "Afegeix una entrada de filtre",
"Add ignore patterns": "Afegir patrons a ignorar",
"Add new folder?": "Afegir nova carpeta?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Adicionalment s'augmentarà l'interval d'escaneig complet (times 60, per exemple, ficarà el nou temps per defecte a 1 hora). També pots configurar-ho manualment per a cada carpeta més tard elegint No.",
@@ -45,6 +47,7 @@
"Automatically create or share folders that this device advertises at the default path.": "Crear o compartir automàticament les carpetes que aquest dispositiu anuncia en la ruta per defecte.",
"Available debug logging facilities:": "Hi han disponibles les següents utilitats per a depurar el registre:",
"Be careful!": "Tin precaució!",
"Body:": "Cos de text:",
"Bugs": "Errors (Bugs)",
"Cancel": "Cancel·lar",
"Changelog": "Registre de canvis",
@@ -63,14 +66,19 @@
"Connection Error": "Error de connexió",
"Connection Type": "Tipus de connexió",
"Connections": "Connexions",
"Connections via relays might be rate limited by the relay": "Les connexions mitjançant relés poden estar limitades pel relé",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Ara està disponible la revisió continua de canvix dins de Syncthing. Acò detectarà els canvis i llençarà un escaneig sols a les rutes modificades. Els beneficis són que els canvis es propaguen mé ràpidamente i es necessiten menys escanejos complets.",
"Copied from elsewhere": "Copiat de qualsevol lloc",
"Copied from original": "Copiat de l'original",
"Copied!": "Copiat!",
"Copy": "Copiar",
"Copy failed! Try to select and copy manually.": "La còpia ha fallat! Intenta seleccionar i copiar manualment.",
"Currently Shared With Devices": "Actualment compartit amb dispositius",
"Custom Range": "Interval personalitzat",
"Danger!": "Perill!",
"Database Location": "Ubicació de la base de dades",
"Debugging Facilities": "Utilitats de Depuració",
"Default": "Per defecte",
"Default Configuration": "Configuració per defecte",
"Default Device": "Dispositiu per Defecte",
"Default Folder": "Carpeta per Defecte",
@@ -100,6 +108,7 @@
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desactiva la comparació i sincronització dels permisos de fitxers. Útil en sistemes amb permisos personalitzats o no existents (p. ex. FAT, exFAT, Synology, Android).",
"Discard": "Descartar",
"Disconnected": "Desconnectat",
"Disconnected (Inactive)": "Desconnectat (inactiu)",
"Disconnected (Unused)": "Desconnectat (No util·litzat)",
"Discovered": "Descobert",
"Discovery": "Descobriment",
@@ -135,6 +144,7 @@
"Enter up to three octal digits.": "Introduïu fins a tres dígits octals.",
"Error": "Error",
"Extended Attributes": "Atributs ampliats",
"Extended Attributes Filter": "Filtre d'atributs estesos",
"External": "Extern",
"External File Versioning": "Versionat extern de fitxers",
"Failed Items": "Objectes fallits",
@@ -167,7 +177,7 @@
"GUI Authentication Password": "Password d'autenticació de l'Interfície Gràfica d'Usuari (GUI)",
"GUI Authentication User": "Autenticació de l'usuari de l'Interfície Gràfica d'Usuari (GUI)",
"GUI Authentication: Set User and Password": "Autenticació de la interfície gràfica d'usuari: defineix l'usuari i la contrasenya",
"GUI Listen Address": "Adreça d'Escolta de l'Interfície Gràfica d'Usuari (GUI).",
"GUI Listen Address": "Adreça d'escolta de la Interfície Gràfica d'Usuari (GUI)",
"GUI Override Directory": "Directori de substitució de la interfície gràfica d'usuari",
"GUI Theme": "Tema de l'Interfície Gràfica d'Usuari (GUI)",
"General": "General",
@@ -176,6 +186,7 @@
"Global Discovery Servers": "Servidors de Descobriment Global",
"Global State": "Estat global",
"Help": "Ajuda",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Suggeriment: només s'han detectat regles de denegació mentre el valor predeterminat és denegació. Penseu en afegir \"permet qualsevol\" com a darrera regla.",
"Home page": "Pàgina inicial",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Tanmateix, la vostra configuració actual indica que potser no voleu que estigui activada. Hem desactivat l'informe automàtic d'errors.",
"Identification": "Identificació",
@@ -205,6 +216,7 @@
"Last seen": "Vist per última vegada",
"Latest Change": "Últim Canvi",
"Learn more": "Saber més",
"Learn more at {%url%}": "Més informació a {{url}}",
"Limit": "Límit",
"Listener Failures": "Errors en l'escolta",
"Listener Status": "Estatus en l'escolta",
@@ -223,10 +235,15 @@
"Major Upgrade": "Actualització important",
"Mass actions": "Accions en masa",
"Maximum Age": "Edat màxima",
"Maximum single entry size": "Mida màxima d'entrada única",
"Maximum total size": "Mida total màxima",
"Metadata Only": "Sols metadades",
"Minimum Free Disk Space": "Espai minim de disc lliure",
"Mod. Device": "Dispositiu Modificador",
"Mod. Time": "Temps de la Modificació",
"More than a month ago": "Fa més d'un mes",
"More than a week ago": "Fa més d'una setmana",
"More than a year ago": "Fa més d'un any",
"Move to top of queue": "Moure al principi de la cua",
"Multi level wildcard (matches multiple directory levels)": "Comodí multinivell (coincideix amb múltiples nivells de directoris)",
"Never": "Mai",
@@ -236,10 +253,12 @@
"No": "No",
"No File Versioning": "Sense versionat de fitxer",
"No files will be deleted as a result of this operation.": "Amb aquesta operació no s'esborrarà cap fitxer.",
"No rules set": "No hi ha regles establertes",
"No upgrades": "Sense actualitzacions",
"Not shared": "No compartit",
"Notice": "Avís",
"OK": "OK",
"OK": "D'acord",
"Off": "Apagat",
"Oldest First": "El més vell primer",
"Optional descriptive label for the folder. Can be different on each device.": "Etiqueta descriptiva opcional per la carpeta. Pot ser diferent en cada dispositiu.",
"Options": "Opcions",
@@ -271,6 +290,9 @@
"Preview": "Vista prèvia",
"Preview Usage Report": "Informe d'ús de vista prèvia",
"QR code": "Codi QR",
"QUIC LAN": "Xarxa QUIC LAN",
"QUIC WAN": "Xarxa QUIC WAN",
"QUIC connections are in most cases considered suboptimal": "En la majoria dels casos, les connexions QUIC es consideren subòptimes",
"Quick guide to supported patterns": "Guía ràpida de patrons suportats",
"Random": "Aleatori",
"Receive Encrypted": "Rebre xifrat",
@@ -278,8 +300,10 @@
"Received data is already encrypted": "Les dades rebudes ja estan xifrades",
"Recent Changes": "Canvis Recents",
"Reduced by ignore patterns": "Reduït ignorant patrons",
"Relay LAN": "LAN de relé",
"Relay WAN": "WAN de relé",
"Release Notes": "Notes de la versió",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Les versions candidates (Release Candidates) contenen les darreres característiques i arreglos. Són paregudes a les versions tradicionals bi-semanals de Syncthing. ",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Les versions candidates (Release Candidates) contenen les darreres característiques i correccions. Són semblants a les versions tradicionals bi setmanals de Syncthing.",
"Remote Devices": "Dispositius Remots",
"Remote GUI": "Interfície Gràfica d'Usuari remota",
"Remove": "Eliminar",
@@ -317,6 +341,8 @@
"Settings": "Ajustos",
"Share": "Compartir",
"Share Folder": "Compartir carpeta",
"Share by Email": "Comparteix per correu electrònic",
"Share by SMS": "Comparteix per SMS",
"Share this folder?": "Compartir aquesta carpeta?",
"Shared Folders": "Carpetes compartides",
"Shared With": "Compartit amb",
@@ -330,7 +356,7 @@
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'actualitzarà al nom que el dispositiu anuncia si es deixa buit.",
"Shutdown": "Apagar",
"Shutdown Complete": "Apagar completament",
"Simple": "Simple",
"Simple": "Senzill",
"Simple File Versioning": "Versionat de fitxers senzill",
"Single level wildcard (matches within a directory only)": "Comodí de nivell únic (coincideix sols dins d'un directori)",
"Size": "Tamany",
@@ -348,6 +374,7 @@
"Statistics": "Estadístiques",
"Stopped": "Parat",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Emmagatzema i sincronitza només dades encriptades. Les carpetes de tots els dispositius connectats s'han de configurar amb la mateixa contrasenya o també ser del tipus \"{{receiveEncrypted}}\".",
"Subject:": "Assumpte:",
"Support": "Suport",
"Support Bundle": "Lot de Suport",
"Sync Extended Attributes": "Sincronitza els atributs ampliats",
@@ -355,16 +382,20 @@
"Sync Protocol Listen Addresses": "Direccions d'escolta del protocol de sincronització",
"Sync Status": "Estat de sincronització",
"Syncing": "Sincronitzant",
"Syncthing has been shut down.": "Syncthing s'ha apagat",
"Syncthing device ID for \"{%devicename%}\"": "ID del dispositiu Syncthing amb el nom \"{{devicename}}\"",
"Syncthing has been shut down.": "Syncthing s'ha tancat.",
"Syncthing includes the following software or portions thereof:": "Syncthing inclou el següent software o parts d'ell:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing és Software Gratuït i Open Source llicenciat com MPL v2.0.",
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing és un programa de sincronització contínua de fitxers. Sincronitza fitxers entre dos o més ordinadors en temps real, protegit de manera segura de mirades indiscretes. Les vostres dades són només les vostres i mereixeu triar on s'emmagatzemen, si es comparteixen amb un tercer i com es transmeten per Internet.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "La sincronització està escoltant a les adreces de xarxa següents els intents de connexió des d'altres dispositius:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing no està escoltant els intents de connexió d'altres dispositius a cap adreça. Només poden funcionar les connexions sortints d'aquest dispositiu.",
"Syncthing is restarting.": "Syncthing està reiniciant.",
"Syncthing is upgrading.": "Syncthing està actualitzant-se.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ara admet la notificació automàtica d'errors als desenvolupadors. Aquesta funció està activada per defecte.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing pareix apagat o hi ha un problema amb la connexió a Internet. Tornant a intentar...",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing sembla apagat o hi ha un problema amb la connexió a Internet. Tornant a intentar",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing pareix que té un problema processant la seua sol·licitud. Per favor, refresque la pàgina o reinicie Syncthing si el problema persistix.",
"TCP LAN": "Connexió TCP LAN",
"TCP WAN": "Connexió TCP WAN",
"Take me back": "Porta'm enrere",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "L'adreça del GUI és sobreescrita per les opcions d'inici. Els canvis ací no surtiràn efecte mentre la sobreescritura estiga en marxa.",
"The Syncthing Authors": "Els autors de Syncthing",
@@ -374,7 +405,7 @@
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuració ha sigut gravada però no activada. Syncthing deu reiniciar per tal d'activar la nova configuració.",
"The device ID cannot be blank.": "L'ID del dispositiu no pot estar buida.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "L'ID del dispositiu que hi ha que introduïr ací es pot trobar en el menú \"Accions > Mostrar ID\" en l'altre dispositiu. Els espais i les barres son opcionals (ignorats).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "L'informe encriptat d'ús s'envia diariament. S'utilitza per a rastrejar plataformes comuns, tamanys de carpetes i versions de l'aplicació. Si el conjunt de dades enviat a l'informe es canvia, se li demanarà a vosté l'autorització altra vegada.\n",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "L'informe d'ús xifrat s'envia diàriament. S'utilitza per fer un seguiment de plataformes habituals, mides de carpetes i versions d'aplicacions. Si es canvia el conjunt de dades informat, se us demanarà de nou aquest diàleg.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID del dispositiu introduïda no pareix vàlida. Deuria ser una cadena de 52 o 56 caracters consistents en lletres i nombre, amb espais i barres opcionals.",
"The folder ID cannot be blank.": "L'ID de la carpeta no pot estar buit.",
"The folder ID must be unique.": "L'ID de la carpeta deu ser única.",
@@ -385,6 +416,7 @@
"The following items could not be synchronized.": "Els següents objectes no s'han pogut sincronitzar.",
"The following items were changed locally.": "Els següents ítems es canviaren localment.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Els mètodes següents s'utilitzen per descobrir altres dispositius a la xarxa i anunciar que aquest dispositiu serà trobat per altres:",
"The following text will automatically be inserted into a new message.": "El text següent s'inserirà automàticament en un missatge nou.",
"The following unexpected items were found.": "S'han trobat els següents elements inesperats.",
"The interval must be a positive number of seconds.": "L'interval ha de ser un nombre positiu de segons.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'interval, en segons, per executar la neteja al directori de versions. Zero per desactivar la neteja periòdica.",
@@ -411,6 +443,8 @@
"This setting controls the free space required on the home (i.e., index database) disk.": "Aquest ajust controla l'espai lliure requerit en el disc inicial (per exemple, la base de dades de l'index).",
"Time": "Temps",
"Time the item was last modified": "Hora a la que l'ítem fou modificat per última vegada",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Per connectar-vos amb el dispositiu Syncthing anomenat \"{{devicename}}\", afegiu un nou dispositiu remot amb aquest identificador:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Per permetre una regla, marqueu la casella de selecció. Per denegar una regla, deixeu-la sense marcar.",
"Today": "Avui",
"Trash Can": "Paperera",
"Trash Can File Versioning": "Versionat d'arxius de la paperera",
@@ -429,7 +463,7 @@
"Unshared Folders": "Carpetes no compartides",
"Untrusted": "No fiable",
"Up to Date": "Actualitzat",
"Updated {%file%}": "S'ha actualitzat {{fitxer}}",
"Updated {%file%}": "S'ha actualitzat {{file}}",
"Upgrade": "Actualitzar",
"Upgrade To {%version%}": "Actualitzar a {{version}}",
"Upgrading": "Actualitzant",
@@ -440,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Usar notificacions del sistema de fitxers per a detectar els ítems canviats.",
"User Home": "Carpeta d'inici de l'usuari",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "El nom d'usuari/contrasenya no s'ha establert per a l'autenticació de la interfície gràfica d'usuari. Penseu a configurar-ho.",
"Using a direct TCP connection over LAN": "Utilitzant una connexió TCP directa per LAN",
"Using a direct TCP connection over WAN": "Utilitzant una connexió TCP directa a través de WAN",
"Version": "Versió",
"Versions": "Versions",
"Versions Path": "Ruta de les versions",
@@ -460,19 +496,28 @@
"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.": "Quant s'afig una nova carpeta, hi ha que tindre en compte que l'ID de la carpeta s'utilitza per a juntar les carpetes entre dispositius. Són sensibles a les majúscules i deuen coincidir exactament entre tots els dispositius.",
"Yes": "Sí",
"Yesterday": "Ahir",
"You can also copy and paste the text into a new message manually.": "També podeu copiar i enganxar el text en un missatge nou manualment.",
"You can also select one of these nearby devices:": "Pots seleccionar també un d'aquestos dispositius propers:",
"You can change your choice at any time in the Settings dialog.": "Pots canviar la teua elecció en qualsevol moment en el dialog Ajustos",
"You can change your choice at any time in the Settings dialog.": "Podeu canviar la vostra elecció en qualsevol moment al diàleg Configuració.",
"You can read more about the two release channels at the link below.": "Pots llegir més sobre els dos canals de versions en l'enllaç de baix.",
"You have no ignored devices.": "No tens dispositius ignorats.",
"You have no ignored folders.": "No tens carpetes ignorades.",
"You have unsaved changes. Do you really want to discard them?": "Tens canvis sense guardar. Realment vols descartar-los?",
"You must keep at least one version.": "Es deu mantindre al menys una versió.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Mai no hauríeu d'afegir ni canviar res localment en una carpeta \"{{receiveEncrypted}}\".",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "La vostra aplicació SMS s'hauria d'obrir per permetre't triar el destinatari i enviar-lo des del teu propi número.",
"Your email app should open to let you choose the recipient and send it from your own address.": "La vostra aplicació de correu electrònic s'hauria d'obrir per permetre-vos triar el destinatari i enviar-lo des de la vostra adreça.",
"days": "dies",
"deleted": "esborrat",
"deny": "denegar",
"directories": "directoris",
"file": "fitxer",
"files": "arxius",
"folder": "carpeta",
"full documentation": "Documentació completa",
"items": "Elements",
"modified": "modificat",
"permit": "permís",
"seconds": "segons",
"theme-name-black": "Negre",
"theme-name-dark": "Fosc",

View File

@@ -125,7 +125,7 @@
"Enable Relaying": "Povolit přenašeče (relay)",
"Enabled": "Zapnuto",
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Umožňuje odesílat rozšířené atributy do dalších zařízení a aplikovat příchozí rozšířené atributy. Může vyžadovat spuštění se zvýšeným oprávněním.",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Umožňuje odesílat rozšířené atributy do dalších zařízení, ale ne aplikaci příchozích rozšířených atributů. Může přínést výrazné zhoršení výkonu. Automaticky povoleno, když je povoleno „Synchronizovat rozšířené atributy.“",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Umožňuje odesílat rozšířené atributy do dalších zařízení, ale ne aplikaci příchozích rozšířených atributů. Může přínést výrazné zhoršení výkonu. Automaticky povoleno, když je povoleno „Synchronizovat rozšířené atributy.“",
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Umožňuje odesílat informace o vlastnictví do dalších zařízení a aplikovat příchozí vlastnictví. Typicky vyžaduje spuštění s vyšším oprávněním.",
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "Umožňuje odesílat informace o vlastnictví do dalších zařízení, ale ne aplikaci příchozích vlastnictví. Může přinést výrazné zhoršení výkonu. Vždy povoleno, když je povoleno „Synchronizovat informace o vlastnictví.“",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Zadejte kladné číslo (např. „2.35“) a zvolte jednotku. Procenta znamenají část celkové velikosti úložiště.",

View File

@@ -5,14 +5,16 @@
"API Key": "API-nøgle",
"About": "Om",
"Action": "Handling",
"Actions": "Handlinger.",
"Actions": "Handlinger",
"Active filter rules": "Aktive filterregler",
"Add": "Tilføj",
"Add Device": "Tilføj enhed",
"Add Folder": "Tilføj mappe",
"Add Remote Device": "Tilføj fjernenhed",
"Add devices from the introducer to our device list, for mutually shared folders.": "Tilføj enheder fra den introducerende enhed til vores enhedsliste for gensidigt delte mapper.",
"Add filter entry": "Tilføj filterpost",
"Add ignore patterns": "Tilføj ignoreringsmønstre",
"Add new folder?": "Tilføj ny mappe",
"Add new folder?": "Tilføj ny mappe?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Derudover vil intervallet for den komplette genskan blive forøget (60 gange, dvs. ny standard er 1 time). Du kan også konfigurere det manuelt for hver mappe senere efter at have valgt Nej.",
"Address": "Adresse",
"Addresses": "Adresser",
@@ -76,6 +78,7 @@
"Danger!": "Fare!",
"Database Location": "Database placering",
"Debugging Facilities": "Faciliteter til fejlretning",
"Default": "Standard",
"Default Configuration": "Standard opsætning",
"Default Device": "Standard enhed",
"Default Folder": "Standard mappe",
@@ -141,6 +144,7 @@
"Enter up to three octal digits.": "Indtast op til tre oktale cifre.",
"Error": "Fejl",
"Extended Attributes": "Udvidede attributter",
"Extended Attributes Filter": "Udvidet filter for egenskaber",
"External": "Eksternt",
"External File Versioning": "Ekstern filversionering",
"Failed Items": "Mislykkede filer",
@@ -168,12 +172,13 @@
"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.": "For de følgende mapper opstod en fejl ved start på overvågning af ændringer. Der prøves igen hvert minut, så fejlene går eventuelt væk snart. Hvis de forbliver, kan du prøve at rette den tilgrundliggende fejl eller spørge efter hjælp, hvis du ikke kan.",
"Forever": "For altid",
"Full Rescan Interval (s)": "Interval for komplet genskan (sek.)",
"GUI": "GUI",
"GUI": "Grafisk brugerflade (GUI)",
"GUI / API HTTPS Certificate": "GUI / API HTTPS-certifikat",
"GUI Authentication Password": "GUI-adgangskode",
"GUI Authentication User": "GUI-brugernavn",
"GUI Authentication: Set User and Password": "GUI godkendelse: Angiv bruger og adgangskode",
"GUI Listen Address": "GUI-lytteadresse",
"GUI Override Directory": "Mappe til GUI-tilsidesættelse",
"GUI Theme": "GUI-tema",
"General": "Generelt",
"Generate": "Opret",
@@ -181,6 +186,7 @@
"Global Discovery Servers": "Globale opslagsservere",
"Global State": "Global tilstand",
"Help": "Hjælp",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tip: kun afvis-regler registreres, når standardværdien er afvis. Overvej at tilføje \"tillad alle\" som sidste regel.",
"Home page": "Hjem",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dine nuværende indstillinger tyder dog på, at du måske ikke ønsker det aktiveret. Vi har deaktiveret automatisk nedbrudsrapportering for dig.",
"Identification": "Identifikation",
@@ -229,6 +235,8 @@
"Major Upgrade": "Opgradering til ny hovedversion",
"Mass actions": "Massehandlinger",
"Maximum Age": "Maksimal alder",
"Maximum single entry size": "Maksimal størrelse af en enkelt post",
"Maximum total size": "Maksimal samlet størrelse",
"Metadata Only": "Kun metadata",
"Minimum Free Disk Space": "Mindst ledig diskplads",
"Mod. Device": "Enhed for ændring",
@@ -245,6 +253,7 @@
"No": "Nej",
"No File Versioning": "Ingen filversionering",
"No files will be deleted as a result of this operation.": "Ingen filer vil blive slettet som resultat af denne handling.",
"No rules set": "Ingen regler indstillet",
"No upgrades": "Ingen opgraderinger",
"Not shared": "Ikke delte",
"Notice": "Bemærk",
@@ -281,6 +290,8 @@
"Preview": "Forhåndsvisning",
"Preview Usage Report": "Forhåndsvisning af forbrugsrapport",
"QR code": "QR-kode",
"QUIC LAN": "QUIC LAN",
"QUIC WAN": "QUIC WAN",
"QUIC connections are in most cases considered suboptimal": "QUIC-forbindelser anses i de fleste tilfælde for at være mindre optimale",
"Quick guide to supported patterns": "Kvikguide til understøttede mønstre",
"Random": "Tilfældig",
@@ -289,6 +300,8 @@
"Received data is already encrypted": "Modtaget data er allerede krypteret",
"Recent Changes": "Nylige ændringer",
"Reduced by ignore patterns": "Reduceret af ignoreringsmønstre",
"Relay LAN": "Relay LAN",
"Relay WAN": "Relay WAN",
"Release Notes": "Udgivelsesnoter",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Udgivelseskandidater indeholder alle de nyeste funktioner og rettelser. De er det samme som de traditionelle tougers-udgivelser af Syncthing.",
"Remote Devices": "Fjernenheder",
@@ -381,6 +394,8 @@
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing understøtter nu automatisk rapportering af nedbrud til udviklere. Denne funktion er aktiveret som standard.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing ser ud til at være stoppet eller oplever problemer med din internetforbindelse. Prøver igen…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Det ser ud til, at Syncthing har problemer med at udføre opgaven. Prøv at genindlæse siden eller genstarte Synching, hvis problemet vedbliver.",
"TCP LAN": "TCP LAN",
"TCP WAN": "TCP WAN",
"Take me back": "Tag mig tilbage",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "GUI-adressen tilsidesættes af opstartsindstillingerne. Ændringer her vil ikke træde i kraft, så længe tilsidesættelsen er i kraft.",
"The Syncthing Authors": "Syncthing udviklere",
@@ -429,6 +444,7 @@
"Time": "Tid",
"Time the item was last modified": "Tidspunkt for seneste ændring af filen",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "For at oprette forbindelse til Syncthing-enheden med navnet \"{{devicename}}\" skal du tilføje en ny fjernenhed med dette ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Hvis du vil tillade en regel, skal afkrydsningsfeltet være markeret. Hvis du vil afvise en regel, skal du lade det forblive umarkeret.",
"Today": "I dag",
"Trash Can": "Affaldskurv",
"Trash Can File Versioning": "Versionering med papirkurv",
@@ -493,6 +509,7 @@
"Your email app should open to let you choose the recipient and send it from your own address.": "Din e-mail-app bør åbne, så du kan vælge modtageren og sende den fra din egen adresse.",
"days": "dage",
"deleted": "slettet",
"deny": "afvis",
"directories": "kataloger",
"file": "fil",
"files": "filer",
@@ -500,6 +517,7 @@
"full documentation": "fuld dokumentation",
"items": "filer",
"modified": "ændret",
"permit": "tillad",
"seconds": "sekunder",
"theme-name-black": "Sort",
"theme-name-dark": "Mørk",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Benachrichtigungen des Dateisystems nutzen, um Änderungen zu erkennen.",
"User Home": "Benutzer-Stammverzeichnis",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Benutzername / Passwort wurde für die Benutzeroberfläche nicht gesetzt. Bitte erwägen Sie dies einzurichten.",
"Using a QUIC connection over LAN": "Verwendet eine QUIC-Verbindung über LAN",
"Using a QUIC connection over WAN": "Verwendet eine QUIC-Verbindung über WAN",
"Using a direct TCP connection over LAN": "Verwendet eine direkte TCP-Verbindung über LAN",
"Using a direct TCP connection over WAN": "Verwendet eine direkte TCP-Verbindung über WAN",
"Version": "Version",

View File

@@ -199,7 +199,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Τακτική σάρωση ανά καθορισμένο διάστημα και απενεργοποίηση επιτήρησης αλλαγών",
"Periodic scanning at given interval and enabled watching for changes": "Τακτική σάρωση ανά καθορισμένο διάστημα και ενεργοποίηση επιτήρησης αλλαγών",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Τακτική σάρωση ανά καθορισμένο διάστημα και αποτυχία ενεργοποίησης επιτήρησης αλλαγών. Γίνεται νέα προσπάθεια κάθε 1m:",
"Please consult the release notes before performing a major upgrade.": "Παρακαλούμε, πριν από την εκτέλεση μιας σημαντικής αναβάθμισης, να συμβουλευτείς το σημείωμα που τη συνοδεύει. ",
"Please consult the release notes before performing a major upgrade.": "Παρακαλούμε, πριν από την εκτέλεση μιας σημαντικής αναβάθμισης, να συμβουλευτείς το σημείωμα που τη συνοδεύει.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Παρακαλώ όρισε στις ρυθμίσεις έναν χρήστη και έναν κωδικό πρόσβασης για τη διεπαφή.",
"Please wait": "Παρακαλώ περιμένετε",
"Prefix indicating that the file can be deleted if preventing directory removal": "Πρόθεμα που δείχνει ότι το αρχείο θα μπορεί να διαγραφεί αν εμποδίζει τη διαγραφή καταλόγου",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Use notifications from the filesystem to detect changed items.",
"User Home": "User Home",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
"Using a QUIC connection over LAN": "Using a QUIC connection over LAN",
"Using a QUIC connection over WAN": "Using a QUIC connection over WAN",
"Using a direct TCP connection over LAN": "Using a direct TCP connection over LAN",
"Using a direct TCP connection over WAN": "Using a direct TCP connection over WAN",
"Version": "Version",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Use notifications from the filesystem to detect changed items.",
"User Home": "User Home",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
"Using a QUIC connection over LAN": "Using a QUIC connection over LAN",
"Using a QUIC connection over WAN": "Using a QUIC connection over WAN",
"Using a direct TCP connection over LAN": "Using a direct TCP connection over LAN",
"Using a direct TCP connection over WAN": "Using a direct TCP connection over WAN",
"Version": "Version",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Usar notificaciones del sistema de archivos para detectar elementos cambiados.",
"User Home": "Carpeta de inicio del usuario",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "No se ha configurado el nombre de usuario/la contraseña para la autenticación de la GUI. Por favor, considere configurarlos.",
"Using a QUIC connection over LAN": "Usando una conexión QUIC a través de una LAN",
"Using a QUIC connection over WAN": "Usando una conexión QUIC a través de una WAN",
"Using a direct TCP connection over LAN": "Utilizar una conexión TCP directa a través de LAN",
"Using a direct TCP connection over WAN": "Utilizar una conexión TCP directa a través de la WAN",
"Version": "Versión",

View File

@@ -24,7 +24,7 @@
"Allow Anonymous Usage Reporting?": "Izenik gabeko erabiltze erreportak baimendu?",
"Allowed Networks": "Sare baimenduak",
"Alphabetic": "Alfabetikoa",
"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.": "Kanpoko kontrolagailu batek fitxategien bertsioak kudeatzen ditu. Fitxategiak kendu behar ditu errepertorio sinkronizatuan. Aplikaziorako ibilbideak espazioak baditu, komatxo artean egon behar du.",
"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.": "Kanpoko kontrolagailu batek fitxategien bertsioak kudeatzen ditu. Fitxategiak kendu behar ditu errepertorio sinkronizatuan. Aplikaziorako ibilbideak espazioak baditu, komatxo artean egon behar du.",
"Anonymous Usage Reporting": "Izenik gabeko erabiltze erreportak",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Erabilera anonimoko txostenaren formatua aldatu egin da. Formatu berria erabili nahi duzu?",
"Apply": "Ezarri",
@@ -121,7 +121,7 @@
"Enable NAT traversal": "NAT translazioa aktibatu",
"Enable Relaying": "Aldizkatzea posible",
"Enabled": "Aktibatuta",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Negatiboa ez den zenbaki bat hauta ezazu (\"2.35\" adib.) bai eta unitate bat. Disko osoaren ehuneko espazioa",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Negatiboa ez den zenbaki bat hauta ezazu (\"2.35\" adib.) bai eta unitate bat. Disko osoaren ehuneko espazioa",
"Enter a non-privileged port number (1024 - 65535).": "Abantailatua ez den portu zenbalki bat sar ezazu (1024 - 65535)",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Sartu komaz bereizitako helbideak (\"tcp://ip:port\", \"tcp://host:port\"), edo \"dynamic\" helbidea automatikoki bilatzeko.",
"Enter ignore patterns, one per line.": "Ezkluzio filtroak sar, lerro bakoitzean bat bakarrik.",
@@ -160,7 +160,7 @@
"GUI Theme": "Interfaze grafiko tema",
"General": "Orokorra",
"Generate": "Sortu",
"Global Discovery": "Aurkikuntza orokorra",
"Global Discovery": "Aurkikuntza orokorra",
"Global Discovery Servers": "Orokor aurkikuntza zerbitzaria",
"Global State": "Egoera orokorra",
"Help": "Laguntza",
@@ -212,7 +212,7 @@
"Mod. Device": "Gailu aldatzailea",
"Mod. Time": "Ordua aldatu",
"Move to top of queue": "Lerro bururat lekuz alda",
"Multi level wildcard (matches multiple directory levels)": "Hein anitzerako jokerra (errepertorio eta azpi errepertorioeri dagokiona)",
"Multi level wildcard (matches multiple directory levels)": "Hein anitzerako jokerra (errepertorio eta azpi errepertorioeri dagokiona)",
"Never": "Sekulan",
"New Device": "Tresna berria",
"New Folder": "Partekatze berria",
@@ -242,9 +242,9 @@
"Paused": "Gelditua",
"Paused (Unused)": "Geldi (erabili gabe)",
"Pending changes": "Egiteke dauden aldaketak",
"Periodic scanning at given interval and disabled watching for changes": "Emandako tartearen aldizkako miaketa, eta desaktibatuta aldaketak ikusteko",
"Periodic scanning at given interval and disabled watching for changes": "Emandako tartearen aldizkako miaketa, eta desaktibatuta aldaketak ikusteko",
"Periodic scanning at given interval and enabled watching for changes": "Emandako tartearen aldizkako miaketa, eta aktibatuta aldaketak ikusteko",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Emandako tartearen aldizkako miaketa, eta huts egin du aldaketak ikusteko ezarpenak, berriro saiatuz minuturo:",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Emandako tartearen aldizkako miaketa, eta huts egin du aldaketak ikusteko ezarpenak, berriro saiatuz minuturo:",
"Permanently add it to the ignore list, suppressing further notifications.": "Behin betiko gehitu ezikusien zerrendara, jakinarazpen gehiago ezabatuz.",
"Please consult the release notes before performing a major upgrade.": "Aktualizatze garrantzitsu bat egin baino lehen, bertsioaren oharrak begira itzazu.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Konfigurazio leihoan asma itzazu erabiltzale izen bat eta pasahitz bat",
@@ -342,7 +342,7 @@
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Iduri luke Syncthing gelditua dela, edo bestenaz arrazo bat bada interneten konekzioarekin. Berriz entsea zaitez…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Iduri luke Syncthing-ek arazo bat duela zure eskaera tratatzeko. Otoi, orrialdea freska ezazu edo bestenaz, arazoak segitzen badu, Syncthing berriz pitz ezazu .",
"Take me back": "Eraman nazazu atzera",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Erabiltzailearen Interfaze Grafikoaren GUI helbidea gainidatzita dago hasierako aukerengatik. Hemengo aldaketek ez dute ondoriorik izango gainidatzi aktibo dagoen bitartean.",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Erabiltzailearen Interfaze Grafikoaren GUI helbidea gainidatzita dago hasierako aukerengatik. Hemengo aldaketek ez dute ondoriorik izango gainidatzi aktibo dagoen bitartean.",
"The Syncthing Authors": "Syncthing autoreak",
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthing-en administrazio interfazea pentsatua da urrundikako helbideak pasahitzik gabe onartzeko !",
"The aggregated statistics are publicly available at the URL below.": "Estadistikak zuzen bide honetan publikoki ikusgarriak dira",
@@ -354,7 +354,7 @@
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Sartu den tresnaren ID-ak iduri du ez duela balio. 52 edo 56-ko ezaugarriko kadena baten itxura behar luke, hizkiak, zifrak eta baita ere tarte edo gioiez egina.",
"The folder ID cannot be blank.": "Partekatzearen ID-a ez da hutsa izaiten ahal",
"The folder ID must be unique.": "Partekatzearen ID-a bakarra izan behar da",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Beste gailuetako karpetaren edukia gainidatzi egingo da gailu honek duenaren berdina izan dadin. Hemen ez dauden fitxategiak beste gailu batzuetan ere ezabatuko dira. ",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Beste gailuetako karpetaren edukia gainidatzi egingo da gailu honek duenaren berdina izan dadin. Hemen ez dauden fitxategiak beste gailu batzuetan ere ezabatuko dira.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Gailu honetako karpetaren edukia gainidatzi egingo da beste gailuek dutenaren berdina izan dadin. Hemen gehitu berri diren fitxategiak ezabatuko dira.. ",
"The folder path cannot be blank.": "Partekatzeari buruzko bidea ez da hutsa izaiten ahal",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Hunako tarteak erabiliak dira: lehen orduan bertsio bat kontserbatua da 30 segundu guziz. Lehen egunean, bertsio bat ordu bakoitz, lehen 30 egunetan bertsio bat egunero. Handik harat, adinaren mugetan egonez, bertsio bat astero.",
@@ -416,8 +416,8 @@
"Waiting to Scan": "Eskaneatzeko zain",
"Waiting to Sync": "Sinkronizatzeko zain",
"Warning": "Abisua",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Kasu, bide hau dagoen partekatze baten karpeta ahaidea da (adibidez, \"{{otherFolder}}\"). Segitzen baduzu, azpi-karpeta berri bat sortu behar duzu, bestenaz arazoak sortzen ahal dira, fitxategi kentzeak edo doblatzeak.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Kasu, bide hau dagoen partekatze baten karpeta ahaidea da (adibidez, \"{{otherFolderLabel}}\" ({{otherFolder}}). ",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Kasu, bide hau dagoen partekatze baten karpeta ahaidea da (adibidez, \"{{otherFolder}}\"). Segitzen baduzu, azpi-karpeta berri bat sortu behar duzu, bestenaz arazoak sortzen ahal dira, fitxategi kentzeak edo doblatzeak.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Kasu, bide hau dagoen partekatze baten karpeta ahaidea da (adibidez, \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Kasu, bide hau \"{{otherFolder}}\" partekatzearen azpi-karpeta da. Arazoak emaiten ahal ditu, fitxategi kentzeak edo doblatzeak, adibidez.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Kasu, bide hau \"{{otherFolderLabel}}\" ({{otherFolder}}) partekatzearen azpi-karpeta da. Arazoak emaiten ahal ditu, fitxategi kentzeak edo doblatzeak, adibidez.",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Oharra: {{syncthingInotify}} moduko kanpoko behatzaile bat erabiltzen ari bazara, desaktibatuta dagoela ziurtatu behar duzu.",
@@ -442,6 +442,6 @@
"items": "Elementuak",
"seconds": "segunduak",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}}k \"{{folder}}\" partekatze hontan gomitatzen zaitu.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}k \"{{folderlabel}}\" ({{folder}}) hontan gomitatzen zaitu.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}k \"{{folderlabel}}\" ({{folder}}) hontan gomitatzen zaitu.",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} -ek gailu hau birsar lezake."
}

View File

@@ -205,7 +205,7 @@
"The rescan interval must be a non-negative number of seconds.": "L'intervalle d'analyse ne doit pas être un nombre négatif de secondes.",
"They are retried automatically and will be synced when the error is resolved.": "Ils seront automatiquement retentés et synchronisés quand l'erreur sera résolue.",
"This Device": "Cet appareil",
"This can easily give hackers access to read and change any files on your computer.": "Ceci peut aisément permettre à un intrus de lire et modifier n'importe quel fichier de votre ordinateur. ",
"This can easily give hackers access to read and change any files on your computer.": "Ceci peut aisément permettre à un intrus de lire et modifier n'importe quel fichier de votre ordinateur.",
"This is a major version upgrade.": "Il s'agit d'une mise à jour majeure.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Ce réglage contrôle l'espace disque requis dans le disque qui abrite votre répertoire utilisateur (pour la base de données d'indexation).",
"Time": "Heure",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Utiliser les notifications du système de fichiers pour détecter les éléments modifiés.",
"User Home": "Répertoire de base de l'utilisateur",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Utilisateur/Mot de passe n'ont pas été définis pour l'accès à l'interface graphique. Envisagez de le faire.",
"Using a QUIC connection over LAN": "Connexion QUIC sur LAN",
"Using a QUIC connection over WAN": "Connexion QUIC sur WAN",
"Using a direct TCP connection over LAN": "Connexion TCP directe LAN",
"Using a direct TCP connection over WAN": "Connexion TCP directe WAN",
"Version": "Version",

View File

@@ -10,7 +10,7 @@
"Add Device": "Apparaat taheakje",
"Add Folder": "Map taheakje",
"Add Remote Device": "Apparaat op Ofstân Taheakje",
"Add devices from the introducer to our device list, for mutually shared folders.": "Heakje apparaten fan de yntrodusearders ta oan ús apparatenlyst, foar mei-inoar dielde mappen.",
"Add devices from the introducer to our device list, for mutually shared folders.": "Heakje apparaten fan de yntrodusearders ta oan ús apparatenlyst, foar mei-inoar dielde mappen.",
"Add ignore patterns": "Foech nije negear-patroanen ta",
"Add new folder?": "Nije map taheakje?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Boppedat wurd it ynterfal foar in folledige wer-sken omheech brocht (kear 60 minuten, dit is in nije standert fan 1 oere). Jo kinne dit ek letter foar elke map hânmjittich ynstelle nei it kiezen fan Nee.",
@@ -65,7 +65,7 @@
"Connection Type": "Ferbiningstype",
"Connections": "Ferbinings",
"Connections via relays might be rate limited by the relay": "Ferbinings fia relays kinne yn harren rate beheind wurde troch it relay.",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "It konstant byhâlden fan feroarings is no ek beskikber foar Syncthing. Dit hâld feroarings op de skiif yn de gaten en skent allinnich de paden dy't feroare binne. De foardielen binne dat feroarings earder trochjûn wurde en dat minder skens nedich binne. ",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "It konstant byhâlden fan feroarings is no ek beskikber foar Syncthing. Dit hâld feroarings op de skiif yn de gaten en skent allinnich de paden dy't feroare binne. De foardielen binne dat feroarings earder trochjûn wurde en dat minder skens nedich binne.",
"Copied from elsewhere": "Oernommen fan earne oars",
"Copied from original": "Oernommen fan orizjineel",
"Copied!": "Kopiearre!",
@@ -333,7 +333,7 @@
"The Syncthing admin interface is configured to allow remote access without a password.": "De Syncthing haadbrûker-ynterfaasje is sa ynstelt dat tagong fan ôfstân sûnder wachtwurd tastean is.",
"The aggregated statistics are publicly available at the URL below.": "De fersammele statistiken binnen yn it publyk beskikber fia ûndersteande keppeling.",
"The cleanup interval cannot be blank.": "It ynterfal foar opromjen kin net leech wêze.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De konfiguraasje is bewarre mar noch net aktivearre. Syncthing moat werstarte om de nije konfiguraasje te aktivearren.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De konfiguraasje is bewarre mar noch net aktivearre. Syncthing moat werstarte om de nije konfiguraasje te aktivearren.",
"The device ID cannot be blank.": "It apparaat-ID kin net leech wêze.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "It apparaat-ID dat hjir ynfierd wurde kin, kin fûn wurde yn in it \"Askjes > ID sjen litte\" dialooch op de oare apparaten. Spaasjes en streepkes binne mooglik (wurde negeard).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "It ferkaaide brûkensrapport wurd eltse dei ferstjoerd. It wurd brûkt om algemiene platfoarmen, mapgruttens en app-ferzjes by te hâlden. As de rapportearre dataset feroaret, krije jo dit dialooch wer te sjen.",
@@ -407,7 +407,7 @@
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warskôging: As jo in eksterne sjogger lykas {{syncthingInotify}} brûke, bin der dan wiis fan dat dizze út stiet.",
"Watch for Changes": "Sjoch foar Feroarings",
"Watching for Changes": "Sjocht foar Feroarings",
"Watching for changes discovers most changes without periodic scanning.": "Sjen foar feroarings ûntdekt de measte feroarings sûnder periodyk skennen.",
"Watching for changes discovers most changes without periodic scanning.": "Sjen foar feroarings ûntdekt de measte feroarings sûnder periodyk skennen.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Hâld by it taheakjen fan in nij apparaat yn de holle dat it apparaat oan de oare kant ek taheakke wurde moat. ",
"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.": "Hâld by it taheakjen fan in nije map yn de holle dat de map-ID brûkt wurd om de mappen tusken apparaten mei-inoar te ferbinen. Se binne haadlettergefoelich en moatte oer alle apparaten eksakt oerienkomme.",
"Yes": "Ja",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Usa le notifiche dal filesystem per rilevare gli elementi modificati.",
"User Home": "Home Utente",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Utente/password non sono stati impostati per autenticazione GUI. Considerane la configurazione.",
"Using a QUIC connection over LAN": "Utilizza una connessione QUIC su LAN",
"Using a QUIC connection over WAN": "Utilizza una connessione QUIC su WAN",
"Using a direct TCP connection over LAN": "Utilizzo di una connessione TCP diretta su LAN",
"Using a direct TCP connection over WAN": "Utilizzo di una connessione TCP diretta su WAN",
"Version": "Versione",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "파일 시스템 알림을 사용하여 변경 항목을 감시합니다.",
"User Home": "사용자 홈 폴더",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "GUI 인증을 위한 사용자 이름과 비밀번호가 설정되지 않았습니다. 이들을 설정하는 것을 고려해 주십시오.",
"Using a QUIC connection over LAN": "QUIC 프로토콜을 이용한 근거리 통신망(LAN)을 통해 연결되어 있습니다.",
"Using a QUIC connection over WAN": "QUIC 프로토콜을 이용한 광역 통신망(WAN)을 통해 연결되어 있습니다.",
"Using a direct TCP connection over LAN": "TCP 프로토콜을 이용한 근거리 통신망(LAN)을 통해 직결되어 있습니다.",
"Using a direct TCP connection over WAN": "TCP 프로토콜을 이용한 광역 통신망(WAN)을 통해 직결되어 있습니다.",
"Version": "버전",

View File

@@ -156,7 +156,7 @@
"GUI Theme": "Valdymo skydelio apipavidalinimas",
"General": "Bendra",
"Generate": "Sukurti",
"Global Discovery": "Visuotinis matomumas",
"Global Discovery": "Visuotinis matomumas",
"Global Discovery Servers": "Visuotinio matomumo serveriai",
"Global State": "Visuotinė būsena",
"Help": "Pagalba",
@@ -327,7 +327,7 @@
"Syncthing is restarting.": "Syncthing perleidžiamas",
"Syncthing is upgrading.": "Syncthing atsinaujina.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Dabar, Syncthing palaiko ir automatiškai plėtotojams siunčia ataskaitas apie strigtis. Pagal numatymą, ši ypatybė yra įjungta.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing išjungta arba problemos su Interneto ryšių. Bandoma iš naujo...",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing išjungta arba problemos su Interneto ryšių. Bandoma iš naujo",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Atrodo, kad Syncthing, vykdydamas jūsų užklausą, susidūrė su problemomis. Prašome iš naujo įkelti puslapį, arba jei problema išlieka, iš naujo paleisti Syncthing.",
"TCP LAN": "TCP LAN (vietinis tinklas)",
"TCP WAN": "TCP WAN (platusis tinklas)",
@@ -353,7 +353,7 @@
"The following text will automatically be inserted into a new message.": "Šis tekstas bus automatiškai įterptas į naują žinutę.",
"The following unexpected items were found.": "Buvo rasti šie netikėti elementai.",
"The interval must be a positive number of seconds.": "Intervalas privalo būti teigiamas sekundžių skaičius.",
"The maximum age must be a number and cannot be blank.": "Maksimalus amžius turi būti skaitmuo ir negali būti tuščias laukelis.",
"The maximum age must be a number and cannot be blank.": "Maksimalus amžius turi būti skaitmuo ir negali būti tuščias laukelis.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maksimalus laikas kurį bus saugojama versija (dienomis, nustatykite 0 norėdami saugoti amžinai).",
"The number of days must be a number and cannot be blank.": "Dienų skaičius turi būti teigiamas skaičius.",
"The number of days to keep files in the trash can. Zero means forever.": "Kiek dienų laikyti failus šiukšliadėžėje. Nulis reiškia amžinai.",
@@ -392,7 +392,7 @@
"Up to Date": "Atnaujinta",
"Updated {%file%}": "Atnaujintas {{file}}",
"Upgrade": "Atnaujinimas",
"Upgrade To {%version%}": "Atnaujinti į {{version}}",
"Upgrade To {%version%}": "Atnaujinti į {{version}}",
"Upgrading": "Atnaujinama",
"Upload Rate": "Išsiuntimo greitis",
"Uptime": "Veiksnumo laikas",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Meldingen van het bestandssysteem gebruiken om gewijzigde items te detecteren.",
"User Home": "Thuismap gebruiker",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Gebruikersnaam/wachtwoord is niet ingesteld voor de GUI-authenticatie. Overweeg om het in te stellen.",
"Using a QUIC connection over LAN": "Een QUIC-verbinding via LAN gebruiken",
"Using a QUIC connection over WAN": "Een QUIC-verbinding via WAN gebruiken",
"Using a direct TCP connection over LAN": "Een directe TCP-verbinding via LAN gebruiken",
"Using a direct TCP connection over WAN": "Een directe TCP-verbinding via WAN gebruiken",
"Version": "Versie",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Używaj powiadomień systemu plików do wykrywania zmienionych elementów.",
"User Home": "Katalog domowy użytkownika",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Nazwa użytkownika i hasło do uwierzytelniania GUI nie zostały skonfigurowane. Zastanów się nad ich ustawieniem.",
"Using a QUIC connection over LAN": "Używane jest połączenie przez protokół QUIC w sieci lokalnej LAN",
"Using a QUIC connection over WAN": "Używane jest połączenie przez protokół QUIC w sieci rozległej WAN",
"Using a direct TCP connection over LAN": "Używane jest bezpośrednie połączenie przez protokół TCP w sieci lokalnej LAN",
"Using a direct TCP connection over WAN": "Używane jest bezpośrednie połączenie przez protokół TCP w sieci rozległej WAN",
"Version": "Wersja",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Usar notificações do sistema de ficheiros para detectar itens alterados.",
"User Home": "Pasta do usuário",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "O Usuário/Senha não foi definido para a autenticação da GUI. Por favor, considere defini-los.",
"Using a QUIC connection over LAN": "Usando conexão QUIC sobre LAN",
"Using a QUIC connection over WAN": "Usando conexão QUIC sobre WAN",
"Using a direct TCP connection over LAN": "Usando uma conexão TCP direta via LAN",
"Using a direct TCP connection over WAN": "Usando uma conexão TCP direta via WAN",
"Version": "Versão",

View File

@@ -55,7 +55,7 @@
"Connections": "Connections",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.",
"Copied from elsewhere": "Copiat din altă parte",
"Copied from original": "Copiat din original",
"Copied from original": "Copiat din original",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Danger!",
"Debugging Facilities": "Debugging Facilities",

View File

@@ -1,71 +1,121 @@
{
"A device with that ID is already added.": "Prístroj s týmto ID je už pridaný.",
"A device with that ID is already added.": "Zariadenie s týmto ID je už pridané.",
"A negative number of days doesn't make sense.": "Záporný počet dní nedáva zmysel.",
"A new major version may not be compatible with previous versions.": "Nová hlavná verzia nemusí byť kompatibilná s predchádzajúcimi verziami.",
"API Key": "API kľúč",
"About": "O aplikácii",
"Action": "Akcia",
"Actions": "Akcie",
"Active filter rules": "Aktívne pravidlá filtrovania",
"Add": "Pridať",
"Add Device": "Pridať zariadenie",
"Add Folder": "Pridať adresár",
"Add Remote Device": "Pridať vzdialené zariadenie",
"Add devices from the introducer to our device list, for mutually shared folders.": "Pre vzájomne zdieľané adresáre pridaj zariadenie od zavádzača do svojho zoznamu zariadení.",
"Add filter entry": "Pridať filter",
"Add ignore patterns": "Pridať ignorované vzory",
"Add new folder?": "Pridať nový adresár?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Navyše, interval plného skenovania bude navýšený (60krát, t.j. nová výchozia hodnota 1h). Môžete to nastaviť aj manuálne pre každý adresár ak zvolíte Nie.",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Navyše, interval plného skenovania bude navýšený (60krát, t.j. nová východzia hodnota 1h). Môžete to nastaviť aj manuálne pre každý adresár ak zvolíte Nie.",
"Address": "Adresa",
"Addresses": "Adresy",
"Advanced": "Pokročilé",
"Advanced Configuration": "Pokročilá konfigurácia",
"All Data": "Všetky dáta",
"All Time": "Celé obdobie",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Všetky adresáre zdieľané s týmto zariadením musia byť chránené heslom, aby všetky odoslané dáta boli bez daného hesla nečitateľné.",
"Allow Anonymous Usage Reporting?": "Povoliť anoynmné hlásenia o použivaní?",
"Allowed Networks": "Povolené siete",
"Alphabetic": "Abecedne",
"Altered by ignoring deletes.": "Zmenené ignorovaním zmazaných.",
"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.": "Externý príkaz sa stará o vytváranie verzií. Musí odstrániť súbor zo zdieľaného priečinka. Ak cesta k aplikácii obsahuje medzery, mala by byť v úvodzovkách.",
"Anonymous Usage Reporting": "Anonymné hlásenie o používaní",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formát anonymného hlásenia o používaní sa zmenil. Chcete prejsť na nový formát?",
"Apply": "Použiť",
"Are you sure you want to override all remote changes?": "Ste si istý, že chcete prepísať všetky vzdialené zmeny?",
"Are you sure you want to permanently delete all these files?": "Určite chcete vymazať všetky tieto súbory?",
"Are you sure you want to remove device {%name%}?": "Určite chcete odobrať zariadenie {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Určite chcete odobrať adresár {{label}}?",
"Are you sure you want to restore {%count%} files?": "Určite chcete obnoviť {{count}} súborov?",
"Are you sure you want to revert all local changes?": "Naozaj chcete vrátiť všetky lokálne zmeny?",
"Are you sure you want to upgrade?": "Určite chcete aktualizovať?",
"Authors": "Autori",
"Auto Accept": "Automatické prijatie",
"Automatic Crash Reporting": "Automatické hlásenie chýb",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatická aktualizácia teraz ponúka voľbu medzi stabilnými vydaniami a kandidátmi na vydanie.",
"Automatic upgrades": "Automatické aktualizácie",
"Automatic upgrades are always enabled for candidate releases.": "Automatické aktualizácie sú vždy povolené pre kandidátske vydania.",
"Automatically create or share folders that this device advertises at the default path.": "Automaticky vytvoriť alebo zdieľať adresáre, ktoré toto zariadenie ohlasuje, v predvolenom adresári.",
"Be careful!": "Buď opatrný!",
"Available debug logging facilities:": "Dostupné možnosti protokolov ladenia:",
"Be careful!": "Buďte opatrný!",
"Body:": "Obsah:",
"Bugs": "Chyby",
"Cancel": "Zrušiť",
"Changelog": "Záznam zmien",
"Clean out after": "Vyčistenie po",
"Clean out after": "Vyčist po",
"Cleaning Versions": "Čistenie verzií",
"Cleanup Interval": "Interval čistenia",
"Click to see full identification string and QR code.": "Kliknutím zobrazíte úplný identifikačný reťazec a QR kód.",
"Close": "Zatvoriť",
"Command": "Príkaz",
"Comment, when used at the start of a line": "Komentár, keď použité na začiatku riadku",
"Comment, when used at the start of a line": "Považované za komentár, keď použité na začiatku riadku",
"Compression": "Kompresia",
"Configuration Directory": "Konfiguračný adresár",
"Configuration File": "Konfiguračný súbor",
"Configured": "Nakonfigurované",
"Connected (Unused)": "Pripojené (Nepoužité)",
"Connection Error": "Chyba pripojenia",
"Connection Type": "Typ pripojenia",
"Connections": "Spojenia",
"Connections": "Pripojenia",
"Connections via relays might be rate limited by the relay": "Pripojenia cez relé môžu byť obmedzené rýchlosťou relé",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Nepretržité sledovanie zmien je už dostupné. Tým sa skenovanie spustí iba pre zmenené súbory. Výhoda je, že týmto spôsobom sa rýchlejšie šíria zmeny a nie je potrebných toľko veľa úplných skenov.",
"Copied from elsewhere": "Skoprírované odinakiaľ",
"Copied from original": "Skopírované z originálu",
"Copied!": "Skopírované!",
"Copy": "Kopírovať",
"Copy failed! Try to select and copy manually.": "Kopírovanie zlyhalo! Skúste vybrať a skopírovať manuálne.",
"Currently Shared With Devices": "Aktuálne zdieľané so zariadeniami",
"Custom Range": "Vlastný rozsah",
"Danger!": "Pozor!",
"Database Location": "Umiestnenie databázy",
"Debugging Facilities": "Ladenie",
"Default": "Predvolené",
"Default Configuration": "Predvolená konfigurácia",
"Default Device": "Predvolené zariadenie",
"Default Folder": "Predvolený priečinok",
"Default Ignore Patterns": "Predvolené ignorované vzory",
"Defaults": "Predvolené",
"Delete": "Zmazať",
"Delete Unexpected Items": "Odstrániť neočakávané položky",
"Deleted {%file%}": "Odstránené {{file}}",
"Deselect All": "Odznačiť všetko",
"Deselect devices to stop sharing this folder with.": "Zrušte výber zariadení, s ktorými chcete prestať zdieľať tento priečinok.",
"Deselect folders to stop sharing with this device.": "Zrušte výber priečnikov, s ktorými sa má ukončiť zdieľanie s týmto zariadením.",
"Device": "Zariadenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zariadenie \"{{name}}\" ({{device}} na {{address}}) sa chce pripojiť. Pridať nové zariadenie?",
"Device Certificate": "Certifikát zariadenia",
"Device ID": "ID zariadenia",
"Device Identification": "Identifikácia zariadenia",
"Device Name": "Názov zariadenia",
"Device is untrusted, enter encryption password": "Zariadenie je nedôveryhodné, zadajte šifrovacie heslo",
"Device rate limits": "Obmedzenia rýchlosti zariadenia",
"Device that last modified the item": "Zariadenie, ktoré naposledy pozmenilo položku",
"Devices": "Zariadenia",
"Disable Crash Reporting": "Zakázať hlásenia o zlyhaní",
"Disabled": "Odpojené",
"Disabled periodic scanning and disabled watching for changes": "Zakázané pravidelné skenovanie a vypnuté sledovanie zmien",
"Disabled periodic scanning and enabled watching for changes": "Zakázané pravidelné skenovanie a povolené sledovanie zmien",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Zakázané pravidelné skenovanie a zlyhalo nastavenie sledovania zmien, opakovanie každú 1 m:",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Zakáže porovnávanie a synchronizáciu povolení súborov. Užitočné v systémoch s neexistujúcimi alebo vlastnými povoleniami (napr. FAT, exFAT, Synology, Android).",
"Discard": "Zahodiť",
"Disconnected": "Odpojené",
"Disconnected (Inactive)": "Odpojené (neaktívne)",
"Disconnected (Unused)": "Odpojené (Nepoužité)",
"Discovered": "Zistené",
"Discovery": "Zisťovanie",
"Discovery Failures": "Zlyhania zisťovania",
"Discovery Status": "Stav zisťovania",
"Dismiss": "Odmietnuť",
"Do not add it to the ignore list, so this notification may recur.": "Nepridávať to do zoznamu ignorovaných, takže toto upozornenie sa môže opakovať.",
"Do not restore": "Neobnovovať",
"Do not restore all": "Neobnovovať všetko",
"Do you want to enable watching for changes for all your folders?": "Chcete zapnúť sledovanie zmien vo všetkých priečinkoch?",
@@ -75,26 +125,39 @@
"Downloading": "Sťahovanie",
"Edit": "Upraviť",
"Edit Device": "Upraviť zariadenie",
"Edit Device Defaults": "Upraviť predvolené nastavenia zariadenia",
"Edit Folder": "Upraviť priečinok",
"Edit Folder Defaults": "Upraviť predvolené nastavenia priečinka",
"Editing {%path%}.": "Úprava {{path}}.",
"Enable Crash Reporting": "Zapnúť hlásenie chýb",
"Enable NAT traversal": "Povoliť priechod NAT",
"Enable Relaying": "Povoliť prenosové uzly",
"Enabled": "Povolené",
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Umožňuje odosielanie rozšírených atribútov do iných zariadení a použitie prichádzajúcich rozšírených atribútov. Môže vyžadovať spustenie so zvýšenými oprávneniami.",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Umožňuje odosielanie rozšírených atribútov do iných zariadení, ale nepoužíva prichádzajúce rozšírené atribúty. To môže mať významný vplyv na výkon. Vždy povolené, keď je povolená možnosť „Synchronizovať rozšírené atribúty“.",
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Umožňuje odosielanie informácií o vlastníctve do iných zariadení a použitie prichádzajúcich informácií o vlastníctve. Zvyčajne vyžaduje spustenie so zvýšenými oprávneniami.",
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "Umožňuje odosielanie informácií o vlastníctve do iných zariadení, ale neuplatňuje prichádzajúce informácie o vlastníctve. To môže mať významný vplyv na výkon. Vždy povolené, keď je povolená možnosť „Synchronizovať vlastníctvo“.",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Vložte kladné číslo (napr. \"2,35\") a zvoľte jednotku. Percentá sa zobrazujú ako časť celkovej veľkosti disku.",
"Enter a non-privileged port number (1024 - 65535).": "Vložte číslo neprivilegovaného portu (1024-65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadajte adresy oddelené čiarkou (\"tcp://ip:port\", \"tcp://hostiteľ:port\") alebo \"dynamické\" pre automatické zistenie adresy.",
"Enter ignore patterns, one per line.": "Zadaj ignorované vzory, jeden na riadok.",
"Enter up to three octal digits.": "Zadajte max 3. číslice v osmičkovej sústave.",
"Error": "Chyba",
"Extended Attributes": "Rozšírené atribúty",
"Extended Attributes Filter": "Filter rozšírených atribútov",
"External": "Externé",
"External File Versioning": "Externé spracovanie verzií súborov",
"Failed Items": "Zlyhané položky",
"Failed to setup, retrying": "Nepodarilo sa nastaviť, opakujem.",
"Failed to load file versions.": "Nepodarilo sa načítať verzie súborov.",
"Failed to load ignore patterns.": "Nepodarilo sa načítať ignorované vzory.",
"Failed to setup, retrying": "Nepodarilo sa nastaviť, opakujem",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Zlyhanie pripojenia k IPv6 serverom je očakávané ak neexistujú žiadne IPv6 pripojenia.",
"File Pull Order": "Poradie sťahovania súborov",
"File Versioning": "Verzie súborov",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Súbory zmenené alebo zmazané aplikáciou Syncthing sú presunuté do adresára .stversions .",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Súbory premiestnené alebo zmazané aplikáciou Sycthing sú presunuté do verzií označených dátumov v adresári .stversions.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Soubory sú chránené pred zmenami na ostatních zariadeniach, ale zmeny provedené z tohto zariadenia budú rozoslané na zvyšok klastra.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Súbory sa synchronizujú z klastra, ale žiadne lokálne vykonané zmeny sa neodošlú do iných zariadení.",
"Filesystem Watcher Errors": "Chyby zo subsystému \"Filesystem watcher\"",
"Filter by date": "Filtrovanie podľa dátumu",
"Filter by name": "Filtrovanie podľa mena",
@@ -103,13 +166,19 @@
"Folder Label": "Označenie adresára",
"Folder Path": "Cesta k adresáru",
"Folder Type": "Typ adresára",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Typ priečinka „{{receiveEncrypted}}“ je možné nastaviť iba pri pridávaní nového priečinka.",
"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.": "Typ priečinka \"{{receiveEncrypted}}\" nie je možné po pridaní priečinka zmeniť. Musíte odstrániť priečinok, odstrániť alebo dešifrovať údaje na disku a znova pridať priečinok.",
"Folders": "Adresáre",
"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.": "Zlyhal pokus na spustenie sledovania zmien v týchto priečinkoch. Sledovania sa bude opakovane spúšťať každú minútu, takže možno sa čoskoro podarí vykonať. Ak problém zotrváva, skúste nájsť príčinu alebo požiadajte o pomoc.",
"Forever": "Navždy",
"Full Rescan Interval (s)": "Interval úplného skenu (s)",
"GUI": "GUI",
"GUI": "Grafické rozhranie (GUI)",
"GUI / API HTTPS Certificate": "Certifikát GUI / API HTTPS",
"GUI Authentication Password": "Prihlasovacie heslo do GUI",
"GUI Authentication User": "Prihlasovacie meno do GUI",
"GUI Authentication: Set User and Password": "Autentifikácia do webového rozhrania: Nastavenie mena a hesla",
"GUI Listen Address": "Adresa pre prístup do GUI",
"GUI Override Directory": "Adresár pre GUI Override",
"GUI Theme": "Grafická téma GUI",
"General": "Všeobecné",
"Generate": "Generovať",
@@ -117,43 +186,64 @@
"Global Discovery Servers": "Globálne zisťovacie servery",
"Global State": "Globálny status",
"Help": "Pomoc",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tip: Zistili sa iba pravidlá pre odmietnutie, pričom predvolená hodnota je odmietnutie. Zvážte pridanie „povoliť akékoľvek“ ako posledné pravidlo.",
"Home page": "Domovská stránka",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Vaše aktuálne nastavenia naznačujú, že funkciu nechcete povoliť. Automatické hlásenia zlyhaní boli vypnuté.",
"Identification": "Identifikácia",
"If untrusted, enter encryption password": "Ak nie je dôveryhodný, uveďte heslo na dešifrovanie",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Ak chcete ostatným používateľom tohto počítača zabrániť v prístupe k službe Syncthing a prostredníctvom nej k vašim súborom, zvážte nastavenie overenia.",
"Ignore": "Ignorovať",
"Ignore Patterns": "Ignorované vzory",
"Ignore Permissions": "Ignorované práva",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Ignorované vzory je možné pridať až po vytvorení priečinka. Ak je začiarknuté, po uložení sa zobrazí vstupné pole na zadanie ignorovaných vzorov.",
"Ignored Devices": "Ignorované zariadenia",
"Ignored Folders": "Ingorované priečinky",
"Ignored at": "Ignorované na",
"Included Software": "Zahrnutý softvér",
"Incoming Rate Limit (KiB/s)": "Limit pre sťahovanie (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Nesprávna konfigurácia môže poškodiť váš adresár a spôsobiť nefunkčnosť aplikácie Súbory.",
"Internally used paths:": "Interne používané cesty:",
"Introduced By": "Uvedené",
"Introducer": "Uvádzač",
"Inversion of the given condition (i.e. do not exclude)": "Inverzia danej podmienky (napr. nevynechať)",
"Keep Versions": "Ponechanie verzií",
"LDAP": "LDAP",
"Largest First": "Najprv najväčšie",
"Last 30 Days": "Posledných 30 dní",
"Last 7 Days": "Poslených 7 dní",
"Last Month": "Posledný mesiac",
"Last Scan": "Posledný sken",
"Last seen": "Naposledy videný",
"Latest Change": "Posledná zmena",
"Learn more": "Zisti viac",
"Learn more at {%url%}": "Viac sa dozviete na {{url}}",
"Limit": "Limit",
"Listener Failures": "Zlyhania pri načúvaní",
"Listener Status": "Stav načúvania",
"Listeners": "Načúvajúci",
"Loading data...": "Načítavanie údajov...",
"Loading...": "Načítavanie...",
"Local Additions": "Miestne doplnky",
"Local Discovery": "Lokálne vyhľadávanie",
"Local State": "Lokálny status",
"Local State (Total)": "Lokálny status (celkový)",
"Locally Changed Items": "Lokálne zmenené položky",
"Log": "Záznam",
"Log File": "Súbor denníka",
"Log tailing paused. Scroll to the bottom to continue.": "Pozastavený záznam do denníka . Ak chcete pokračovať, prejdite nadol.",
"Logs": "Záznamy",
"Major Upgrade": "Hlavná aktualizácia",
"Mass actions": "Hromadná akcia",
"Maximum Age": "Maximálny časový limit",
"Maximum single entry size": "Maximálna veľkosť jedného vstupu",
"Maximum total size": "Maximálna celková veľkosť",
"Metadata Only": "Iba metadáta",
"Minimum Free Disk Space": "Minimálna veľkosť voľného miesta na disku",
"Mod. Device": "Mod. zariadenie",
"Mod. Time": "Mod. čas",
"More than a month ago": "Pred viac ako mesiacom",
"More than a week ago": "Pred viac ako týždňom",
"More than a year ago": "Pred viac ako rokom",
"Move to top of queue": "Presun na začiatok poradia",
"Multi level wildcard (matches multiple directory levels)": "Viacúrovňový zástupný znak (zhoda naprieč viacerými úrovňami adresára)",
"Never": "Nikdy",
@@ -163,7 +253,9 @@
"No": "Nie",
"No File Versioning": "Bez verzií súbor",
"No files will be deleted as a result of this operation.": "Ako výsledok tejto operáciu nebudú zmazané žiadne súbory.",
"No rules set": "Nie sú nastavené žiadne pravidlá",
"No upgrades": "Bez aktualizácií",
"Not shared": "Nezdieľané",
"Notice": "Oznámenie",
"OK": "OK",
"Off": "Vypnúť",
@@ -173,24 +265,34 @@
"Out of Sync": "Nesynchronizované",
"Out of Sync Items": "Nezosynchronizované položky",
"Outgoing Rate Limit (KiB/s)": "Obmedzenie odchádzajúcej rýchlosti (KiB/s)",
"Override": "Prepísať",
"Override Changes": "Prepísať zmeny",
"Ownership": "Vlastníctvo",
"Path": "Cesta",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Cesta k adresáru na lokálnom počítači. Ak neexistuje, bude vytvorená. Znak vlnovky (~) môže byť použitý ako skratka pre",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Cesta, kde budú uložené verzie (ponechajte prázdne pre predvolený adresár .stversions v zdieľanom adresari)",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Cesta, kde budú uložené verzie (ponechajte prázdne pre predvolený adresár .stversions v zdieľanom priečinku).",
"Paths": "Cesty",
"Pause": "Pozastaviť",
"Pause All": "Pozastaviť všetky",
"Paused": "Pozastavené",
"Paused (Unused)": "Pozastavené (nepoužité)",
"Pending changes": "Čakajúce zmeny",
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a vypnuté sledovanie zmien.",
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a zapnuté sledovanie zmien.",
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a vypnuté sledovanie zmien",
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a zapnuté sledovanie zmien",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Pravidelné skenovanie v danom intervale a neúspešné nastavenie sledovania zmien, opakovanie každú 1 m:",
"Permanently add it to the ignore list, suppressing further notifications.": "Natrvalo pridať do zoznamu ignorovaných, čím potlačíte ďalšie upozornenia.",
"Please consult the release notes before performing a major upgrade.": "Pred spustením hlavnej aktualizácie si prosím prečítajte poznámky k vydaniu.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Zadajte prosím prihlasovanie meno a heslo v dialógovom okne nastavení.",
"Please wait": "Prosím čakajte",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix označujúci, že súbor môže byť odstránený, ak bráni odstráneniu adresára.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix označujúci, že vzory by mali ignorovať veľkosť písma.",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix označujúci, že súbor môže byť odstránený, ak bráni odstráneniu adresára",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix označujúci, že vzory by mali ignorovať veľkosť písma",
"Preparing to Sync": "Príprava na Sync",
"Preview": "Náhľad",
"Preview Usage Report": "Náhľad záznamu o používaní",
"QR code": "QR kód",
"QUIC LAN": "QUIC LAN",
"QUIC WAN": "QUIC WAN",
"QUIC connections are in most cases considered suboptimal": "Pripojenia QUIC sa vo väčšine prípadov považujú za suboptimálne",
"Quick guide to supported patterns": "Rýchly sprievodca podporovanými vzormi",
"Random": "Náhodne",
"Receive Encrypted": "Prijať šifrované",
@@ -198,9 +300,12 @@
"Received data is already encrypted": "Prijaté údaje sú už šifrované",
"Recent Changes": "Nedávne zmeny",
"Reduced by ignore patterns": "Znížené o ignorované vzory",
"Relay LAN": "Relé LAN",
"Relay WAN": "Relé WAN",
"Release Notes": "Poznámky k vydaniu",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Kandidáti na vydanie obsahujú najnovšie vlastnosti a opravy. Sú podobné tradičným dvojtýždenným vydaniam programu Syncthing.",
"Remote Devices": "Vzdialené zariadenia",
"Remote GUI": "Vzdialené GUI",
"Remove": "Odstrániť",
"Remove Device": "Odstrániť zariadenie",
"Remove Folder": "Odstrániť adresár",
@@ -216,91 +321,149 @@
"Resume": "Pokračovať",
"Resume All": "Pokračuj so všetkými",
"Reused": "Opakovane použité",
"Revert": "Vrátiť späť",
"Revert Local Changes": "Vrátiť lokálne zmeny",
"Save": "Uložiť",
"Scan Time Remaining": "Zostávajúci čas skenovania",
"Scanning": "Skenovanie",
"See external versioning help for supported templated command line parameters.": "Podporované šablónové parametre príkazového riadka nájdete v pomocníkovi pre externú správu verzií.",
"Select All": "Vybrať všetko",
"Select a version": "Zvoliť verziu",
"Select additional devices to share this folder with.": "Vyberte ďalšie zariadenia, s ktorými chcete zdieľať tento priečinok.",
"Select additional folders to share with this device.": "Vyberte ďalšie priečinky na zdieľanie s týmto zariadením.",
"Select latest version": "Zvoliť najnovšiu verziu",
"Select oldest version": "Zvoliť najstaršiu verziu",
"Send & Receive": "Prijímať a odosielať",
"Send Extended Attributes": "Odoslať rozšírené atribúty",
"Send Only": "Iba odosielať",
"Send Ownership": "Odoslať vlastníctvo",
"Set Ignores on Added Folder": "Nastaviť ignorované v pridanom priečinku",
"Settings": "Nastavenia",
"Share": "Zdieľať",
"Share Folder": "Zdieľať adresár",
"Share by Email": "Zdieľať e-mailom",
"Share by SMS": "Zdieľať cez SMS",
"Share this folder?": "Zdieľať tento adresár?",
"Shared Folders": "Zdieľané priečinky",
"Shared With": "Zdieľané s",
"Sharing": "Zdieľať",
"Show ID": "Zobraziť ID",
"Show QR": "Zobraziť QR",
"Show detailed discovery status": "Zobraziť podrobný stav objavovania",
"Show detailed listener status": "Zobraziť podrobný stav načúvania",
"Show diff with previous version": "Ukázať rozdiely s predchádzajúcou verziou",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazované namiesto ID zariadenia v štatúte klastra. Toto pomenovanie bude oznamovať ostatným zariadeniam ako voliteľné predvolené pomenovanie.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazované namiesto ID zariadenia v klastri. Ak je ponechané prázdne, bude nahradené pomenovaním, ktoré oznamuje zariadenie.",
"Shutdown": "Vypnutie",
"Shutdown Complete": "Vypnutie ukončené",
"Simple": "Jednoduché",
"Simple File Versioning": "Jednoduché verzie súborov",
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Single level wildcard (matches within a directory only)": "Zástupný znak jednej úrovne (zhoduje sa iba v rámci adresára)",
"Size": "Veľkosť",
"Smallest First": "Najmenší najprv",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Nepodarilo sa vytvoriť niektoré metódy objavovania ďalších zariadení alebo ohlasovanie tohto zariadenia:",
"Some items could not be restored:": "Niektoré položky nemôžu byť obnovené:",
"Some listening addresses could not be enabled to accept connections:": "Niektoré adresy pre načúvanie nemohli byť povolené pre prichádzajúce spojenia:",
"Source Code": "Zdrojový kód",
"Stable releases and release candidates": "Stabilné verzie a kandidáti na vydanie",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilné vydania sú odložené asi o dva týždne. Počas tejto doby prechádzajú testovaním ako kandidáti na vydanie.",
"Stable releases only": "Iba stabilné verzie",
"Staggered": "Rozložené verzie",
"Staggered File Versioning": "Rozložené verzie súborov",
"Start Browser": "Spusti prehliadač",
"Statistics": "Štatistika",
"Stopped": "Zastavené",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Ukladá a synchronizuje iba šifrované údaje. Priečinky na všetkých pripojených zariadeniach musia byť nastavené s rovnakým heslom alebo musia byť typu „{{receiveEncrypted}}“.",
"Subject:": "Predmet:",
"Support": "Podpora",
"Support Bundle": "Balík podpory",
"Sync Extended Attributes": "Synchronizovať rozšírené atribúty",
"Sync Ownership": "Synchronizovať vlastníctvo",
"Sync Protocol Listen Addresses": "Adresa načúvania synchronizačného protokolu",
"Sync Status": "Synchronizovať stav",
"Syncing": "Synchronizácia",
"Syncthing device ID for \"{%devicename%}\"": "ID Syncthing zariadenia pre \"{{devicename}}\"",
"Syncthing has been shut down.": "Syncthing bol vypnutý.",
"Syncthing includes the following software or portions thereof:": "Syncthing obsahuje nasledujúci software nebo jeho časti:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing je otvorený softvér s licenciou MPL v2.0.",
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing je program na nepretržitú synchronizáciu súborov. Synchronizuje súbory medzi dvoma alebo viacerými počítačmi v reálnom čase, bezpečne chránený pred zvedavými očami. Vaše údaje sú iba vašimi údajmi a vy si zaslúžite vybrať si, kde budú uložené, či budú zdieľané s nejakou treťou stranou a ako sa budú prenášať cez internet.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing načúva pre pokusy o pripojenie z iných zariadení na týchto sieťových adresách:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing nenačúva pre pokusy o pripojenie z iných zariadení na žiadnej adrese. Môžu fungovať iba odchádzajúce spojenia z tohto zariadenia.",
"Syncthing is restarting.": "Syncthing sa reštartuje.",
"Syncthing is upgrading.": "Syncthing sa aktualizuje.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing se zdá byť nefunkčný, alebo je problém s internetovým pripojením. Opakujem...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing teraz podporuje automatické hlásenie pádov vývojárom. Táto funkcia je predvolene povolená.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing se zdá byť nefunkčný, alebo je problém s internetovým pripojením. Opakujem…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Zdá sa, že v aplikácii Syncthing sa vyskytol problém so spracovaním vašej požiadavky. Ak problém pretrváva, obnovte stránku alebo reštartujte Syncthing.",
"TCP LAN": "TCP LAN",
"TCP WAN": "TCP WAN",
"Take me back": "Späť",
"The Syncthing admin interface is configured to allow remote access without a password.": "The Syncthing admin interface is configured to allow remote access without a password.",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Adresa GUI je potlačená možnosťami pri spustení. Kým trvá potlačenie, tu vykonané zmeny sú bez účinku.",
"The Syncthing Authors": "Autori Syncthing",
"The Syncthing admin interface is configured to allow remote access without a password.": "Rozhranie správcu Syncthing je nakonfigurované tak, aby umožňovalo vzdialený prístup bez hesla.",
"The aggregated statistics are publicly available at the URL below.": "Súhrnné štatistiky sú verejne dostupné na uvedenej URL.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.",
"The cleanup interval cannot be blank.": "Interval čistenia nemôže byť prázdny.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurácia bola uložená, ale nebola aktivovaná. Aby sa aktivovala nová konfigurácia, musíte reštartovať Syncthing.",
"The device ID cannot be blank.": "ID zariadenia nemôže byť prázdne.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID zariadenie pre vloženie môžete nájsť na druhom zariadení v dialógu \"Akcia > Zobraziť ID\". Medzery a pomlčky sú voliteľné (ignorované).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The folder ID cannot be blank.": "The folder ID cannot be blank.",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Zašifrovaná správa o používaní sa odosiela denne. Používa sa na sledovanie bežných platforiem, veľkostí priečinkov a verzií aplikácie. Ak sa nahlásený súbor údajov zmení, zobrazí sa vám toto dialógové okno znova.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Zdá sa, že zadané ID zariadenia nie je platné. Mal by to byť reťazec s 52 alebo 56 znakmi pozostávajúci z písmen a číslic, pričom medzery a pomlčky sú voliteľné.",
"The folder ID cannot be blank.": "ID priečinka nemôže byť prázdne.",
"The folder ID must be unique.": "ID adresára musí byť jedinečné.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Obsah priečinka na ostatných zariadeniach sa prepíše, aby bol totožný s týmto zariadením. Súbory, ktoré sa tu nenachádzajú, budú odstránené na ostatných zariadeniach.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Obsah priečinka na tomto zariadení sa prepíše, aby sa zhodoval s ostatnými zariadeniami. Lokálne pridané súbory budú vymazané.",
"The folder path cannot be blank.": "Cesta k adresáru nemôže byť prázdna.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.",
"The following items could not be synchronized.": "The following items could not be synchronized.",
"The following items were changed locally.": "Tieto položky boli zmenené lokálne",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Používajú sa tieto intervaly: počas prvej hodiny sa verzia uchováva každých 30 send, počas prvého dňa sa verzia uchováva každú hodinu, počas prvých 30 dní sa verzia uchováva každý deň, až do maximálneho veku sa verzia uchováva každých týždeň.",
"The following items could not be synchronized.": "Nasledujúce položky nebolo možné synchronizovať.",
"The following items were changed locally.": "Tieto položky boli zmenené lokálne.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "K objavovaniu ostatných zariadení a oznamovanie tohto zariadenia sa používajú nasledujúce metódy:",
"The following text will automatically be inserted into a new message.": "Nasledujúci text sa automaticky vloží do novej správy.",
"The following unexpected items were found.": "Našli sa nasledujúce neočakávané položky.",
"The interval must be a positive number of seconds.": "Interval musí byť kladný počet sekúnd.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval v sekundách pre spustenie čistenia v adresári s verziami. Nula čistenie vypína.",
"The maximum age must be a number and cannot be blank.": "Maximálny vek musí byť číslo a nemôže byť prázdne.",
"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 maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maximálny čas na uchovanie verzie (v dňoch, nastavte na 0, ak chcete verzie zachovať navždy).",
"The number of days must be a number and cannot be blank.": "Počet dní musí byť číslo a nemôže byť prázdny.",
"The number of days to keep files in the trash can. Zero means forever.": "Počet dní pre uchovanie súborov v koši. Nula znamená navždy.",
"The number of old versions to keep, per file.": "Počet uchovávaných starších verzií pre každý súbor.",
"The number of versions must be a number and cannot be blank.": "Počet verzií musí byť číslo a nemôže byť prázdny.",
"The path cannot be blank.": "Cesta nemôže byť prázdna.",
"The rate limit must be a non-negative number (0: no limit)": "Limit rýchlosti musí byť kladné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"The remote device has not accepted sharing this folder.": "Vzdialené zariadenie neprijalo zdieľanie tohto priečinka.",
"The remote device has paused this folder.": "Vzdialené zariadenie pozastavilo tento priečinok.",
"The rescan interval must be a non-negative number of seconds.": "Interval opätovného skenovania musí byť nezáporný počet sekúnd.",
"There are no devices to share this folder with.": "Neexistujú žiadne zariadenia, s ktorými sa by sa dal zdieľať tento priečinok.",
"There are no file versions to restore.": "Neexistujú žiadne verzie súborov na obnovenie.",
"There are no folders to share with this device.": "Neexistujú žiadne priečinky na zdieľanie s týmto zariadením.",
"They are retried automatically and will be synced when the error is resolved.": "Automaticky sa zopakujú a po odstránení chyby sa zosynchronizujú.",
"This Device": "Toto zariadenie",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
"This Month": "Tento mesiac",
"This can easily give hackers access to read and change any files on your computer.": "Toto môže jednoducho umožniť hackerom čítať a meniť všetky súbory na vašom počítači.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Toto zariadenie nemôže automaticky objaviť iné zariadenia ani oznámiť svoju vlastnú adresu, aby ju našli ostatní. Pripojiť sa môžu iba zariadenia so staticky nakonfigurovanými adresami.",
"This is a major version upgrade.": "Toto je hlavná aktualizácia.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Toto nastavenie kontroluje voľné miesto požadované na domovskom disku (napr. indexová databáza).",
"Time": "Čas",
"Time the item was last modified": "Čas poslednej zmeny položky",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Ak sa chcete pripojiť k zariadeniu Syncthing s názvom „{{devicename}}“, pridajte u seba nové vzdialené zariadenie s týmto ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Ak chcete povoliť pravidlo, začiarknite políčko. Ak chcete pravidlo odmietnuť, nechajte ho nezačiarknuté.",
"Today": "Dnes",
"Trash Can": "Kôš",
"Trash Can File Versioning": "Verzie súborov v koši",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX povolenia",
"Unavailable": "Nedostupné",
"Unavailable/Disabled by administrator or maintainer": "Nedostupné/Zakázané administrátorom alebo správcom",
"Undecided (will prompt)": "Nerozhodnuté (spýta sa)",
"Unexpected Items": "Neočakávané položky",
"Unexpected items have been found in this folder.": "V tomto priečinku sa našli neočakávané položky.",
"Unignore": "Prestať ignorovať",
"Unknown": "Neznáme",
"Unshared": "Nezdieľané",
"Unshared Devices": "Nezdieľané zariadenia",
"Unshared Folders": "Nezdieľané priečinky",
"Untrusted": "Nedôveryhodný",
"Up to Date": "Aktuálne",
"Updated {%file%}": "Aktualizovaný súbor {{file}}",
"Upgrade": "Aktualizácia",
"Upgrade To {%version%}": "Aktualizovať na {{version}}",
"Upgrading": "Aktualizácia",
@@ -308,6 +471,11 @@
"Uptime": "Doba prevádzky",
"Usage reporting is always enabled for candidate releases.": "Hlásenia o používaní sú pri kandidátoch na vydanie vždy povolené.",
"Use HTTPS for GUI": "Použiť HTTPS pre grafické rozhranie",
"Use notifications from the filesystem to detect changed items.": "Na zistenie zmenených položiek použite upozornenia zo súborového systému.",
"User Home": "Domovský adresár",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Používateľské meno/heslo nebolo nastavené pre overenie GUI. Zvážte jeho nastavenie.",
"Using a direct TCP connection over LAN": "Použitie priameho pripojenia TCP cez LAN",
"Using a direct TCP connection over WAN": "Použitie priameho pripojenia TCP cez WAN",
"Version": "Verzia",
"Versions": "Verzie",
"Versions Path": "Cesta k verziám",
@@ -315,28 +483,47 @@
"Waiting to Clean": "Čakám na čistenie",
"Waiting to Scan": "Čakám na sken",
"Waiting to Sync": "Čakám na Sync",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a parent directory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a parent directory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a subdirectory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning": "Varovanie",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Upozornenie, táto cesta je nadradeným adresárom existujúceho priečinka \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Upozornenie, táto cesta je nadradeným adresárom existujúceho priečinka \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Upozornenie, táto cesta je podadresárom existujúceho priečinka \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Upozornenie, táto cesta je podadresárom existujúceho priečinka \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Upozornenie: Ak používate externý sledovač, ako je {{syncthingInotify}}, mali by ste sa uistiť, že toto sledovanie je vypnuté.",
"Watch for Changes": "Sleduj zmeny",
"Watching for Changes": "Sledujú sa zmeny",
"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.",
"Watching for changes discovers most changes without periodic scanning.": "Sledovanie zmien odhalí väčšinu zmien bez pravidelného skenovania.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Pri pridávaní nového zariadenia majte na pamäti, že toto zariadenie musíte pridať aj na druhej strane.",
"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.": "Pri pridávaní nového priečinka majte na pamäti, že ID priečinka sa používa na prepojenie priečinkov medzi zariadeniami. Rozlišujú veľké a malé písmená a musia sa presne zhodovať medzi všetkými zariadeniami.",
"Yes": "Áno",
"Yesterday": "Včera",
"You can also copy and paste the text into a new message manually.": "Text môžete do novej správy skopírovať a vložiť aj ručne.",
"You can also select one of these nearby devices:": "Môžete tiež vybrať jedno z týchto blízkych zariadení:",
"You can change your choice at any time in the Settings dialog.": "Voľbu môžete kedykoľvek zmeniť v dialógu Nastavenia.",
"You can read more about the two release channels at the link below.": "O dvoch vydávacích kanáloch si môžete viacej prečítať v odkaze nižšie.",
"You have no ignored devices.": "Nemáte žiadne ignorované zariadenia.",
"You have no ignored folders.": "Nemáte žiadne ignorované priečinky.",
"You have unsaved changes. Do you really want to discard them?": "Niektoré zmeny ste neuložili. Chcete ich skutočne zahodiť?",
"You must keep at least one version.": "Musíte ponechať aspoň jednu verziu",
"You must keep at least one version.": "Musíte ponechať aspoň jednu verziu.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "V priečinku „{{receiveEncrypted}}“ by ste nikdy nemali nič lokálne pridávať ani meniť.",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Mala by sa otvoriť aplikácia SMS, aby ste si mohli vybrať príjemcu a odoslať správu z vlastného čísla.",
"Your email app should open to let you choose the recipient and send it from your own address.": "Vaša e-mailová aplikácia by sa mala otvoriť, aby ste si mohli vybrať príjemcu a odoslať správu zo svojej vlastnej adresy.",
"days": "dní",
"deleted": "vymazané",
"deny": "odoprieť",
"directories": "priečinky",
"file": "súbor",
"files": "súbory",
"folder": "priečinok",
"full documentation": "úplná dokumntácia",
"items": "položiek",
"modified": "zmenené",
"permit": "povolenie",
"seconds": "sekúnd",
"theme-name-black": "Čierny",
"theme-name-dark": "Tmavý",
"theme-name-default": "Predvolené",
"theme-name-light": "Svetlý",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce zdieľať adresár \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}})."
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} môže znova uviesť toto zariadenie."
}

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
"User Home": "Användarhem",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn/lösenord har inte ställts in för autentisering av det grafiska gränssnittet. Överväg att ställa in det.",
"Using a QUIC connection over LAN": "Använder en QUIC-anslutning över LAN",
"Using a QUIC connection over WAN": "Använder en QUIC-anslutning över WAN",
"Using a direct TCP connection over LAN": "Använda en direkt TCP-anslutning över LAN",
"Using a direct TCP connection over WAN": "Använda en direkt TCP-anslutning över WAN",
"Version": "Version",

View File

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

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "Değişen öğeleri tespit etmek için dosya sistemi bildirimleri kullanın.",
"User Home": "Kullanıcı Girişi",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Kullanıcı adı/Parola, GKA kimlik doğrulaması için ayarlanmadı. Lütfen ayarlamayı düşünün.",
"Using a QUIC connection over LAN": "LAN üzerinden QUIC bağlantısı kullanma",
"Using a QUIC connection over WAN": "WAN üzerinden QUIC bağlantısı kullanma",
"Using a direct TCP connection over LAN": "LAN üzerinden doğrudan TCP bağlantısı kullanma",
"Using a direct TCP connection over WAN": "WAN üzerinden doğrudan TCP bağlantısı kullanma",
"Version": "Sürüm",

View File

@@ -225,7 +225,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Khi thêm một thiết bị mới, hãy nhớ rằng thiết bị này cũng phải được thêm vào máy kia.",
"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.": "Khi thêm một thư mục mới, hãy nhớ rằng ID thư mục được dùng để gắn kết thư mục giữa các thiết bị với nhau. Chúng phải chính xác từng chữ, cả viết hoa và thường giữa tất cả thiết bị.",
"Yes": "Vâng",
"You can also select one of these nearby devices:": "Bạn cũng có thể chọn một trong những thiết bị ở gần :",
"You can also select one of these nearby devices:": "Bạn cũng có thể chọn một trong những thiết bị ở gần :",
"You can change your choice at any time in the Settings dialog.": "Bạn có thể thay đổi lựa chọn của bạn bất kỳ lúc nào trong hộp thoại cài đặt",
"You can read more about the two release channels at the link below.": "Bạn có thể đọc thêm về hai kênh cập nhật tại liên kết dưới đây.",
"You have no ignored devices.": "Bạn không có thiết bị được bỏ qua",

View File

@@ -474,6 +474,8 @@
"Use notifications from the filesystem to detect changed items.": "使用文件系统的通知来检测更改的项目。",
"User Home": "用户主目录",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "尚未为GUI身份验证设置用户名/密码。 请考虑进行设置。",
"Using a QUIC connection over LAN": "正使用局域网 QUIC 连接",
"Using a QUIC connection over WAN": "正使用广域网 QUIC 连接",
"Using a direct TCP connection over LAN": "通过局域网使用直接TCP连接",
"Using a direct TCP connection over WAN": "通过广域网使用直接TCP连接",
"Version": "版本",

View File

@@ -210,7 +210,7 @@
"Last seen": "最後可見",
"Latest Change": "最後更改",
"Learn more": "瞭解更多",
"Learn more at {%url%}": "在 {{url}}了解更多",
"Learn more at {%url%}": "在 {{url}}了解更多",
"Limit": "限制",
"Listener Failures": "偵聽程序失敗",
"Listener Status": "偵聽程序狀態",

View File

@@ -1 +1 @@
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Valencian","cs":"Czech","da":"Danish","de":"German","en":"English","en-GB":"English (United Kingdom)","es":"Spanish","eu":"Basque","fr":"French","fy":"Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean","lt":"Lithuanian","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian","ru":"Russian","si":"Sinhala","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (Simplified)","zh-HK":"Chinese (Traditional, Hong Kong)","zh-TW":"Chinese (Traditional)"}
var langPrettyprint = {"bg":"Bulgarian","ca":"Catalan","ca@valencia":"Valencian","cs":"Czech","da":"Danish","de":"German","en":"English","en-GB":"English (United Kingdom)","es":"Spanish","eu":"Basque","fr":"French","fy":"Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean","lt":"Lithuanian","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian","ru":"Russian","si":"Sinhala","sk":"Slovak","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (Simplified)","zh-HK":"Chinese (Traditional, Hong Kong)","zh-TW":"Chinese (Traditional)"}

View File

@@ -1 +1 @@
var validLangs = ["bg","ca@valencia","cs","da","de","en","en-GB","es","eu","fr","fy","hu","id","it","ja","ko-KR","lt","nl","pl","pt-BR","pt-PT","ro-RO","ru","si","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]
var validLangs = ["bg","ca","ca@valencia","cs","da","de","en","en-GB","es","eu","fr","fy","hu","id","it","ja","ko-KR","lt","nl","pl","pt-BR","pt-PT","ro-RO","ru","si","sk","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]

View File

@@ -288,7 +288,7 @@
<div class="panel-body">
<p ng-repeat="err in errorList()">
<small>{{err.when | date:"yyyy-MM-dd HH:mm:ss"}}:</small>
<span ng-bind-html="friendlyDevices(err.message) | linky: '_blank'"></span>
<span ng-bind="friendlyDevices(err.message)"></span>
</p>
</div>
<div class="panel-footer">
@@ -553,8 +553,14 @@
</tr>
<tr>
<th><span class="fas fa-fw fa-share-alt"></span>&nbsp;<span translate>Shared With</span></th>
<td class="text-right">
<span tooltip data-original-title="{{sharesFolder(folder)}} {{folderHasUnacceptedDevices(folder) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : ''}} {{folderHasPausedDevices(folder) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : ''}}" ng-bind-html="sharesFolder(folder)"></span>
<td class="text-right no-overflow-ellipse word-break-all">
<span ng-repeat="device in folder.devices">
<span ng-if="device.deviceID != myID" ng-switch="completion[device.deviceID][folder.id].remoteState">
<span ng-switch-when="notSharing" data-original-title="{{'The remote device has not accepted sharing this folder.' | translate}}" tooltip>{{deviceName(devices[device.deviceID])}}<sup>1</sup><span ng-if="!$last">,</span></span>
<span ng-switch-when="paused" data-original-title="{{'The remote device has paused this folder.' | translate}}" tooltip>{{deviceName(devices[device.deviceID])}}<sup>2</sup><span ng-if="!$last">,</span></span>
<span ng-switch-default>{{deviceName(devices[device.deviceID])}}<span ng-if="!$last">,</span></span>
</span>
</span>
</td>
</tr>
<tr ng-if="folderStats[folder.id].lastScan">
@@ -883,8 +889,14 @@
</tr>
<tr ng-if="deviceFolders(deviceCfg).length > 0">
<th><span class="fas fa-fw fa-folder"></span>&nbsp;<span translate>Folders</span></th>
<td class="text-right">
<span tooltip data-original-title="{{sharedFolders(deviceCfg)}} {{deviceHasUnacceptedFolders(deviceCfg) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : '' }} {{deviceHasPausedFolders(deviceCfg) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : '' }}" ng-bind-html="sharedFolders(deviceCfg)"></span>
<td class="text-right no-overflow-ellipse word-break-all">
<span ng-repeat="folderID in deviceFolders(deviceCfg)">
<span ng-switch="completion[deviceCfg.deviceID][folderID].remoteState">
<span ng-switch-when="notSharing" data-original-title="{{'The remote device has not accepted sharing this folder.' | translate}}" tooltip>{{folderLabel(folderID)}}<sup>1</sup><span ng-if="!$last">,</span></span>
<span ng-switch-when="paused" data-original-title="{{'The remote device has paused this folder.' | translate}}" tooltip>{{folderLabel(folderID)}}<sup>2</sup><span ng-if="!$last">,</span></span>
<span ng-switch-default>{{folderLabel(folderID)}}<span ng-if="!$last">,</span></span>
</span>
</span>
</td>
</tr>
<tr ng-if="deviceCfg.remoteGUIPort > 0">

View File

@@ -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, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, 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 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, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, 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, Kalle Laine, Karol Różycki, Kebin Liu, 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, 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, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, 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, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, 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 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, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, 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 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, 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, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, 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>
@@ -86,37 +86,37 @@ Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexan
<tbody>
<tr>
<th translate>User Home</th>
<td><code class="file-path">{{ about.paths['baseDir-userHome'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['baseDir-userHome'] }}</code></td>
</tr>
<tr>
<th><strong translate>Configuration Directory</strong></th>
<td><code class="file-path"><strong>{{ about.paths['baseDir-config'] }}</strong></code></td>
<td><code class="word-break-all"><strong>{{ about.paths['baseDir-config'] }}</strong></code></td>
</tr>
<tr>
<th translate>Configuration File</th>
<td><code class="file-path">{{ about.paths['config'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['config'] }}</code></td>
</tr>
<tr>
<th translate>Device Certificate</th>
<td><code class="file-path">{{ about.paths['certFile'] }}</code>
<br /><code class="file-path">{{ about.paths['keyFile'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['certFile'] }}</code>
<br /><code class="word-break-all">{{ about.paths['keyFile'] }}</code></td>
</tr>
<tr>
<th translate>GUI / API HTTPS Certificate</th>
<td><code class="file-path">{{ about.paths['httpsCertFile'] }}</code>
<br /><code class="file-path">{{ about.paths['httpsKeyFile'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['httpsCertFile'] }}</code>
<br /><code class="word-break-all">{{ about.paths['httpsKeyFile'] }}</code></td>
</tr>
<tr>
<th translate>Database Location</th>
<td><code class="file-path">{{ about.paths['database'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['database'] }}</code></td>
</tr>
<tr>
<th translate>Log File</th>
<td><code class="file-path">{{ about.paths['logFile'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['logFile'] }}</code></td>
</tr>
<tr>
<th translate>GUI Override Directory</th>
<td><code class="file-path">{{ about.paths['guiAssets'] }}</code></td>
<td><code class="word-break-all">{{ about.paths['guiAssets'] }}</code></td>
</tr>
</tbody>
</table>

View File

@@ -1,7 +1,13 @@
<div class="col-md-6 checkbox">
<label for="sharedwith-{{id}}">
<input id="sharedwith-{{id}}" ng-model="selected[id]" type="checkbox" />
<span tooltip data-original-title="{{id}}" ng-bind-html="label"></span>
<span tooltip data-original-title="{{id}}">
<span ng-switch="remoteState">
<span ng-switch-when="notSharing">{{label}}<sup>1</sup></span>
<span ng-switch-when="paused">{{label}}<sup>2</sup></span>
<span ng-switch-default>{{label}}</span>
</span>
</span>
</label>
</div>
<div class="col-md-6">

View File

@@ -70,7 +70,7 @@ angular.module('syncthing.core')
});
// inform syncthingContoller that a modal is ready
// inform syncthingController that a modal is ready
scope.$parent.modalLoaded();
}
};

View File

@@ -1256,8 +1256,9 @@ angular.module('syncthing.core')
case "relaywan":
return $translate.instant('Connections via relays might be rate limited by the relay');
case "quiclan":
return $translate.instant('Using a QUIC connection over LAN');
case "quicwan":
return $translate.instant('QUIC connections are in most cases considered suboptimal');
return $translate.instant('Using a QUIC connection over WAN');
case "tcpwan":
return $translate.instant('Using a direct TCP connection over WAN');
case "tcplan":
@@ -2451,30 +2452,6 @@ angular.module('syncthing.core')
+ '&device=' + encodeURIComponent(deviceID));
};
$scope.deviceNameMarkRemoteState = function (deviceID, folderID) {
var name = $scope.deviceName($scope.devices[deviceID]);
// Add footnote if sharing was not accepted on the remote device
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID]) {
if ($scope.completion[deviceID][folderID].remoteState == 'notSharing') {
name += '<sup>1</sup>';
} else if ($scope.completion[deviceID][folderID].remoteState == 'paused') {
name += '<sup>2</sup>';
}
}
return name;
};
$scope.sharesFolder = function (folderCfg) {
var names = [];
folderCfg.devices.forEach(function (device) {
if (device.deviceID !== $scope.myID) {
names.push($scope.deviceNameMarkRemoteState(device.deviceID, folderCfg.id));
}
});
names.sort();
return names.join(", ");
};
$scope.folderHasUnacceptedDevices = function (folderCfg) {
for (var deviceID in $scope.completion) {
if (deviceID in $scope.devices
@@ -2518,27 +2495,6 @@ angular.module('syncthing.core')
return label && label.length > 0 ? label : folderID;
};
$scope.folderLabelMarkRemoteState = function (folderID, deviceID) {
var label = $scope.folderLabel(folderID);
// Add footnote if sharing was not accepted on the remote device
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID]) {
if ($scope.completion[deviceID][folderID].remoteState == 'notSharing') {
label += '<sup>1</sup>';
} else if ($scope.completion[deviceID][folderID].remoteState == 'paused') {
label += '<sup>2</sup>';
}
}
return label;
};
$scope.sharedFolders = function (deviceCfg) {
var labels = [];
$scope.deviceFolders(deviceCfg).forEach(function (folderID) {
labels.push($scope.folderLabelMarkRemoteState(folderID, deviceCfg.deviceID));
});
return labels.join(', ');
};
$scope.deviceHasUnacceptedFolders = function (deviceCfg) {
if (!(deviceCfg.deviceID in $scope.completion)) {
return false;
@@ -3428,6 +3384,7 @@ angular.module('syncthing.core')
id: '@',
label: '@',
folderType: '@',
remoteState: '@',
untrusted: '=',
},
link: function (scope, elem, attrs) {

View File

@@ -3,9 +3,7 @@ angular.module('syncthing.core')
return {
restrict: 'A',
link: function (scope, element, attributes) {
$(element).tooltip({
html: 'true'
});
$(element).tooltip();
}
};
});

View File

@@ -92,7 +92,7 @@
<a href="#" ng-click="selectAllSharedFolders(false)" translate>Deselect All</a></small>
</p>
<div class="form-group" ng-repeat="folder in currentSharing.shared">
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabelMarkRemoteState(folder.id, currentDevice.deviceID)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" />
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabel(folder.id)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" remote-state="{{completion[currentDevice.deviceID][folder.id].remoteState}}" />
</div>
<p class="help-block" ng-if="deviceHasUnacceptedFolders(currentDevice)">
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>

View File

@@ -28,7 +28,7 @@
<span ng-switch-default>{{changeEvent.data.type}}</span>
</td>
<td class="no-overflow-ellipse">{{folderLabel(changeEvent.data.folder)}}</td>
<td class="file-path no-overflow-ellipse">{{changeEvent.data.path}}</td>
<td class="word-break-all no-overflow-ellipse">{{changeEvent.data.path}}</td>
<td class="no-overflow-ellipse">{{changeEvent.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr>
</tbody>

View File

@@ -56,7 +56,7 @@
<a href="#" ng-click="selectAllSharedDevices(false)" translate>Deselect All</a></small>
</p>
<div class="form-group" ng-repeat="device in currentSharing.shared">
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceNameMarkRemoteState(device.deviceID, currentFolder.id)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" />
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceName(device)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" remote-state="{{completion[device.deviceID][currentFolder.id].remoteState}}" />
</div>
<p class="help-block" ng-if="folderHasUnacceptedDevices(currentFolder)">
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>

View File

@@ -15,7 +15,7 @@
</tr>
</thead>
<tr dir-paginate="file in localChanged.files | itemsPerPage: localChanged.perpage" current-page="localChanged.page" total-items="model[localChangedFolder].receiveOnlyTotalItems" pagination-id="localChanged">
<td class="file-path">{{file.name}}</td>
<td class="word-break-all">{{file.name}}</td>
<td><span ng-hide="file.type == 'DIRECTORY'">{{file.size | binary}}B</span></td>
</tr>
</table>

View File

@@ -22,7 +22,7 @@
</tr>
</thead>
<tr dir-paginate="file in remoteNeed[folder].files | itemsPerPage: remoteNeed[folder].perpage" current-page="remoteNeed[folder].page" total-items="completion[remoteNeedDevice.deviceID][folder].needItems" pagination-id="'remoteNeed-' + folder">
<td class="file-path">{{file.name}}</td>
<td class="word-break-all">{{file.name}}</td>
<td><span ng-hide="file.type == 'DIRECTORY'">{{file.size | binary}}B</span></td>
<td>{{file.modified | date:"yyyy-MM-dd HH:mm:ss"}}</td>
<td ng-if="file.modifiedBy">{{friendlyNameFromShort(file.modifiedBy)}}</td>

View File

@@ -775,9 +775,9 @@ func (s *service) getDBBrowse(w http.ResponseWriter, r *http.Request) {
}
func (s *service) getDBCompletion(w http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
var folder = qs.Get("folder") // empty means all folders
var deviceStr = qs.Get("device") // empty means local device ID
qs := r.URL.Query()
folder := qs.Get("folder") // empty means all folders
deviceStr := qs.Get("device") // empty means local device ID
// We will check completion status for either the local device, or a
// specific given device ID.
@@ -814,14 +814,14 @@ func (s *service) getDBStatus(w http.ResponseWriter, r *http.Request) {
}
func (s *service) postDBOverride(_ http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
var folder = qs.Get("folder")
qs := r.URL.Query()
folder := qs.Get("folder")
go s.model.Override(folder)
}
func (s *service) postDBRevert(_ http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
var folder = qs.Get("folder")
qs := r.URL.Query()
folder := qs.Get("folder")
go s.model.Revert(folder)
}
@@ -1015,7 +1015,7 @@ func (s *service) postSystemRestart(w http.ResponseWriter, _ *http.Request) {
}
func (s *service) postSystemReset(w http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
qs := r.URL.Query()
folder := qs.Get("folder")
if len(folder) > 0 {
@@ -1210,7 +1210,6 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
l.Warnln("Support bundle: failed to serialize usage-reporting.json.txt", err)
} else {
files = append(files, fileEntry{name: "usage-reporting.json.txt", data: usageReportingData})
}
}
@@ -1243,7 +1242,7 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
zipFilePath := filepath.Join(locations.GetBaseDir(locations.ConfigBaseDir), zipFileName)
// Write buffer zip to local zip file (back up)
if err := os.WriteFile(zipFilePath, zipFilesBuffer.Bytes(), 0600); err != nil {
if err := os.WriteFile(zipFilePath, zipFilesBuffer.Bytes(), 0o600); err != nil {
l.Warnln("Support bundle: support bundle zip could not be created:", err)
}
@@ -1299,7 +1298,6 @@ func (s *service) getReport(w http.ResponseWriter, r *http.Request) {
} else {
sendJSON(w, r)
}
}
func (*service) getRandomString(w http.ResponseWriter, r *http.Request) {
@@ -1497,8 +1495,8 @@ func (s *service) postSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
func (s *service) makeDevicePauseHandler(paused bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
var deviceStr = qs.Get("device")
qs := r.URL.Query()
deviceStr := qs.Get("device")
var msg string
var status int
@@ -1573,8 +1571,8 @@ func (*service) getHealth(w http.ResponseWriter, _ *http.Request) {
}
func (*service) getQR(w http.ResponseWriter, r *http.Request) {
var qs = r.URL.Query()
var text = qs.Get("text")
qs := r.URL.Query()
text := qs.Get("text")
code, err := qr.Encode(text, qr.M)
if err != nil {
http.Error(w, "Invalid", http.StatusInternalServerError)
@@ -1655,7 +1653,6 @@ func (s *service) getFolderErrors(w http.ResponseWriter, r *http.Request) {
page, perpage := getPagingParams(qs)
errors, err := s.model.FolderErrors(folder)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
@@ -1687,7 +1684,21 @@ func (*service) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
var fsType fs.FilesystemType
fsType.UnmarshalText([]byte(qs.Get("filesystem")))
sendJSON(w, browseFiles(current, fsType))
sendJSON(w, browse(fsType, current))
}
func browse(fsType fs.FilesystemType, current string) []string {
if current == "" {
return browseRoots(fsType)
}
parent, base := parentAndBase(current)
ffs := fs.NewFilesystem(fsType, parent)
files := browseFiles(ffs, base)
for i := range files {
files[i] = filepath.Join(parent, files[i])
}
return files
}
const (
@@ -1708,14 +1719,18 @@ func checkPrefixMatch(s, prefix string) int {
return noMatch
}
func browseFiles(current string, fsType fs.FilesystemType) []string {
if current == "" {
filesystem := fs.NewFilesystem(fsType, "")
if roots, err := filesystem.Roots(); err == nil {
return roots
}
return nil
func browseRoots(fsType fs.FilesystemType) []string {
filesystem := fs.NewFilesystem(fsType, "")
if roots, err := filesystem.Roots(); err == nil {
return roots
}
return nil
}
// parentAndBase returns the parent directory and the remaining base of the
// path. The base may be empty if the path ends with a path separator.
func parentAndBase(current string) (string, string) {
search, _ := fs.ExpandTilde(current)
pathSeparator := string(fs.PathSeparator)
@@ -1731,24 +1746,27 @@ func browseFiles(current string, fsType fs.FilesystemType) []string {
searchFile = filepath.Base(search)
}
fs := fs.NewFilesystem(fsType, searchDir)
return searchDir, searchFile
}
subdirectories, _ := fs.DirNames(".")
func browseFiles(ffs fs.Filesystem, search string) []string {
subdirectories, _ := ffs.DirNames(".")
pathSeparator := string(fs.PathSeparator)
exactMatches := make([]string, 0, len(subdirectories))
caseInsMatches := make([]string, 0, len(subdirectories))
for _, subdirectory := range subdirectories {
info, err := fs.Stat(subdirectory)
info, err := ffs.Stat(subdirectory)
if err != nil || !info.IsDir() {
continue
}
switch checkPrefixMatch(subdirectory, searchFile) {
switch checkPrefixMatch(subdirectory, search) {
case matchExact:
exactMatches = append(exactMatches, filepath.Join(searchDir, subdirectory)+pathSeparator)
exactMatches = append(exactMatches, subdirectory+pathSeparator)
case matchCaseIns:
caseInsMatches = append(caseInsMatches, filepath.Join(searchDir, subdirectory)+pathSeparator)
caseInsMatches = append(caseInsMatches, subdirectory+pathSeparator)
}
}

View File

@@ -39,6 +39,7 @@ import (
"github.com/syncthing/syncthing/lib/model"
modelmocks "github.com/syncthing/syncthing/lib/model/mocks"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil"
@@ -1168,45 +1169,39 @@ func TestBrowse(t *testing.T) {
pathSep := string(os.PathSeparator)
tmpDir := t.TempDir()
ffs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?nostfolder=true")
if err := os.Mkdir(filepath.Join(tmpDir, "dir"), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(tmpDir, "file"), []byte("hello"), 0644); err != nil {
t.Fatal(err)
}
if err := os.Mkdir(filepath.Join(tmpDir, "MiXEDCase"), 0755); err != nil {
t.Fatal(err)
}
_ = ffs.Mkdir("dir", 0o755)
_ = fs.WriteFile(ffs, "file", []byte("hello"), 0o644)
_ = ffs.Mkdir("MiXEDCase", 0o755)
// We expect completion to return the full path to the completed
// directory, with an ending slash.
dirPath := filepath.Join(tmpDir, "dir") + pathSep
mixedCaseDirPath := filepath.Join(tmpDir, "MiXEDCase") + pathSep
dirPath := "dir" + pathSep
mixedCaseDirPath := "MiXEDCase" + pathSep
cases := []struct {
current string
returns []string
}{
// The directory without slash is completed to one with slash.
{tmpDir, []string{tmpDir + pathSep}},
{"dir", []string{"dir" + pathSep}},
// With slash it's completed to its contents.
// Dirs are given pathSeps.
// Files are not returned.
{tmpDir + pathSep, []string{mixedCaseDirPath, dirPath}},
{"", []string{mixedCaseDirPath, dirPath}},
// Globbing is automatic based on prefix.
{tmpDir + pathSep + "d", []string{dirPath}},
{tmpDir + pathSep + "di", []string{dirPath}},
{tmpDir + pathSep + "dir", []string{dirPath}},
{tmpDir + pathSep + "f", nil},
{tmpDir + pathSep + "q", nil},
{"d", []string{dirPath}},
{"di", []string{dirPath}},
{"dir", []string{dirPath}},
{"f", nil},
{"q", nil},
// Globbing is case-insensitive
{tmpDir + pathSep + "mixed", []string{mixedCaseDirPath}},
{"mixed", []string{mixedCaseDirPath}},
}
for _, tc := range cases {
ret := browseFiles(tc.current, fs.FilesystemTypeBasic)
ret := browseFiles(ffs, tc.current)
if !util.EqualStrings(ret, tc.returns) {
t.Errorf("browseFiles(%q) => %q, expected %q", tc.current, ret, tc.returns)
}

View File

@@ -27,15 +27,21 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
)
var device1, device2, device3, device4 protocol.DeviceID
var testFs fs.Filesystem
func init() {
device1, _ = protocol.DeviceIDFromString("AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ")
device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
device3, _ = protocol.DeviceIDFromString("LGFPDIT-7SKNNJL-VJZA4FC-7QNCRKA-CE753K7-2BW5QDK-2FOZ7FR-FEP57QJ")
device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
testFs = fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
loadTestFiles()
}
func TestDefaultValues(t *testing.T) {
@@ -47,44 +53,49 @@ func TestDefaultValues(t *testing.T) {
Version: CurrentVersion,
Folders: []FolderConfiguration{},
Options: OptionsConfiguration{
RawListenAddresses: []string{"default"},
RawGlobalAnnServers: []string{"default"},
GlobalAnnEnabled: true,
LocalAnnEnabled: true,
LocalAnnPort: 21027,
LocalAnnMCAddr: "[ff12::8384]:21027",
MaxSendKbps: 0,
MaxRecvKbps: 0,
ReconnectIntervalS: 60,
RelaysEnabled: true,
RelayReconnectIntervalM: 10,
StartBrowser: true,
NATEnabled: true,
NATLeaseM: 60,
NATRenewalM: 30,
NATTimeoutS: 10,
AutoUpgradeIntervalH: 12,
KeepTemporariesH: 24,
CacheIgnoredFiles: false,
ProgressUpdateIntervalS: 5,
LimitBandwidthInLan: false,
MinHomeDiskFree: Size{1, "%"},
URURL: "https://data.syncthing.net/newdata",
URInitialDelayS: 1800,
URPostInsecurely: false,
ReleasesURL: "https://upgrades.syncthing.net/meta.json",
AlwaysLocalNets: []string{},
OverwriteRemoteDevNames: false,
TempIndexMinBlocks: 10,
UnackedNotificationIDs: []string{"authenticationUserAndPassword"},
SetLowPriority: true,
CRURL: "https://crash.syncthing.net/newcrash",
CREnabled: true,
StunKeepaliveStartS: 180,
StunKeepaliveMinS: 20,
RawStunServers: []string{"default"},
AnnounceLANAddresses: true,
FeatureFlags: []string{},
RawListenAddresses: []string{"default"},
RawGlobalAnnServers: []string{"default"},
GlobalAnnEnabled: true,
LocalAnnEnabled: true,
LocalAnnPort: 21027,
LocalAnnMCAddr: "[ff12::8384]:21027",
MaxSendKbps: 0,
MaxRecvKbps: 0,
ReconnectIntervalS: 60,
RelaysEnabled: true,
RelayReconnectIntervalM: 10,
StartBrowser: true,
NATEnabled: true,
NATLeaseM: 60,
NATRenewalM: 30,
NATTimeoutS: 10,
AutoUpgradeIntervalH: 12,
KeepTemporariesH: 24,
CacheIgnoredFiles: false,
ProgressUpdateIntervalS: 5,
LimitBandwidthInLan: false,
MinHomeDiskFree: Size{1, "%"},
URURL: "https://data.syncthing.net/newdata",
URInitialDelayS: 1800,
URPostInsecurely: false,
ReleasesURL: "https://upgrades.syncthing.net/meta.json",
AlwaysLocalNets: []string{},
OverwriteRemoteDevNames: false,
TempIndexMinBlocks: 10,
UnackedNotificationIDs: []string{"authenticationUserAndPassword"},
SetLowPriority: true,
CRURL: "https://crash.syncthing.net/newcrash",
CREnabled: true,
StunKeepaliveStartS: 180,
StunKeepaliveMinS: 20,
RawStunServers: []string{"default"},
AnnounceLANAddresses: true,
FeatureFlags: []string{},
ConnectionPriorityTCPLAN: 10,
ConnectionPriorityQUICLAN: 20,
ConnectionPriorityTCPWAN: 30,
ConnectionPriorityQUICWAN: 40,
ConnectionPriorityRelay: 50,
},
Defaults: Defaults{
Folder: FolderConfiguration{
@@ -139,19 +150,19 @@ func TestDefaultValues(t *testing.T) {
func TestDeviceConfig(t *testing.T) {
for i := OldestHandledVersion; i <= CurrentVersion; i++ {
cfgFile := fmt.Sprintf("testdata/v%d.xml", i)
if _, err := os.Stat(cfgFile); os.IsNotExist(err) {
cfgFile := fmt.Sprintf("v%d.xml", i)
if _, err := testFs.Stat(cfgFile); os.IsNotExist(err) {
continue
}
os.RemoveAll(filepath.Join("testdata", DefaultMarkerName))
wr, wrCancel, err := copyAndLoad(cfgFile, device1)
testFs.RemoveAll(DefaultMarkerName)
wr, wrCancel, err := copyAndLoad(testFs, cfgFile, device1)
defer wrCancel()
if err != nil {
t.Fatal(err)
}
_, err = os.Stat(filepath.Join("testdata", DefaultMarkerName))
_, err = testFs.Stat(DefaultMarkerName)
if i < 6 && err != nil {
t.Fatal(err)
} else if i >= 6 && err == nil {
@@ -212,7 +223,7 @@ func TestDeviceConfig(t *testing.T) {
t.Errorf("%d: Incorrect version %d != %d", i, cfg.Version, CurrentVersion)
}
if diff, equal := messagediff.PrettyDiff(expectedFolders, cfg.Folders); !equal {
t.Errorf("%d: Incorrect Folders. Diff:\n%s", i, diff)
t.Errorf("%d: Incorrect Folders. Diff:\n%s\n%v", i, diff, cfg)
}
if diff, equal := messagediff.PrettyDiff(expectedDevices, cfg.Devices); !equal {
t.Errorf("%d: Incorrect Devices. Diff:\n%s", i, diff)
@@ -224,10 +235,10 @@ func TestDeviceConfig(t *testing.T) {
}
func TestNoListenAddresses(t *testing.T) {
cfg, cfgCancel, err := copyAndLoad("testdata/nolistenaddress.xml", device1)
cfg, cfgCancel, err := copyAndLoad(testFs, "nolistenaddress.xml", device1)
defer cfgCancel()
if err != nil {
t.Error(err)
t.Fatal(err)
}
expected := []string{""}
@@ -239,50 +250,55 @@ func TestNoListenAddresses(t *testing.T) {
func TestOverriddenValues(t *testing.T) {
expected := OptionsConfiguration{
RawListenAddresses: []string{"tcp://:23000"},
RawGlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"},
GlobalAnnEnabled: false,
LocalAnnEnabled: false,
LocalAnnPort: 42123,
LocalAnnMCAddr: "quux:3232",
MaxSendKbps: 1234,
MaxRecvKbps: 2341,
ReconnectIntervalS: 6000,
RelaysEnabled: false,
RelayReconnectIntervalM: 20,
StartBrowser: false,
NATEnabled: false,
NATLeaseM: 90,
NATRenewalM: 15,
NATTimeoutS: 15,
AutoUpgradeIntervalH: 24,
KeepTemporariesH: 48,
CacheIgnoredFiles: true,
ProgressUpdateIntervalS: 10,
LimitBandwidthInLan: true,
MinHomeDiskFree: Size{5.2, "%"},
URSeen: 8,
URAccepted: 4,
URURL: "https://localhost/newdata",
URInitialDelayS: 800,
URPostInsecurely: true,
ReleasesURL: "https://localhost/releases",
AlwaysLocalNets: []string{},
OverwriteRemoteDevNames: true,
TempIndexMinBlocks: 100,
UnackedNotificationIDs: []string{"asdfasdf"},
SetLowPriority: false,
CRURL: "https://localhost/newcrash",
CREnabled: false,
StunKeepaliveStartS: 9000,
StunKeepaliveMinS: 900,
RawStunServers: []string{"foo"},
FeatureFlags: []string{"feature"},
RawListenAddresses: []string{"tcp://:23000"},
RawGlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"},
GlobalAnnEnabled: false,
LocalAnnEnabled: false,
LocalAnnPort: 42123,
LocalAnnMCAddr: "quux:3232",
MaxSendKbps: 1234,
MaxRecvKbps: 2341,
ReconnectIntervalS: 6000,
RelaysEnabled: false,
RelayReconnectIntervalM: 20,
StartBrowser: false,
NATEnabled: false,
NATLeaseM: 90,
NATRenewalM: 15,
NATTimeoutS: 15,
AutoUpgradeIntervalH: 24,
KeepTemporariesH: 48,
CacheIgnoredFiles: true,
ProgressUpdateIntervalS: 10,
LimitBandwidthInLan: true,
MinHomeDiskFree: Size{5.2, "%"},
URSeen: 8,
URAccepted: 4,
URURL: "https://localhost/newdata",
URInitialDelayS: 800,
URPostInsecurely: true,
ReleasesURL: "https://localhost/releases",
AlwaysLocalNets: []string{},
OverwriteRemoteDevNames: true,
TempIndexMinBlocks: 100,
UnackedNotificationIDs: []string{"asdfasdf"},
SetLowPriority: false,
CRURL: "https://localhost/newcrash",
CREnabled: false,
StunKeepaliveStartS: 9000,
StunKeepaliveMinS: 900,
RawStunServers: []string{"foo"},
FeatureFlags: []string{"feature"},
ConnectionPriorityTCPLAN: 40,
ConnectionPriorityQUICLAN: 45,
ConnectionPriorityTCPWAN: 50,
ConnectionPriorityQUICWAN: 55,
ConnectionPriorityRelay: 9000,
}
expectedPath := "/media/syncthing"
os.Unsetenv("STNOUPGRADE")
cfg, cfgCancel, err := copyAndLoad("testdata/overridenvalues.xml", device1)
cfg, cfgCancel, err := copyAndLoad(testFs, "overridenvalues.xml", device1)
defer cfgCancel()
if err != nil {
t.Error(err)
@@ -328,7 +344,7 @@ func TestDeviceAddressesDynamic(t *testing.T) {
},
}
cfg, cfgCancel, err := copyAndLoad("testdata/deviceaddressesdynamic.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "deviceaddressesdynamic.xml", device4)
defer cfgCancel()
if err != nil {
t.Error(err)
@@ -374,7 +390,7 @@ func TestDeviceCompression(t *testing.T) {
},
}
cfg, cfgCancel, err := copyAndLoad("testdata/devicecompression.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "devicecompression.xml", device4)
defer cfgCancel()
if err != nil {
t.Error(err)
@@ -417,7 +433,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
},
}
cfg, cfgCancel, err := copyAndLoad("testdata/deviceaddressesstatic.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "deviceaddressesstatic.xml", device4)
defer cfgCancel()
if err != nil {
t.Error(err)
@@ -430,7 +446,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
}
func TestVersioningConfig(t *testing.T) {
cfg, cfgCancel, err := copyAndLoad("testdata/versioningconfig.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "versioningconfig.xml", device4)
defer cfgCancel()
if err != nil {
t.Error(err)
@@ -458,7 +474,7 @@ func TestIssue1262(t *testing.T) {
t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux")
}
cfg, cfgCancel, err := copyAndLoad("testdata/issue-1262.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "issue-1262.xml", device4)
defer cfgCancel()
if err != nil {
t.Fatal(err)
@@ -473,7 +489,7 @@ func TestIssue1262(t *testing.T) {
}
func TestIssue1750(t *testing.T) {
cfg, cfgCancel, err := copyAndLoad("testdata/issue-1750.xml", device4)
cfg, cfgCancel, err := copyAndLoad(testFs, "issue-1750.xml", device4)
defer cfgCancel()
if err != nil {
t.Fatal(err)
@@ -511,20 +527,15 @@ func TestFolderPath(t *testing.T) {
}
func TestFolderCheckPath(t *testing.T) {
n := t.TempDir()
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, n)
err := os.MkdirAll(filepath.Join(n, "dir", ".stfolder"), os.FileMode(0777))
if err != nil {
t.Fatal(err)
}
tmpFs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(16)+"?nostfolder=true")
_ = tmpFs.MkdirAll(filepath.Join("dir", ".stfolder"), 0o777)
testcases := []struct {
path string
err error
}{
{
path: "",
path: ".",
err: ErrMarkerMissing,
},
{
@@ -537,35 +548,20 @@ func TestFolderCheckPath(t *testing.T) {
},
}
err = fs.DebugSymlinkForTestsOnly(testFs, testFs, "dir", "link")
if err == nil {
t.Log("running with symlink check")
testcases = append(testcases, struct {
path string
err error
}{
path: "link",
err: nil,
})
} else if !build.IsWindows {
t.Log("running without symlink check")
t.Fatal(err)
}
for _, testcase := range testcases {
cfg := FolderConfiguration{
Path: filepath.Join(n, testcase.path),
MarkerName: DefaultMarkerName,
FilesystemType: fs.FilesystemTypeFake,
MarkerName: DefaultMarkerName,
}
if err := cfg.CheckPath(); testcase.err != err {
t.Errorf("unexpected error in case %s: %s != %v", testcase.path, err, testcase.err)
if err := cfg.checkFilesystemPath(tmpFs, testcase.path); testcase.err != err {
t.Errorf("unexpected error in case; path: [%s] err [%s] expected err [%v]", testcase.path, err, testcase.err)
}
}
}
func TestNewSaveLoad(t *testing.T) {
path := "testdata/temp.xml"
path := "temp.xml"
os.Remove(path)
defer os.Remove(path)
@@ -647,7 +643,7 @@ func TestPrepare(t *testing.T) {
}
func TestCopy(t *testing.T) {
wrapper, wrapperCancel, err := copyAndLoad("testdata/example.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "example.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -680,14 +676,12 @@ func TestCopy(t *testing.T) {
t.Error("Config should have changed")
}
if !bytes.Equal(bsOrig, bsCopy) {
// os.WriteFile("a", bsOrig, 0644)
// os.WriteFile("b", bsCopy, 0644)
t.Error("Copy should be unchanged")
}
}
func TestPullOrder(t *testing.T) {
wrapper, wrapperCleanup, err := copyAndLoad("testdata/pullorder.xml", device1)
wrapper, wrapperCleanup, err := copyAndLoad(testFs, "pullorder.xml", device1)
defer wrapperCleanup()
if err != nil {
t.Fatal(err)
@@ -740,7 +734,7 @@ func TestPullOrder(t *testing.T) {
}
func TestLargeRescanInterval(t *testing.T) {
wrapper, wrapperCancel, err := copyAndLoad("testdata/largeinterval.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "largeinterval.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -800,7 +794,7 @@ func TestGUIPasswordHash(t *testing.T) {
func TestDuplicateDevices(t *testing.T) {
// Duplicate devices should be removed
wrapper, wrapperCancel, err := copyAndLoad("testdata/dupdevices.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "dupdevices.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -819,7 +813,7 @@ func TestDuplicateDevices(t *testing.T) {
func TestDuplicateFolders(t *testing.T) {
// Duplicate folders are a loading error
_, _Cancel, err := copyAndLoad("testdata/dupfolders.xml", device1)
_, _Cancel, err := copyAndLoad(testFs, "dupfolders.xml", device1)
defer _Cancel()
if err == nil || !strings.Contains(err.Error(), errFolderIDDuplicate.Error()) {
t.Fatal(`Expected error to mention "duplicate folder ID":`, err)
@@ -831,7 +825,7 @@ func TestEmptyFolderPaths(t *testing.T) {
// get messed up by the prepare steps (e.g., become the current dir or
// get a slash added so that it becomes the root directory or similar).
_, _Cancel, err := copyAndLoad("testdata/nopath.xml", device1)
_, _Cancel, err := copyAndLoad(testFs, "nopath.xml", device1)
defer _Cancel()
if err == nil || !strings.Contains(err.Error(), errFolderPathEmpty.Error()) {
t.Fatal("Expected error due to empty folder path, got", err)
@@ -901,7 +895,7 @@ func TestIgnoredDevices(t *testing.T) {
// Verify that ignored devices that are also present in the
// configuration are not in fact ignored.
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoreddevices.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "ignoreddevices.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -920,7 +914,7 @@ func TestIgnoredFolders(t *testing.T) {
// configuration are not in fact ignored.
// Also, verify that folders that are shared with a device are not ignored.
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoredfolders.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "ignoredfolders.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -957,7 +951,7 @@ func TestIgnoredFolders(t *testing.T) {
func TestGetDevice(t *testing.T) {
// Verify that the Device() call does the right thing
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoreddevices.xml", device1)
wrapper, wrapperCancel, err := copyAndLoad(testFs, "ignoreddevices.xml", device1)
defer wrapperCancel()
if err != nil {
t.Fatal(err)
@@ -985,7 +979,8 @@ func TestGetDevice(t *testing.T) {
}
func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
wrapper, wrapperCancel, err := copyAndLoad("testdata/example.xml", device1)
t.Skip("to fix: test hangs")
wrapper, wrapperCancel, err := copyAndLoad(testFs, "example.xml", device1)
defer wrapperCancel()
if err != nil {
t.Errorf("Failed: %s", err)
@@ -1297,38 +1292,63 @@ func defaultConfigAsMap() map[string]interface{} {
return tmp
}
func copyToTmp(path string) (string, error) {
orig, err := os.Open(path)
func copyToTmp(fs fs.Filesystem, path string) (string, error) {
orig, err := fs.Open(path)
if err != nil {
return "", err
}
defer orig.Close()
temp, err := os.CreateTemp("", "syncthing-configTest-")
temp, err := fs.Create("syncthing-configTest-" + rand.String(6))
if err != nil {
return "", err
}
defer temp.Close()
if _, err := io.Copy(temp, orig); err != nil {
return "", err
}
return temp.Name(), nil
}
func copyAndLoad(path string, myID protocol.DeviceID) (*testWrapper, func(), error) {
temp, err := copyToTmp(path)
func copyAndLoad(fs fs.Filesystem, path string, myID protocol.DeviceID) (*testWrapper, func(), error) {
temp, err := copyToTmp(fs, path)
if err != nil {
return nil, func() {}, err
}
wrapper, err := load(temp, myID)
wrapper, err := loadTest(fs, temp, myID)
if err != nil {
return nil, func() {}, err
}
return wrapper, func() {
fs.Remove(temp)
wrapper.stop()
os.Remove(temp)
}, nil
}
func loadTest(fs fs.Filesystem, path string, myID protocol.DeviceID) (*testWrapper, error) {
cfg, _, err := loadWrapTest(fs, path, myID, events.NoopLogger)
if err != nil {
return nil, err
}
return startWrapper(cfg), nil
}
func loadWrapTest(fs fs.Filesystem, path string, myID protocol.DeviceID, evLogger events.Logger) (Wrapper, int, error) {
fd, err := fs.Open(path)
if err != nil {
return nil, 0, err
}
defer fd.Close()
cfg, originalVersion, err := ReadXML(fd, myID)
if err != nil {
return nil, 0, err
}
return Wrap(filepath.Join(testFs.URI(), path), cfg, myID, evLogger), originalVersion, nil
}
func load(path string, myID protocol.DeviceID) (*testWrapper, error) {
cfg, _, err := Load(path, myID, events.NoopLogger)
if err != nil {
@@ -1468,3 +1488,23 @@ func TestXattrFilter(t *testing.T) {
}
}
}
func loadTestFiles() {
entries, err := os.ReadDir("testdata")
if err != nil {
return
}
for _, e := range entries {
handleFile(e.Name())
}
}
func handleFile(name string) {
fd, err := testFs.Create(name)
if err != nil {
return
}
origin, _ := os.ReadFile(filepath.Join("testdata", name))
fd.Write(origin)
fd.Close()
}

View File

@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"path"
"path/filepath"
"sort"
"strings"
"time"
@@ -90,11 +91,11 @@ func (f *FolderConfiguration) CreateMarker() error {
return nil
}
permBits := fs.FileMode(0777)
permBits := fs.FileMode(0o777)
if build.IsWindows {
// Windows has no umask so we must chose a safer set of bits to
// begin with.
permBits = 0700
permBits = 0o700
}
fs := f.Filesystem(nil)
err := fs.Mkdir(DefaultMarkerName, permBits)
@@ -113,7 +114,11 @@ func (f *FolderConfiguration) CreateMarker() error {
// CheckPath returns nil if the folder root exists and contains the marker file
func (f *FolderConfiguration) CheckPath() error {
fi, err := f.Filesystem(nil).Stat(".")
return f.checkFilesystemPath(f.Filesystem(nil), ".")
}
func (f *FolderConfiguration) checkFilesystemPath(ffs fs.Filesystem, path string) error {
fi, err := ffs.Stat(path)
if err != nil {
if !fs.IsNotExist(err) {
return err
@@ -131,7 +136,7 @@ func (f *FolderConfiguration) CheckPath() error {
return ErrPathNotDirectory
}
_, err = f.Filesystem(nil).Stat(f.MarkerName)
_, err = ffs.Stat(filepath.Join(path, f.MarkerName))
if err != nil {
if !fs.IsNotExist(err) {
return err
@@ -145,11 +150,11 @@ func (f *FolderConfiguration) CheckPath() error {
func (f *FolderConfiguration) CreateRoot() (err error) {
// Directory permission bits. Will be filtered down to something
// sane by umask on Unixes.
permBits := fs.FileMode(0777)
permBits := fs.FileMode(0o777)
if build.IsWindows {
// Windows has no umask so we must chose a safer set of bits to
// begin with.
permBits = 0700
permBits = 0o700
}
filesystem := f.Filesystem(nil)

View File

@@ -55,6 +55,15 @@ func (opts *OptionsConfiguration) prepare(guiPWIsSet bool) {
if opts.ConnectionLimitMax < 0 {
opts.ConnectionLimitMax = 0
}
if opts.ConnectionPriorityQUICWAN <= opts.ConnectionPriorityQUICLAN {
l.Warnln("Connection priority number for QUIC over WAN must be worse (higher) than QUIC over LAN. Correcting.")
opts.ConnectionPriorityQUICWAN = opts.ConnectionPriorityQUICLAN + 1
}
if opts.ConnectionPriorityTCPWAN <= opts.ConnectionPriorityTCPLAN {
l.Warnln("Connection priority number for TCP over WAN must be worse (higher) than TCP over LAN. Correcting.")
opts.ConnectionPriorityTCPWAN = opts.ConnectionPriorityTCPLAN + 1
}
}
// RequiresRestartOnly returns a copy with only the attributes that require

View File

@@ -82,7 +82,13 @@ type OptionsConfiguration struct {
ConnectionLimitMax int `protobuf:"varint,52,opt,name=connection_limit_max,json=connectionLimitMax,proto3,casttype=int" json:"connectionLimitMax" xml:"connectionLimitMax"`
// When set, this allows TLS 1.2 on sync connections, where we otherwise
// default to TLS 1.3+ only.
InsecureAllowOldTLSVersions bool `protobuf:"varint,53,opt,name=insecure_allow_old_tls_versions,json=insecureAllowOldTlsVersions,proto3" json:"insecureAllowOldTLSVersions" xml:"insecureAllowOldTLSVersions"`
InsecureAllowOldTLSVersions bool `protobuf:"varint,53,opt,name=insecure_allow_old_tls_versions,json=insecureAllowOldTlsVersions,proto3" json:"insecureAllowOldTLSVersions" xml:"insecureAllowOldTLSVersions"`
ConnectionPriorityTCPLAN int `protobuf:"varint,54,opt,name=connection_priority_tcp_lan,json=connectionPriorityTcpLan,proto3,casttype=int" json:"connectionPriorityTcpLan" xml:"connectionPriorityTcpLan" default:"10"`
ConnectionPriorityQUICLAN int `protobuf:"varint,55,opt,name=connection_priority_quic_lan,json=connectionPriorityQuicLan,proto3,casttype=int" json:"connectionPriorityQuicLan" xml:"connectionPriorityQuicLan" default:"20"`
ConnectionPriorityTCPWAN int `protobuf:"varint,56,opt,name=connection_priority_tcp_wan,json=connectionPriorityTcpWan,proto3,casttype=int" json:"connectionPriorityTcpWan" xml:"connectionPriorityTcpWan" default:"30"`
ConnectionPriorityQUICWAN int `protobuf:"varint,57,opt,name=connection_priority_quic_wan,json=connectionPriorityQuicWan,proto3,casttype=int" json:"connectionPriorityQuicWan" xml:"connectionPriorityQuicWan" default:"40"`
ConnectionPriorityRelay int `protobuf:"varint,58,opt,name=connection_priority_relay,json=connectionPriorityRelay,proto3,casttype=int" json:"connectionPriorityRelay" xml:"connectionPriorityRelay" default:"50"`
ConnectionPriorityUpgradeThreshold int `protobuf:"varint,59,opt,name=connection_priority_upgrade_threshold,json=connectionPriorityUpgradeThreshold,proto3,casttype=int" json:"connectionPriorityUpgradeThreshold" xml:"connectionPriorityUpgradeThreshold" default:"0"`
// Legacy deprecated
DeprecatedUPnPEnabled bool `protobuf:"varint,9000,opt,name=upnp_enabled,json=upnpEnabled,proto3" json:"-" xml:"upnpEnabled,omitempty"` // Deprecated: Do not use.
DeprecatedUPnPLeaseM int `protobuf:"varint,9001,opt,name=upnp_lease_m,json=upnpLeaseM,proto3,casttype=int" json:"-" xml:"upnpLeaseMinutes,omitempty"` // Deprecated: Do not use.
@@ -135,211 +141,228 @@ func init() {
}
var fileDescriptor_d09882599506ca03 = []byte{
// 3261 bytes of a gzipped FileDescriptorProto
// 3521 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x5a, 0x5d, 0x6c, 0x1d, 0x47,
0xd9, 0xce, 0x26, 0x4d, 0xda, 0x6c, 0x1c, 0x27, 0x1e, 0x3b, 0xf6, 0x36, 0x49, 0xbd, 0xee, 0xc9,
0x49, 0xeb, 0xfe, 0x25, 0xb6, 0x93, 0xe6, 0x4b, 0x23, 0x7d, 0xea, 0xe7, 0x9f, 0xfa, 0xab, 0x1b,
0x3b, 0xb1, 0xc6, 0xf6, 0xf7, 0xa1, 0x22, 0xb4, 0x1a, 0xef, 0xce, 0xb1, 0x17, 0xef, 0x99, 0x3d,
0xdd, 0x99, 0xf5, 0x4f, 0x8b, 0xa0, 0x2a, 0x82, 0x72, 0x07, 0x58, 0xfc, 0x48, 0x20, 0xa1, 0x22,
0x40, 0xa2, 0x94, 0x22, 0x24, 0x24, 0x24, 0xb8, 0xa1, 0x42, 0x42, 0xaa, 0xe0, 0xc2, 0xbe, 0x44,
0xa2, 0x2c, 0xaa, 0xd3, 0xab, 0x73, 0x81, 0xc4, 0xb9, 0x34, 0x37, 0x68, 0x66, 0xff, 0x66, 0x77,
0xe7, 0x24, 0xb9, 0x3b, 0xfb, 0x3e, 0xef, 0xfb, 0xce, 0xf3, 0xce, 0xcf, 0x3b, 0xef, 0xcc, 0x1c,
0xfd, 0xb2, 0xe7, 0xae, 0x5e, 0xb5, 0x7d, 0xd2, 0x70, 0xd7, 0xae, 0xfa, 0x2d, 0xe6, 0xfa, 0x84,
0xc6, 0x5f, 0x61, 0x80, 0xf8, 0xd7, 0x95, 0x56, 0xe0, 0x33, 0x1f, 0x9c, 0x88, 0x85, 0xe7, 0x87,
0x24, 0x75, 0x16, 0x12, 0x97, 0xac, 0xc5, 0x0a, 0xe7, 0xcf, 0x49, 0x00, 0x75, 0xdf, 0xc4, 0x89,
0xf8, 0x24, 0xde, 0x66, 0xf1, 0xcf, 0xda, 0xbf, 0xe6, 0xf4, 0x81, 0xbb, 0x71, 0x0b, 0xd3, 0x72,
0x0b, 0xe0, 0x47, 0x9a, 0x7e, 0xd6, 0x73, 0x29, 0xc3, 0xc4, 0x42, 0x8e, 0x13, 0x60, 0x4a, 0x31,
0x35, 0xb4, 0x91, 0x63, 0xa3, 0x27, 0xa7, 0xe8, 0x41, 0x64, 0x02, 0x88, 0xb6, 0xe6, 0x05, 0x3c,
0x99, 0xa2, 0xed, 0xc8, 0x3c, 0xe3, 0x15, 0x45, 0x9d, 0xc8, 0xbc, 0xbc, 0xdd, 0xf4, 0x6e, 0xd5,
0x0a, 0xf2, 0xda, 0x88, 0x83, 0x1b, 0x28, 0xf4, 0xd8, 0xad, 0x5a, 0xf2, 0xa3, 0x76, 0xb8, 0x57,
0x7f, 0x34, 0xf9, 0xbd, 0xbb, 0x5f, 0x57, 0x38, 0x87, 0x65, 0xd7, 0xe0, 0x9f, 0x9a, 0x6e, 0xac,
0x79, 0xfe, 0x2a, 0xf2, 0x2c, 0xc7, 0xa5, 0xb6, 0xbf, 0x89, 0x83, 0x1d, 0x8b, 0xe2, 0x60, 0x13,
0x07, 0xd4, 0x38, 0x2a, 0x88, 0xfe, 0x46, 0x3b, 0x88, 0xcc, 0x7e, 0x88, 0xb6, 0xfe, 0x57, 0xe8,
0x4d, 0x12, 0xb2, 0x14, 0xe3, 0xed, 0xc8, 0x3c, 0xb7, 0x96, 0xca, 0xfc, 0x90, 0xd8, 0x38, 0x01,
0x3a, 0x91, 0xf9, 0xbc, 0x20, 0xac, 0x42, 0x15, 0xbc, 0xdb, 0x7b, 0xf5, 0x01, 0x95, 0x6a, 0x67,
0xaf, 0xae, 0x6e, 0xa0, 0x18, 0xa8, 0x8a, 0x1b, 0x1c, 0x8c, 0x0d, 0x67, 0xd2, 0xa0, 0x12, 0x39,
0xf8, 0x4c, 0x15, 0x30, 0x26, 0x68, 0xd5, 0xc3, 0x8e, 0x71, 0x6c, 0x44, 0x1b, 0x7d, 0x6c, 0xea,
0x7d, 0x1e, 0xf0, 0xd9, 0xcc, 0xe3, 0x2b, 0x31, 0x58, 0x8d, 0x36, 0x01, 0x3a, 0x91, 0xf9, 0xac,
0x22, 0xda, 0x04, 0x95, 0xc2, 0x65, 0x41, 0x88, 0x79, 0xac, 0x5d, 0xdc, 0x74, 0x03, 0x0e, 0xf7,
0xea, 0x8f, 0x70, 0xd3, 0xdd, 0xfd, 0x7a, 0x85, 0x54, 0x25, 0xcc, 0x44, 0x0e, 0x3e, 0xd1, 0xf4,
0x21, 0xcf, 0xb7, 0x95, 0x51, 0x3e, 0x22, 0xa2, 0xfc, 0x09, 0x8f, 0xf2, 0xcc, 0x3c, 0xd7, 0x29,
0x04, 0x39, 0xe0, 0x25, 0xa2, 0x52, 0x8c, 0xcf, 0xc4, 0x53, 0x50, 0x01, 0x2a, 0x42, 0x54, 0x3b,
0xe9, 0x22, 0x97, 0x02, 0x2c, 0xf3, 0x81, 0xe7, 0x84, 0x41, 0x25, 0xbc, 0xbf, 0x68, 0x7a, 0x7f,
0x1c, 0x1e, 0x4a, 0x7c, 0x59, 0x2d, 0x3f, 0x60, 0xc6, 0xf1, 0x11, 0x6d, 0xf4, 0xf8, 0xd4, 0x0f,
0x78, 0x68, 0x3d, 0xa9, 0xab, 0x45, 0x3f, 0x60, 0xed, 0xc8, 0xec, 0x2b, 0x34, 0xcd, 0x85, 0x9d,
0xc8, 0x7c, 0xba, 0x1a, 0x14, 0x47, 0xa4, 0x88, 0x26, 0xc6, 0xc7, 0x26, 0xfe, 0xab, 0x76, 0x18,
0x99, 0xc7, 0x5c, 0xc2, 0xda, 0x7b, 0x75, 0x85, 0x1b, 0x95, 0xf0, 0x70, 0xaf, 0x7e, 0x5c, 0x98,
0xee, 0xee, 0xd7, 0x0b, 0x4c, 0x60, 0x55, 0x17, 0x7c, 0xf5, 0xa8, 0x3e, 0x52, 0x8a, 0xa6, 0x19,
0x7a, 0xcc, 0xb5, 0x11, 0x65, 0x69, 0xde, 0x30, 0x4e, 0x8c, 0x68, 0xa3, 0x27, 0xa7, 0x7e, 0xc7,
0x43, 0xeb, 0x4d, 0x1d, 0x2e, 0x4c, 0xf3, 0x95, 0xdc, 0x8e, 0xcc, 0xfe, 0x82, 0xd3, 0x58, 0xdc,
0x89, 0xcc, 0x1b, 0xd5, 0xf0, 0x62, 0x4c, 0x0a, 0xf0, 0xf3, 0x8d, 0xc6, 0xf8, 0xc4, 0xad, 0x5b,
0x37, 0xaf, 0xdd, 0xbc, 0xfe, 0x85, 0x5b, 0x71, 0xb4, 0xed, 0xbd, 0xba, 0xd2, 0xa1, 0x5a, 0x7c,
0xb8, 0x57, 0x07, 0x55, 0x27, 0xbb, 0xfb, 0xf5, 0x12, 0x4d, 0xf8, 0x44, 0xd1, 0x38, 0x8d, 0x30,
0x49, 0x46, 0xe0, 0xae, 0x7e, 0xba, 0x89, 0xb6, 0x2d, 0x8a, 0x89, 0x63, 0x6d, 0xac, 0xb6, 0xa8,
0xf1, 0xa8, 0x18, 0xcc, 0xe7, 0xda, 0x91, 0x79, 0xaa, 0x89, 0xb6, 0x97, 0x30, 0x71, 0x6e, 0xaf,
0xb6, 0x78, 0x72, 0xe9, 0x13, 0x61, 0x49, 0xb2, 0x74, 0x7c, 0xa0, 0xac, 0x98, 0x3a, 0x0c, 0xb0,
0xbd, 0x19, 0x3b, 0x7c, 0xac, 0xe0, 0x10, 0x62, 0x7b, 0xb3, 0xec, 0x30, 0x95, 0x15, 0x1c, 0xa6,
0x42, 0xf0, 0x5b, 0x4d, 0x1f, 0x0a, 0xb0, 0xed, 0x13, 0x82, 0x6d, 0x9e, 0xde, 0x2d, 0x97, 0x30,
0x1c, 0x6c, 0x22, 0xcf, 0xa2, 0xc6, 0x49, 0xe1, 0xfb, 0xcb, 0x22, 0xa9, 0xa7, 0x2a, 0x73, 0x09,
0xbc, 0xc4, 0x73, 0x87, 0x6c, 0x98, 0x01, 0x9d, 0xc8, 0x1c, 0x15, 0x6d, 0x2b, 0x51, 0x69, 0x94,
0x6e, 0x8c, 0xa5, 0x94, 0x0e, 0xf7, 0xea, 0x47, 0x6f, 0x8c, 0x89, 0xfc, 0x5e, 0x69, 0x07, 0xaa,
0x5b, 0x01, 0x0d, 0xbd, 0x37, 0xc0, 0x1e, 0xda, 0xa1, 0x59, 0x0e, 0xd0, 0x45, 0x0e, 0x78, 0xb9,
0x1d, 0x99, 0xa7, 0x63, 0x24, 0x5f, 0xe8, 0xb5, 0x84, 0x90, 0x24, 0x2d, 0xaf, 0xf0, 0x74, 0xc5,
0xc2, 0xa2, 0x31, 0x78, 0xe7, 0xa8, 0x7e, 0x21, 0x69, 0x28, 0x23, 0x92, 0x77, 0x52, 0xd3, 0x38,
0x25, 0x3a, 0xe9, 0x8f, 0x7c, 0x0e, 0x0f, 0x41, 0xae, 0x57, 0x09, 0x61, 0xa1, 0x1d, 0x99, 0x43,
0x81, 0x1a, 0xca, 0x12, 0x6d, 0x17, 0x5c, 0x62, 0x39, 0x3e, 0x26, 0x2d, 0xd9, 0xae, 0xfe, 0xba,
0x43, 0xbc, 0x93, 0xc7, 0x79, 0x27, 0x77, 0xa3, 0x09, 0x8d, 0x38, 0xce, 0x2a, 0x02, 0x56, 0xf5,
0xd3, 0x94, 0xa1, 0x80, 0x59, 0xab, 0x81, 0xbf, 0x45, 0x71, 0x60, 0xf4, 0x88, 0xbe, 0xfe, 0xef,
0x76, 0x64, 0xf6, 0x08, 0x60, 0x2a, 0x96, 0x77, 0x22, 0xf3, 0x49, 0x11, 0x8e, 0x2c, 0xec, 0xda,
0xd3, 0x05, 0x53, 0xf0, 0x33, 0x4d, 0x3f, 0x47, 0x10, 0xb3, 0x58, 0x80, 0xf8, 0xae, 0x86, 0xbc,
0x6c, 0x60, 0x7b, 0x45, 0x63, 0x6f, 0x1c, 0x44, 0xa6, 0x7e, 0x67, 0x72, 0x39, 0x4f, 0xeb, 0x3a,
0x41, 0x2c, 0x1f, 0x63, 0x53, 0x34, 0x9c, 0x8b, 0x14, 0x29, 0x5c, 0x36, 0x28, 0x7c, 0x49, 0xe9,
0x5a, 0x6a, 0x02, 0xf6, 0x13, 0xc4, 0x96, 0x53, 0x3a, 0xe9, 0x84, 0xf8, 0x7d, 0x85, 0xa7, 0x87,
0x11, 0xc5, 0x56, 0xd3, 0x38, 0x23, 0xa6, 0xc2, 0xd7, 0xf9, 0x54, 0x38, 0x79, 0x67, 0x72, 0x79,
0x9e, 0x8b, 0xf9, 0xe0, 0x9f, 0x21, 0x88, 0xc5, 0x1f, 0x2e, 0x09, 0x99, 0x28, 0x7e, 0x6a, 0x29,
0x59, 0x59, 0xae, 0x5c, 0x1b, 0xed, 0xbd, 0x7a, 0xc5, 0xbe, 0x2a, 0xca, 0x56, 0x50, 0xde, 0x30,
0x04, 0x32, 0xfb, 0x58, 0x06, 0xfe, 0xac, 0xe9, 0x43, 0x45, 0xf2, 0x01, 0x26, 0x78, 0x4b, 0xcc,
0xe4, 0xb3, 0x82, 0xfe, 0x2e, 0xa7, 0x7f, 0xea, 0xce, 0xe4, 0x32, 0x8c, 0x01, 0x1e, 0x40, 0x1f,
0x41, 0x2c, 0xfd, 0xcc, 0x42, 0xa8, 0xa7, 0x21, 0x14, 0x11, 0x29, 0x88, 0x6b, 0x72, 0x10, 0x0a,
0x1f, 0x2a, 0x21, 0x0f, 0xe4, 0x1a, 0x0f, 0x44, 0xa6, 0x00, 0x07, 0xe4, 0x50, 0x52, 0xa9, 0x22,
0x18, 0xe6, 0x36, 0xb1, 0x1f, 0x32, 0x8b, 0x1a, 0x7d, 0xc5, 0x60, 0x96, 0x63, 0x60, 0x29, 0x09,
0x26, 0xfd, 0xe4, 0x33, 0xdd, 0x29, 0x04, 0x53, 0x44, 0xba, 0x2d, 0x3f, 0x85, 0x0f, 0x95, 0x30,
0x5b, 0x72, 0x32, 0x85, 0x62, 0x30, 0xa9, 0x14, 0xfc, 0x50, 0xd3, 0x8d, 0x90, 0xa2, 0x35, 0x6c,
0x05, 0x98, 0xef, 0xfb, 0x2e, 0x59, 0xb3, 0x90, 0x6d, 0xe3, 0x16, 0xc3, 0x8e, 0x01, 0x44, 0x34,
0x88, 0xaf, 0x80, 0x15, 0x38, 0x99, 0x48, 0xf9, 0x0a, 0x08, 0x83, 0xf4, 0xab, 0x13, 0x99, 0x67,
0x45, 0x10, 0xb9, 0x48, 0x22, 0x2c, 0x2b, 0x16, 0xbe, 0xf8, 0x8c, 0xcf, 0x5d, 0xc2, 0x41, 0x41,
0x01, 0xa6, 0x0c, 0x52, 0x39, 0x78, 0x4b, 0x1f, 0x28, 0x93, 0xa3, 0x18, 0x13, 0xa3, 0x5f, 0x10,
0x9b, 0x3b, 0x88, 0xcc, 0x13, 0x2b, 0x70, 0x09, 0x63, 0xd2, 0x8e, 0xcc, 0x13, 0x61, 0xc0, 0x7f,
0x75, 0x22, 0xb3, 0x27, 0x21, 0xc4, 0x3f, 0x25, 0x32, 0xa9, 0x42, 0xf6, 0x6b, 0x77, 0xbf, 0x9e,
0x98, 0x43, 0x50, 0x24, 0xc0, 0x65, 0xe0, 0xbb, 0x9a, 0xfe, 0x78, 0xb9, 0xf5, 0x90, 0xb8, 0x6f,
0x84, 0xd8, 0x72, 0x1d, 0x63, 0x40, 0x14, 0x11, 0xaf, 0xc7, 0x7d, 0xb3, 0x22, 0xc4, 0x73, 0x33,
0x71, 0xdf, 0x24, 0x5f, 0x72, 0xdf, 0xa4, 0x0a, 0xb5, 0xb8, 0x53, 0xd2, 0xcf, 0x8e, 0xfc, 0x95,
0x74, 0x4a, 0x8a, 0x95, 0x3b, 0x25, 0xd5, 0x02, 0x1f, 0x69, 0x7a, 0x7f, 0x85, 0x57, 0xe0, 0x19,
0xe7, 0x04, 0xa3, 0x6f, 0xf2, 0xb9, 0x77, 0x7c, 0x05, 0xae, 0xc0, 0xf9, 0x76, 0x64, 0x1e, 0x0f,
0x83, 0x15, 0x38, 0xdf, 0x89, 0xcc, 0x9b, 0x29, 0x11, 0x38, 0x2f, 0xcd, 0xae, 0x75, 0xc6, 0x5a,
0xf4, 0xd6, 0xd5, 0xab, 0x0e, 0x62, 0xe8, 0x0a, 0xdd, 0x21, 0x36, 0x5b, 0xe7, 0x87, 0x35, 0x82,
0xd9, 0x55, 0x82, 0xb7, 0xb8, 0x94, 0x13, 0x4e, 0x9c, 0xa4, 0x3f, 0x0e, 0xf7, 0xea, 0x0f, 0x61,
0xb8, 0xbb, 0x5f, 0x8f, 0x59, 0xc0, 0xbe, 0x52, 0x1c, 0x81, 0x07, 0xfe, 0xa1, 0xe9, 0x66, 0x39,
0x84, 0x96, 0x4f, 0xf9, 0x0e, 0x47, 0xb1, 0x1d, 0x06, 0xd8, 0xdb, 0x31, 0x06, 0x45, 0xfa, 0xfd,
0xbe, 0x38, 0x41, 0xac, 0xc0, 0x45, 0x9f, 0xb2, 0xb9, 0x0c, 0x6c, 0x47, 0xe6, 0xd9, 0x30, 0x28,
0xca, 0x3a, 0x91, 0xf9, 0x54, 0x12, 0x64, 0x11, 0x90, 0xe2, 0x6d, 0x20, 0x8f, 0x8a, 0x94, 0x5c,
0xb5, 0x56, 0xc8, 0x78, 0xe5, 0x29, 0x2c, 0xf8, 0x79, 0xa1, 0x4c, 0x01, 0x5e, 0x2c, 0x86, 0x55,
0x44, 0xc1, 0xdf, 0x15, 0x11, 0xba, 0xc4, 0x65, 0x2e, 0x3f, 0x47, 0xf0, 0xfd, 0xce, 0xa2, 0xc6,
0x90, 0x98, 0xc5, 0xdf, 0x13, 0xa7, 0x87, 0x15, 0x38, 0x17, 0xa3, 0x33, 0x1c, 0xe4, 0x09, 0xe3,
0x4c, 0x18, 0x14, 0x44, 0x59, 0xba, 0x28, 0xc9, 0xe5, 0x64, 0x71, 0x73, 0xac, 0x90, 0xc0, 0xcb,
0x1e, 0xaa, 0x22, 0xbe, 0x03, 0x71, 0x2b, 0x7e, 0x60, 0x28, 0x51, 0x80, 0x17, 0x8a, 0x01, 0x16,
0x40, 0xf0, 0xae, 0xa6, 0x0f, 0xa1, 0x90, 0xf9, 0x56, 0xd8, 0x5a, 0x0b, 0x90, 0x83, 0xf3, 0xda,
0x64, 0xdd, 0x78, 0x5c, 0xc4, 0xb5, 0xc8, 0x4f, 0x40, 0x5c, 0x65, 0x25, 0xd6, 0x48, 0xb7, 0xf5,
0x57, 0xb3, 0xc3, 0x82, 0x0a, 0x94, 0xa3, 0x99, 0x90, 0x0b, 0xb5, 0xf1, 0x09, 0xa8, 0xf4, 0x06,
0x9a, 0xfa, 0x50, 0xca, 0x81, 0xf9, 0x56, 0x2b, 0xe0, 0x3d, 0x2e, 0xb6, 0x46, 0x6a, 0x9c, 0x17,
0x53, 0xe8, 0x06, 0x27, 0x92, 0xa8, 0x2c, 0xfb, 0x8b, 0x01, 0x86, 0x09, 0xde, 0x89, 0xcc, 0xf3,
0x71, 0x8f, 0x2a, 0xc0, 0x1a, 0x54, 0xda, 0x80, 0x4d, 0x1d, 0x6c, 0x60, 0xdc, 0xb2, 0x18, 0x6e,
0xb6, 0xfc, 0x00, 0x05, 0x2e, 0xa6, 0xd6, 0xba, 0x71, 0x41, 0x84, 0xfc, 0x2a, 0x9f, 0x97, 0x1c,
0x5d, 0xce, 0x41, 0x1e, 0xee, 0x25, 0xd1, 0x4a, 0x19, 0x90, 0x8f, 0x46, 0xd7, 0xe5, 0x50, 0x27,
0xae, 0xc3, 0x8a, 0x17, 0xb0, 0xa3, 0xf7, 0xdb, 0xc8, 0x5e, 0xc7, 0x96, 0xbb, 0x46, 0xfc, 0x00,
0x3b, 0x56, 0xc3, 0xf5, 0x30, 0x35, 0x2e, 0x8a, 0x10, 0xe7, 0xf8, 0x06, 0x23, 0xe0, 0xb9, 0x18,
0x9d, 0xe5, 0x60, 0xd6, 0xd1, 0x15, 0xa4, 0xb2, 0x24, 0xb2, 0xa9, 0x0e, 0xab, 0x6e, 0xc0, 0xb7,
0x35, 0xfd, 0x7c, 0x2b, 0xf0, 0xd7, 0xf8, 0xd9, 0xc2, 0x0a, 0x5b, 0x0e, 0x62, 0x58, 0xae, 0xd7,
0x9f, 0x10, 0xb1, 0x2f, 0xf3, 0x72, 0x33, 0xd5, 0x5a, 0x11, 0x4a, 0x72, 0x6d, 0x1e, 0x9f, 0x79,
0xbb, 0xe0, 0x12, 0x9d, 0x17, 0xa5, 0x8e, 0xd0, 0x5e, 0x84, 0xdd, 0x3c, 0x82, 0x77, 0x34, 0x7d,
0xd0, 0x73, 0x9b, 0x2e, 0xb3, 0x56, 0x11, 0x71, 0xb6, 0x5c, 0x87, 0xad, 0x5b, 0x2e, 0xb1, 0x3c,
0x44, 0x8c, 0x61, 0xd1, 0x25, 0x0b, 0xe2, 0x2c, 0xc7, 0x35, 0xa6, 0x52, 0x85, 0x39, 0x32, 0x8f,
0x48, 0x7e, 0xfe, 0xae, 0x62, 0xf7, 0xe9, 0x16, 0x95, 0x2b, 0xf0, 0xb6, 0xa6, 0x83, 0xa6, 0x4b,
0xac, 0x75, 0xbf, 0x89, 0x2d, 0xc7, 0xa5, 0x1b, 0x56, 0x23, 0xc0, 0xd8, 0x30, 0x47, 0xb4, 0xd1,
0x53, 0x13, 0x3d, 0x57, 0xe2, 0x8b, 0xae, 0x2b, 0x4b, 0xee, 0x9b, 0x78, 0xea, 0x95, 0x8f, 0x23,
0xf3, 0x08, 0x5f, 0xd5, 0x4d, 0x97, 0xbc, 0xea, 0x37, 0xf1, 0x8c, 0x4b, 0x37, 0x66, 0x03, 0x8c,
0xb3, 0xd9, 0x51, 0x92, 0xcb, 0xeb, 0x60, 0xe4, 0x32, 0x27, 0x72, 0x6c, 0x7c, 0xe4, 0x32, 0x2c,
0x9b, 0x83, 0x7b, 0x9a, 0xde, 0x93, 0xce, 0x77, 0xb1, 0x0b, 0x8c, 0x88, 0x5d, 0xe0, 0x0f, 0xa2,
0x02, 0x49, 0x27, 0x6d, 0xbc, 0x17, 0x9c, 0x0a, 0xf2, 0xcf, 0x4e, 0x64, 0xce, 0xa4, 0x07, 0x80,
0x54, 0xa6, 0xd8, 0x17, 0x92, 0x15, 0x40, 0x4b, 0x29, 0xbe, 0x89, 0x19, 0xba, 0xf2, 0x45, 0xea,
0x13, 0x9e, 0x4a, 0x0b, 0x6e, 0x8b, 0x9f, 0x87, 0x7b, 0xf5, 0xd1, 0x87, 0x75, 0xc5, 0xcb, 0x15,
0x89, 0x2f, 0xcc, 0xfd, 0x04, 0x1e, 0xf8, 0x7f, 0xbd, 0x0f, 0x79, 0x5b, 0xfc, 0x30, 0x14, 0x1f,
0xee, 0x09, 0x66, 0xd4, 0x78, 0x52, 0xdc, 0xa9, 0xf1, 0x33, 0xe8, 0x99, 0x18, 0x14, 0x87, 0xe4,
0x3b, 0x98, 0xf1, 0x89, 0x3f, 0x10, 0x67, 0x98, 0x82, 0xbc, 0x06, 0xcb, 0x8a, 0xe0, 0xdf, 0x9a,
0x3e, 0xea, 0x6f, 0xe2, 0x60, 0x2b, 0x70, 0x19, 0x4f, 0x1c, 0x4d, 0x9f, 0x61, 0xcb, 0xc1, 0x9b,
0xae, 0x8d, 0x2d, 0x82, 0x9a, 0x98, 0x5a, 0x3e, 0xb1, 0x92, 0x73, 0x89, 0x51, 0xcb, 0x6f, 0x7b,
0x86, 0xee, 0xa6, 0x46, 0x50, 0xd8, 0xcc, 0xe0, 0xcd, 0x3b, 0x5c, 0xbd, 0x1d, 0x99, 0x97, 0xfc,
0x0a, 0xe4, 0xda, 0x58, 0xa0, 0x77, 0xc9, 0x74, 0xec, 0xaa, 0x13, 0x99, 0x2f, 0x09, 0x82, 0x0f,
0xa1, 0xdb, 0x7d, 0x52, 0xf2, 0x43, 0x55, 0x17, 0x1e, 0xf0, 0x61, 0x58, 0x80, 0xaf, 0xe8, 0xe7,
0x78, 0x1a, 0xb3, 0x5c, 0xe2, 0xe0, 0x6d, 0x8b, 0xcf, 0xe4, 0x55, 0xcf, 0xb7, 0x37, 0xa8, 0x71,
0x49, 0x2c, 0x69, 0x3e, 0x69, 0x00, 0x57, 0x98, 0xe3, 0xf8, 0x82, 0x4b, 0xa6, 0x04, 0x9a, 0x5d,
0xa2, 0x56, 0x21, 0x65, 0xe1, 0x1a, 0x97, 0xa3, 0x50, 0xe1, 0x09, 0xfc, 0x8d, 0x57, 0x9f, 0x04,
0xd9, 0x1b, 0xd8, 0xb1, 0x88, 0xcf, 0xdc, 0x86, 0x6b, 0xa3, 0xf8, 0x3a, 0xc0, 0xa1, 0x46, 0x5d,
0x8c, 0xef, 0x7b, 0xbc, 0xbb, 0x07, 0x57, 0x62, 0xa5, 0x3b, 0x92, 0xce, 0xdc, 0x0c, 0xef, 0xed,
0xc1, 0x50, 0x89, 0x74, 0x22, 0xf3, 0x42, 0x9c, 0xda, 0x55, 0xb0, 0xb8, 0x3a, 0x54, 0x22, 0x9d,
0xbd, 0x7a, 0x17, 0x8f, 0xbb, 0xfb, 0xf5, 0x2e, 0x2c, 0xa0, 0xd2, 0xc2, 0xa1, 0x00, 0xea, 0xa7,
0x59, 0x80, 0x1a, 0x0d, 0xd7, 0xb6, 0x6c, 0x0f, 0x51, 0x6a, 0x5c, 0x16, 0xdd, 0xfa, 0x02, 0x3f,
0xbe, 0x26, 0xc0, 0x34, 0x97, 0x77, 0x22, 0x13, 0xc4, 0x1d, 0x2a, 0x09, 0xb3, 0x7b, 0x93, 0x82,
0x2a, 0x78, 0x4b, 0xef, 0x4f, 0xba, 0xd8, 0x6a, 0xf8, 0x9e, 0x83, 0x03, 0xab, 0x85, 0xd8, 0xba,
0xf1, 0x94, 0x58, 0xf5, 0xb7, 0x0f, 0x22, 0xf3, 0xc2, 0x0c, 0x6e, 0x05, 0xd8, 0x46, 0x0c, 0x3b,
0x33, 0xb1, 0xe2, 0xac, 0xd0, 0x5b, 0x44, 0x6c, 0xbd, 0x1d, 0x99, 0xda, 0x0b, 0xd9, 0x61, 0xd9,
0x29, 0xc3, 0xcf, 0xfb, 0x4d, 0x97, 0x0f, 0x12, 0xdb, 0xa9, 0x19, 0x1a, 0xec, 0xab, 0xe0, 0x60,
0x43, 0x3f, 0x4b, 0x31, 0xb3, 0x3c, 0x7f, 0xcb, 0x6a, 0x05, 0xae, 0x1f, 0xb8, 0x6c, 0xc7, 0x78,
0x5a, 0x2c, 0x8a, 0xc9, 0x76, 0x64, 0xf6, 0x52, 0xcc, 0xe6, 0xfd, 0xad, 0xc5, 0x04, 0xc9, 0x32,
0x5b, 0x51, 0xdc, 0xf5, 0x58, 0x5e, 0x32, 0x07, 0xef, 0x6b, 0xfa, 0x60, 0x13, 0x6d, 0xa7, 0x61,
0xda, 0x3e, 0xb1, 0xc3, 0x20, 0xc0, 0xc4, 0xde, 0x31, 0x46, 0x45, 0x3f, 0x52, 0x71, 0xf7, 0x81,
0xb6, 0x16, 0xd0, 0x76, 0xcc, 0x71, 0x3a, 0x57, 0xe1, 0x5b, 0x7e, 0x53, 0x21, 0xcf, 0xb6, 0x7c,
0x15, 0x98, 0x76, 0xb9, 0xb8, 0xac, 0x50, 0xfb, 0x85, 0x4a, 0xaf, 0xe0, 0x13, 0x4d, 0xef, 0xb7,
0x03, 0x44, 0xd7, 0x4b, 0x25, 0xf9, 0x33, 0x62, 0x58, 0x3e, 0x10, 0x25, 0xf9, 0x74, 0x5a, 0x92,
0xdb, 0x49, 0x49, 0x3e, 0x1b, 0xef, 0xcd, 0xdc, 0x2c, 0x2f, 0x8e, 0x95, 0x69, 0x58, 0xe8, 0x54,
0xcb, 0x6c, 0x21, 0xe6, 0x73, 0xb9, 0xaf, 0xe2, 0x84, 0x17, 0xeb, 0x76, 0x52, 0xac, 0xd7, 0x1f,
0xc6, 0x0d, 0x2f, 0xd7, 0xa7, 0xe3, 0x72, 0xbd, 0xe4, 0x2c, 0xf0, 0xc0, 0x8f, 0x35, 0x7d, 0xa8,
0x1c, 0x5e, 0x7a, 0x4b, 0xf2, 0xac, 0x18, 0x7f, 0xf7, 0x20, 0x32, 0x4f, 0x4e, 0x43, 0xe9, 0x82,
0xbf, 0xe8, 0xa5, 0x7c, 0xc1, 0xaf, 0x44, 0xbb, 0x4d, 0x8d, 0xdd, 0xfd, 0x7a, 0xee, 0x1b, 0xaa,
0x3d, 0x83, 0xaf, 0x69, 0xfa, 0x20, 0x65, 0x21, 0xb1, 0x78, 0xe5, 0x84, 0x3c, 0x77, 0x13, 0x5b,
0xf1, 0xdd, 0x11, 0x35, 0x9e, 0xcb, 0xea, 0xd1, 0x7e, 0xae, 0x71, 0x3b, 0x55, 0x58, 0xe2, 0xf8,
0x52, 0x56, 0x25, 0x29, 0xb0, 0x62, 0x6d, 0x2d, 0x25, 0xb4, 0x63, 0xe3, 0x37, 0xc7, 0xa0, 0xca,
0x1b, 0x3f, 0xb2, 0x96, 0x68, 0xf0, 0xbc, 0x4a, 0x8d, 0xe7, 0x05, 0x89, 0xd7, 0x78, 0xa1, 0x56,
0x30, 0x5b, 0x70, 0x49, 0x5e, 0xda, 0x57, 0x10, 0xb9, 0x46, 0x2c, 0x24, 0xd4, 0x89, 0x31, 0x58,
0xf5, 0xc3, 0xab, 0xf2, 0x1e, 0xd1, 0x7a, 0xfa, 0xee, 0xf4, 0x82, 0xc8, 0xa1, 0xce, 0x41, 0x64,
0xf6, 0x42, 0xb4, 0xb5, 0xc4, 0x42, 0xe9, 0xc5, 0xe9, 0x14, 0xcd, 0x3f, 0xb3, 0xbb, 0xa1, 0x5c,
0xf6, 0xc0, 0x57, 0xb1, 0x92, 0x47, 0x28, 0xfb, 0x03, 0x9b, 0xfa, 0x19, 0x7e, 0x0a, 0x5c, 0x45,
0x14, 0x5b, 0xf1, 0x13, 0xa0, 0x71, 0x65, 0x44, 0x1b, 0xed, 0x9d, 0xe8, 0x4d, 0xcb, 0xa2, 0x65,
0x21, 0x15, 0x97, 0x79, 0xbd, 0xa9, 0x6a, 0x2c, 0xcb, 0x32, 0x47, 0x51, 0x5c, 0x1b, 0x09, 0xb0,
0x18, 0xd2, 0x64, 0x7a, 0xbc, 0xbd, 0x5f, 0xd7, 0x60, 0xc9, 0x14, 0x7c, 0xe7, 0xa8, 0x7e, 0x89,
0x67, 0x8d, 0x2c, 0x5d, 0xf0, 0x33, 0xa5, 0xed, 0x37, 0xf9, 0x94, 0x0d, 0xf0, 0x1b, 0x21, 0xa6,
0xcc, 0xda, 0x70, 0x57, 0x8d, 0xab, 0x62, 0x38, 0xfe, 0xa4, 0x25, 0x4f, 0x87, 0x0b, 0x68, 0x7b,
0x7a, 0x0e, 0xc6, 0xf8, 0x6d, 0x77, 0xaa, 0x1d, 0x99, 0x66, 0x13, 0x6d, 0x67, 0x4b, 0x9c, 0xcd,
0x25, 0x3e, 0x72, 0x95, 0x6c, 0x17, 0x7c, 0x80, 0x9e, 0x74, 0x1e, 0x7b, 0xa0, 0xcb, 0x07, 0xab,
0x24, 0x8f, 0x91, 0x25, 0xba, 0xf0, 0x01, 0x66, 0xab, 0xe0, 0x33, 0x4d, 0x1f, 0xcc, 0x5e, 0x44,
0x3c, 0x24, 0xbf, 0xa1, 0x8e, 0x89, 0x05, 0xfc, 0x21, 0xef, 0x89, 0x81, 0xf4, 0x45, 0x61, 0x7e,
0xf2, 0x8e, 0xfc, 0x8c, 0x3a, 0x80, 0x14, 0xf2, 0xac, 0x90, 0x56, 0x81, 0xaa, 0x87, 0x2c, 0xa5,
0x93, 0x2e, 0x72, 0x69, 0xe9, 0x2b, 0x49, 0xc1, 0xdc, 0x0a, 0x49, 0x6f, 0xb0, 0x9b, 0xfa, 0x79,
0xf1, 0xe8, 0xd1, 0x08, 0x3d, 0x2f, 0xa9, 0x6a, 0x7c, 0x92, 0x1e, 0x51, 0x8d, 0x71, 0x11, 0xe9,
0x2d, 0x5e, 0x35, 0x70, 0xad, 0xd9, 0xd0, 0xf3, 0x44, 0x3d, 0x72, 0x97, 0x24, 0x87, 0xca, 0x4e,
0x64, 0x5e, 0x4c, 0xb6, 0x2c, 0x15, 0x5c, 0x83, 0x5d, 0xec, 0xc0, 0x6b, 0xfa, 0xe9, 0x06, 0x46,
0x2c, 0x0c, 0xb0, 0xd5, 0xf0, 0xd0, 0x1a, 0x35, 0x26, 0xc4, 0xba, 0xbb, 0xcc, 0x77, 0xfa, 0x04,
0x98, 0xe5, 0xf2, 0xec, 0x81, 0x44, 0x12, 0xd6, 0x60, 0x41, 0x05, 0x6c, 0xe9, 0x43, 0xd2, 0xbb,
0x48, 0x7c, 0xc6, 0xc1, 0xc4, 0x0f, 0xd7, 0xd6, 0x8d, 0x6b, 0x62, 0xd2, 0xbe, 0x2c, 0xd2, 0x6b,
0xa6, 0x32, 0xcf, 0x35, 0x5e, 0x11, 0x0a, 0x59, 0xd5, 0xa3, 0x44, 0xb3, 0x8a, 0x42, 0x6d, 0x0c,
0x36, 0xf4, 0x81, 0x4a, 0xc3, 0x4d, 0xb4, 0x6d, 0x5c, 0x17, 0xad, 0xbe, 0xc4, 0x8b, 0xc1, 0x92,
0xe1, 0x02, 0xda, 0xee, 0x44, 0xa6, 0xa1, 0x6a, 0x72, 0x01, 0x6d, 0x67, 0xed, 0x29, 0xcc, 0xc0,
0xbb, 0x47, 0x75, 0x33, 0xbd, 0xec, 0xb1, 0x90, 0xc7, 0x4b, 0x0a, 0xdf, 0x73, 0x2c, 0xe6, 0x51,
0x8b, 0xe7, 0x0f, 0xd7, 0x27, 0xd4, 0x78, 0x51, 0x8c, 0xd7, 0x47, 0x7c, 0x66, 0x5e, 0x48, 0xaf,
0x56, 0x26, 0xb9, 0xea, 0x5d, 0xcf, 0x59, 0x9e, 0x5f, 0xfa, 0xbf, 0x44, 0xaf, 0x1d, 0x99, 0x17,
0xdc, 0xee, 0x70, 0x56, 0xef, 0xdc, 0x47, 0x87, 0xcf, 0xcf, 0xfb, 0xfa, 0xb8, 0x3f, 0xbc, 0xbb,
0x5f, 0xbf, 0x1f, 0x41, 0x58, 0xb5, 0xf5, 0x68, 0x0a, 0x82, 0x2f, 0xe9, 0x3d, 0x61, 0x8b, 0xb4,
0xb2, 0x0d, 0xf5, 0xe7, 0xb3, 0x22, 0xec, 0xcf, 0x1d, 0x44, 0xe6, 0xb9, 0xbc, 0x96, 0x5b, 0x59,
0x24, 0x8b, 0xf9, 0xee, 0x2a, 0xaa, 0xb8, 0xa4, 0xc0, 0x6d, 0x91, 0x56, 0x02, 0x48, 0xf5, 0xdb,
0xee, 0x7e, 0x5d, 0x6d, 0x6c, 0x68, 0xf0, 0x94, 0x64, 0x02, 0x7e, 0xaa, 0x25, 0xcd, 0xa7, 0xaf,
0x09, 0xef, 0xcf, 0x8a, 0xe1, 0x7e, 0x5b, 0xe4, 0x83, 0xa2, 0x8b, 0xec, 0x65, 0x41, 0x34, 0x3f,
0x92, 0x35, 0x2f, 0xbf, 0x08, 0x48, 0x1c, 0xf2, 0xc4, 0x77, 0xbe, 0xbb, 0x16, 0x5f, 0xe0, 0xaa,
0x56, 0x0c, 0x0d, 0xea, 0xb9, 0x15, 0xf8, 0xb5, 0xa6, 0xf7, 0x0a, 0x9a, 0xf9, 0xbb, 0xc1, 0x2f,
0x62, 0xa2, 0xdf, 0x10, 0xe7, 0x83, 0xa2, 0x0b, 0xe9, 0x0d, 0x41, 0x50, 0xad, 0x65, 0x54, 0x8b,
0xb7, 0xfe, 0x4a, 0xb2, 0x17, 0xef, 0xa7, 0xc7, 0x4f, 0x01, 0xea, 0xb6, 0x0c, 0x0d, 0xf6, 0xc8,
0x96, 0x39, 0xe5, 0xfc, 0x75, 0xe0, 0x83, 0xee, 0x94, 0xa5, 0x97, 0x82, 0x12, 0xe5, 0xe2, 0xdd,
0x7e, 0x77, 0xca, 0xdd, 0xf4, 0xaa, 0x94, 0x53, 0xcd, 0x94, 0x72, 0xf6, 0x18, 0xd0, 0xd0, 0xe3,
0x57, 0xc8, 0xac, 0x7c, 0xf8, 0xe5, 0xac, 0xc8, 0x63, 0xff, 0x53, 0xe4, 0x2b, 0x1e, 0xf2, 0xf2,
0x3a, 0x42, 0x9a, 0x8c, 0x41, 0x8e, 0x14, 0x0f, 0x13, 0x3d, 0x12, 0x42, 0xc5, 0xe5, 0x4d, 0xf5,
0xde, 0xc4, 0x6a, 0xd9, 0xcc, 0xf8, 0x90, 0x77, 0x91, 0x36, 0xb5, 0x70, 0x10, 0x99, 0x17, 0xf3,
0x16, 0x17, 0x8a, 0xb7, 0x1e, 0x8b, 0x36, 0x2b, 0xf6, 0x53, 0xb3, 0x82, 0x17, 0x9b, 0x07, 0x55,
0x05, 0x5e, 0x2b, 0x0d, 0x94, 0x2a, 0x05, 0x6a, 0x23, 0x42, 0x8d, 0x5f, 0xc5, 0xa3, 0xb4, 0x5c,
0xa2, 0x20, 0xef, 0xb0, 0x4b, 0x5c, 0xb1, 0x44, 0xa1, 0x82, 0x57, 0x87, 0x4a, 0x30, 0xa9, 0xe8,
0x4d, 0xdd, 0xfe, 0xf8, 0xd3, 0xe1, 0x23, 0xfb, 0x9f, 0x0e, 0x1f, 0xf9, 0xf8, 0x60, 0x58, 0xdb,
0x3f, 0x18, 0xd6, 0xbe, 0x75, 0x6f, 0xf8, 0xc8, 0x7b, 0xf7, 0x86, 0xb5, 0xfd, 0x7b, 0xc3, 0x47,
0xfe, 0x7a, 0x6f, 0xf8, 0xc8, 0xeb, 0xcf, 0xac, 0xb9, 0x6c, 0x3d, 0x5c, 0xbd, 0x62, 0xfb, 0xcd,
0xab, 0x59, 0xfd, 0x2e, 0xfd, 0xca, 0xff, 0x56, 0xb5, 0x7a, 0x42, 0xfc, 0x8f, 0xea, 0xda, 0x7f,
0x02, 0x00, 0x00, 0xff, 0xff, 0xc4, 0x13, 0xeb, 0x90, 0xb3, 0x25, 0x00, 0x00,
0x15, 0xce, 0x26, 0x4d, 0xda, 0x6c, 0x1c, 0x27, 0x1e, 0x3b, 0xf6, 0x26, 0x4e, 0xbd, 0xee, 0xcd,
0x4d, 0xeb, 0xb6, 0x69, 0x62, 0x3b, 0x3f, 0x4d, 0x8d, 0x50, 0xf1, 0x4f, 0x4d, 0xdd, 0xd8, 0x8e,
0x3b, 0xb6, 0x6b, 0x54, 0x84, 0x56, 0xe3, 0xbd, 0x73, 0xed, 0xad, 0xf7, 0xee, 0xde, 0xec, 0xce,
0xfa, 0xa7, 0x45, 0x50, 0x15, 0x41, 0x79, 0xa3, 0x5c, 0x15, 0x90, 0x40, 0xaa, 0x8a, 0x00, 0x89,
0x52, 0x8a, 0x90, 0x90, 0x90, 0x40, 0x42, 0x54, 0x48, 0x48, 0x15, 0x3c, 0xf8, 0x3e, 0x21, 0x24,
0xca, 0xa2, 0x3a, 0x7d, 0xba, 0x0f, 0x3c, 0xdc, 0x47, 0xf3, 0x82, 0x66, 0xf6, 0x6f, 0x76, 0x77,
0xd6, 0xce, 0xdb, 0xdd, 0xf3, 0x9d, 0x39, 0x73, 0xbe, 0xf9, 0x39, 0x73, 0xce, 0xcc, 0x95, 0x2f,
0x9b, 0xc6, 0xea, 0x35, 0xdd, 0xb6, 0xaa, 0xc6, 0xda, 0x35, 0xbb, 0x4e, 0x0c, 0xdb, 0x72, 0x83,
0x2f, 0xcf, 0x41, 0xf4, 0xeb, 0x6a, 0xdd, 0xb1, 0x89, 0x0d, 0x4e, 0x04, 0xc2, 0x0b, 0x7d, 0x9c,
0x3a, 0xf1, 0x2c, 0xc3, 0x5a, 0x0b, 0x14, 0x2e, 0x9c, 0xe3, 0x00, 0xd7, 0x78, 0x1d, 0x87, 0xe2,
0x93, 0x78, 0x9b, 0x04, 0x3f, 0x4b, 0xef, 0x2d, 0xca, 0x3d, 0x77, 0x83, 0x1e, 0x26, 0xf9, 0x1e,
0xc0, 0x7b, 0x92, 0x7c, 0xd6, 0x34, 0x5c, 0x82, 0x2d, 0x0d, 0x55, 0x2a, 0x0e, 0x76, 0x5d, 0xec,
0x2a, 0xd2, 0xe0, 0xb1, 0xa1, 0x93, 0x13, 0xee, 0x9e, 0xaf, 0x02, 0x88, 0xb6, 0x66, 0x19, 0x3c,
0x1e, 0xa1, 0x2d, 0x5f, 0x3d, 0x63, 0xa6, 0x45, 0x6d, 0x5f, 0xbd, 0xbc, 0x5d, 0x33, 0xc7, 0x4a,
0x29, 0x79, 0x69, 0xb0, 0x82, 0xab, 0xc8, 0x33, 0xc9, 0x58, 0x29, 0xfc, 0x51, 0xda, 0xdf, 0x2d,
0x3f, 0x1c, 0xfe, 0x6e, 0x34, 0xcb, 0x02, 0xe3, 0x30, 0x6b, 0x1a, 0xfc, 0x57, 0x92, 0x95, 0x35,
0xd3, 0x5e, 0x45, 0xa6, 0x56, 0x31, 0x5c, 0xdd, 0xde, 0xc4, 0xce, 0x8e, 0xe6, 0x62, 0x67, 0x13,
0x3b, 0xae, 0x72, 0x94, 0x39, 0xfa, 0x3b, 0x69, 0xcf, 0x57, 0xbb, 0x21, 0xda, 0xfa, 0x32, 0xd3,
0x1b, 0xb7, 0xac, 0xc5, 0x00, 0x6f, 0xf9, 0xea, 0xb9, 0xb5, 0x48, 0x66, 0x7b, 0x96, 0x8e, 0x43,
0xa0, 0xed, 0xab, 0x57, 0x98, 0xc3, 0x22, 0x54, 0xe0, 0x77, 0x6b, 0xb7, 0xdc, 0x23, 0x52, 0x6d,
0xef, 0x96, 0xc5, 0x1d, 0xa4, 0x89, 0x8a, 0x7c, 0x83, 0xbd, 0x41, 0xc3, 0xa9, 0x88, 0x54, 0x28,
0x07, 0x9f, 0x8b, 0x08, 0x63, 0x0b, 0xad, 0x9a, 0xb8, 0xa2, 0x1c, 0x1b, 0x94, 0x86, 0x1e, 0x99,
0xf8, 0x80, 0x12, 0x3e, 0x1b, 0x5b, 0x7c, 0x21, 0x00, 0xf3, 0x6c, 0x43, 0xa0, 0xed, 0xab, 0x4f,
0x09, 0xd8, 0x86, 0x28, 0x47, 0x97, 0x38, 0x1e, 0xa6, 0x5c, 0x0b, 0xcc, 0x14, 0x01, 0xfb, 0xbb,
0xe5, 0x87, 0x68, 0xd3, 0x46, 0xb3, 0x9c, 0x73, 0x2a, 0x47, 0x33, 0x94, 0x83, 0x4f, 0x25, 0xb9,
0xcf, 0xb4, 0x75, 0x21, 0xcb, 0x87, 0x18, 0xcb, 0x9f, 0x51, 0x96, 0x67, 0x66, 0xa9, 0x4e, 0x8a,
0x64, 0x8f, 0x19, 0x8a, 0x32, 0x1c, 0x9f, 0x0c, 0x96, 0xa0, 0x00, 0x14, 0x50, 0x14, 0x1b, 0x29,
0x90, 0x73, 0x04, 0xb3, 0xfe, 0xc0, 0x73, 0xac, 0x41, 0x8e, 0xde, 0xdf, 0x25, 0xb9, 0x3b, 0xa0,
0x87, 0x42, 0x5b, 0x5a, 0xdd, 0x76, 0x88, 0x72, 0x7c, 0x50, 0x1a, 0x3a, 0x3e, 0xf1, 0x63, 0x4a,
0xad, 0x23, 0x32, 0xb5, 0x60, 0x3b, 0xa4, 0xe5, 0xab, 0x5d, 0xa9, 0xae, 0xa9, 0xb0, 0xed, 0xab,
0x4f, 0xe4, 0x49, 0x51, 0x84, 0x63, 0x34, 0x3a, 0x32, 0x3c, 0xfa, 0x6c, 0x69, 0xdf, 0x57, 0x8f,
0x19, 0x16, 0x69, 0xed, 0x96, 0x05, 0x66, 0x44, 0xc2, 0xfd, 0xdd, 0xf2, 0x71, 0xd6, 0xb4, 0xd1,
0x2c, 0xa7, 0x3c, 0x81, 0x79, 0x5d, 0xf0, 0xad, 0xa3, 0xf2, 0x60, 0x86, 0x4d, 0xcd, 0x33, 0x89,
0xa1, 0x23, 0x97, 0x44, 0x71, 0x43, 0x39, 0x31, 0x28, 0x0d, 0x9d, 0x9c, 0xf8, 0x03, 0xa5, 0xd6,
0x19, 0x19, 0x9c, 0x9b, 0xa4, 0x3b, 0xb9, 0xe5, 0xab, 0xdd, 0x29, 0xa3, 0x81, 0xb8, 0xed, 0xab,
0xb7, 0xf2, 0xf4, 0x02, 0x8c, 0x23, 0xf8, 0xd5, 0x6a, 0x75, 0x64, 0x74, 0x6c, 0xec, 0xf6, 0xf5,
0xdb, 0x37, 0xbe, 0x36, 0x16, 0xb0, 0x6d, 0xed, 0x96, 0x85, 0x06, 0xc5, 0xe2, 0xfd, 0xdd, 0x32,
0xc8, 0x1b, 0x69, 0x34, 0xcb, 0x19, 0x37, 0xe1, 0xa3, 0xe9, 0xc6, 0x11, 0xc3, 0x30, 0x18, 0x81,
0xbb, 0xf2, 0xe9, 0x1a, 0xda, 0xd6, 0x5c, 0x6c, 0x55, 0xb4, 0x8d, 0xd5, 0xba, 0xab, 0x3c, 0xcc,
0x26, 0xf3, 0xe9, 0x96, 0xaf, 0x9e, 0xaa, 0xa1, 0xed, 0x45, 0x6c, 0x55, 0xee, 0xac, 0xd6, 0x69,
0x70, 0xe9, 0x62, 0xb4, 0x38, 0x59, 0x34, 0x3f, 0x90, 0x57, 0x8c, 0x0c, 0x3a, 0x58, 0xdf, 0x0c,
0x0c, 0x3e, 0x92, 0x32, 0x08, 0xb1, 0xbe, 0x99, 0x35, 0x18, 0xc9, 0x52, 0x06, 0x23, 0x21, 0xf8,
0xbd, 0x24, 0xf7, 0x39, 0x58, 0xb7, 0x2d, 0x0b, 0xeb, 0x34, 0xbc, 0x6b, 0x86, 0x45, 0xb0, 0xb3,
0x89, 0x4c, 0xcd, 0x55, 0x4e, 0x32, 0xdb, 0xdf, 0x60, 0x41, 0x3d, 0x52, 0x99, 0x09, 0xe1, 0x45,
0x1a, 0x3b, 0xf8, 0x86, 0x31, 0xd0, 0xf6, 0xd5, 0x21, 0xd6, 0xb7, 0x10, 0xe5, 0x66, 0xe9, 0xd6,
0x70, 0xe4, 0xd2, 0xfe, 0x6e, 0xf9, 0xe8, 0xad, 0x61, 0x16, 0xdf, 0x73, 0xfd, 0x40, 0x71, 0x2f,
0xa0, 0x2a, 0x77, 0x3a, 0xd8, 0x44, 0x3b, 0x6e, 0x1c, 0x03, 0x64, 0x16, 0x03, 0x9e, 0x6f, 0xf9,
0xea, 0xe9, 0x00, 0x49, 0x36, 0x7a, 0x29, 0x74, 0x88, 0x93, 0x66, 0x77, 0x78, 0xb4, 0x63, 0x61,
0xba, 0x31, 0x78, 0xeb, 0xa8, 0xdc, 0x1f, 0x76, 0x14, 0x3b, 0x92, 0x0c, 0x52, 0x4d, 0x39, 0xc5,
0x06, 0xe9, 0x2f, 0x74, 0x0d, 0xf7, 0x41, 0xaa, 0x97, 0xa3, 0x30, 0xd7, 0xf2, 0xd5, 0x3e, 0x47,
0x0c, 0xc5, 0x81, 0xb6, 0x00, 0xe7, 0xbc, 0x1c, 0x19, 0xe6, 0xb6, 0x6c, 0xa1, 0xbd, 0x62, 0x88,
0x0e, 0xf2, 0x08, 0x1d, 0xe4, 0x22, 0x37, 0xa1, 0x12, 0xf0, 0xcc, 0x23, 0x60, 0x55, 0x3e, 0xed,
0x12, 0xe4, 0x10, 0x6d, 0xd5, 0xb1, 0xb7, 0x5c, 0xec, 0x28, 0x1d, 0x6c, 0xac, 0xbf, 0xd8, 0xf2,
0xd5, 0x0e, 0x06, 0x4c, 0x04, 0xf2, 0xb6, 0xaf, 0x3e, 0xc6, 0xe8, 0xf0, 0xc2, 0xc2, 0x91, 0x4e,
0x35, 0x05, 0xbf, 0x90, 0xe4, 0x73, 0x16, 0x22, 0x1a, 0x71, 0x10, 0x3d, 0xd5, 0x90, 0x19, 0x4f,
0x6c, 0x27, 0xeb, 0xec, 0xde, 0x9e, 0xaf, 0xca, 0xf3, 0xe3, 0x4b, 0x49, 0x58, 0x97, 0x2d, 0x44,
0x92, 0x39, 0x56, 0x59, 0xc7, 0x89, 0x48, 0x10, 0xc2, 0xf9, 0x06, 0xa9, 0x2f, 0x2e, 0x5c, 0x73,
0x5d, 0xc0, 0x6e, 0x0b, 0x91, 0xa5, 0xc8, 0x9d, 0x68, 0x41, 0xfc, 0x31, 0xe7, 0xa7, 0x89, 0x91,
0x8b, 0xb5, 0x9a, 0x72, 0x86, 0x2d, 0x85, 0xef, 0xd0, 0xa5, 0x70, 0x72, 0x7e, 0x7c, 0x69, 0x96,
0x8a, 0xe9, 0xe4, 0x9f, 0xb1, 0x10, 0x09, 0x3e, 0x0c, 0xcb, 0x23, 0x2c, 0xf9, 0x29, 0x45, 0xce,
0xf2, 0x72, 0xe1, 0xde, 0x68, 0xed, 0x96, 0x73, 0xed, 0xf3, 0xa2, 0x78, 0x07, 0x25, 0x1d, 0x43,
0xc0, 0x7b, 0x1f, 0xc8, 0xc0, 0xdf, 0x24, 0xb9, 0x2f, 0xed, 0xbc, 0x83, 0x2d, 0xbc, 0xc5, 0x56,
0xf2, 0x59, 0xe6, 0x7e, 0x83, 0xba, 0x7f, 0x6a, 0x7e, 0x7c, 0x09, 0x06, 0x00, 0x25, 0xd0, 0x65,
0x21, 0x12, 0x7d, 0xc6, 0x14, 0xca, 0x11, 0x85, 0x34, 0xc2, 0x91, 0xb8, 0xce, 0x93, 0x10, 0xd8,
0x10, 0x09, 0x29, 0x91, 0xeb, 0x94, 0x08, 0xef, 0x02, 0xec, 0xe1, 0xa9, 0x44, 0x52, 0x01, 0x19,
0x62, 0xd4, 0xb0, 0xed, 0x11, 0xcd, 0x55, 0xba, 0xd2, 0x64, 0x96, 0x02, 0x60, 0x31, 0x24, 0x13,
0x7d, 0xd2, 0x95, 0x5e, 0x49, 0x91, 0x49, 0x23, 0x45, 0xdb, 0x4f, 0x60, 0x43, 0x24, 0x8c, 0xb7,
0x1c, 0xef, 0x42, 0x9a, 0x4c, 0x24, 0x05, 0x3f, 0x91, 0x64, 0xc5, 0x73, 0xd1, 0x1a, 0xd6, 0x1c,
0x4c, 0xcf, 0x7d, 0xc3, 0x5a, 0xd3, 0x90, 0xae, 0xe3, 0x3a, 0xc1, 0x15, 0x05, 0x30, 0x36, 0x88,
0xee, 0x80, 0x65, 0x38, 0x1e, 0x4a, 0xe9, 0x0e, 0xf0, 0x9c, 0xe8, 0xab, 0xed, 0xab, 0x67, 0x19,
0x89, 0x44, 0xc4, 0x39, 0xcc, 0x2b, 0xa6, 0xbe, 0xe8, 0x8a, 0x4f, 0x4c, 0xc2, 0x5e, 0xe6, 0x02,
0x8c, 0x3c, 0x88, 0xe4, 0xe0, 0x0d, 0xb9, 0x27, 0xeb, 0x9c, 0x8b, 0xb1, 0xa5, 0x74, 0x33, 0xc7,
0x66, 0xf6, 0x7c, 0xf5, 0xc4, 0x32, 0x5c, 0xc4, 0xd8, 0x6a, 0xf9, 0xea, 0x09, 0xcf, 0xa1, 0xbf,
0xda, 0xbe, 0xda, 0x11, 0x3a, 0x44, 0x3f, 0x39, 0x67, 0x22, 0x85, 0xf8, 0x57, 0xa3, 0x59, 0x0e,
0x9b, 0x43, 0x90, 0x76, 0x80, 0xca, 0xc0, 0x0f, 0x24, 0xf9, 0x7c, 0xb6, 0x77, 0xcf, 0x32, 0xee,
0x79, 0x58, 0x33, 0x2a, 0x4a, 0x0f, 0x4b, 0x22, 0x5e, 0x0d, 0xc6, 0x66, 0x99, 0x89, 0x67, 0xa6,
0x82, 0xb1, 0x09, 0xbf, 0xf8, 0xb1, 0x89, 0x14, 0x4a, 0xc1, 0xa0, 0x44, 0x9f, 0x6d, 0xfe, 0x2b,
0x1c, 0x94, 0x08, 0xcb, 0x0e, 0x4a, 0xa4, 0x05, 0x3e, 0x96, 0xe4, 0xee, 0x9c, 0x5f, 0x8e, 0xa9,
0x9c, 0x63, 0x1e, 0x7d, 0x8f, 0xae, 0xbd, 0xe3, 0xcb, 0x70, 0x19, 0xce, 0xb6, 0x7c, 0xf5, 0xb8,
0xe7, 0x2c, 0xc3, 0xd9, 0xb6, 0xaf, 0xde, 0x8e, 0x1c, 0x81, 0xb3, 0xdc, 0xea, 0x5a, 0x27, 0xa4,
0xee, 0x8e, 0x5d, 0xbb, 0x56, 0x41, 0x04, 0x5d, 0x75, 0x77, 0x2c, 0x9d, 0xac, 0xd3, 0x62, 0xcd,
0xc2, 0xe4, 0x9a, 0x85, 0xb7, 0xa8, 0x94, 0x3a, 0x1c, 0x1a, 0x89, 0x7e, 0xec, 0xef, 0x96, 0x1f,
0xa0, 0x61, 0xa3, 0x59, 0x0e, 0xbc, 0x80, 0x5d, 0x19, 0x1e, 0x8e, 0x09, 0xfe, 0x23, 0xc9, 0x6a,
0x96, 0x42, 0xdd, 0x76, 0xe9, 0x09, 0xe7, 0x62, 0xdd, 0x73, 0xb0, 0xb9, 0xa3, 0xf4, 0xb2, 0xf0,
0xfb, 0x23, 0x56, 0x41, 0x2c, 0xc3, 0x05, 0xdb, 0x25, 0x33, 0x31, 0xd8, 0xf2, 0xd5, 0xb3, 0x9e,
0x93, 0x96, 0xb5, 0x7d, 0xf5, 0xf1, 0x90, 0x64, 0x1a, 0xe0, 0xf8, 0x56, 0x91, 0xe9, 0xb2, 0x90,
0x9c, 0x6f, 0x2d, 0x90, 0xd1, 0xcc, 0x93, 0xb5, 0xa0, 0xf5, 0x42, 0xd6, 0x05, 0x78, 0x31, 0x4d,
0x2b, 0x8d, 0x82, 0x7f, 0x0b, 0x18, 0x1a, 0x96, 0x41, 0x0c, 0x5a, 0x47, 0xd0, 0xf3, 0x4e, 0x73,
0x95, 0x3e, 0xb6, 0x8a, 0x7f, 0xc8, 0xaa, 0x87, 0x65, 0x38, 0x13, 0xa0, 0x53, 0x14, 0xa4, 0x01,
0xe3, 0x8c, 0xe7, 0xa4, 0x44, 0x71, 0xb8, 0xc8, 0xc8, 0xf9, 0x60, 0x71, 0x7b, 0x38, 0x15, 0xc0,
0xb3, 0x16, 0xf2, 0x22, 0x7a, 0x02, 0xd1, 0x56, 0xb4, 0x60, 0xc8, 0xb8, 0x00, 0xfb, 0xd3, 0x04,
0x53, 0x20, 0x78, 0x5b, 0x92, 0xfb, 0x90, 0x47, 0x6c, 0xcd, 0xab, 0xaf, 0x39, 0xa8, 0x82, 0x93,
0xdc, 0x64, 0x5d, 0x39, 0xcf, 0x78, 0x2d, 0xd0, 0x0a, 0x88, 0xaa, 0x2c, 0x07, 0x1a, 0xd1, 0xb1,
0xfe, 0x62, 0x5c, 0x2c, 0x88, 0x40, 0x9e, 0xcd, 0x28, 0x9f, 0xa8, 0x8d, 0x8c, 0x42, 0xa1, 0x35,
0x50, 0x93, 0xfb, 0x22, 0x1f, 0x88, 0xad, 0xd5, 0x1d, 0x3a, 0xe2, 0xec, 0x68, 0x74, 0x95, 0x0b,
0x6c, 0x09, 0xdd, 0xa2, 0x8e, 0x84, 0x2a, 0x4b, 0xf6, 0x82, 0x83, 0x61, 0x88, 0xb7, 0x7d, 0xf5,
0x42, 0x30, 0xa2, 0x02, 0xb0, 0x04, 0x85, 0x6d, 0xc0, 0xa6, 0x0c, 0x36, 0x30, 0xae, 0x6b, 0x04,
0xd7, 0xea, 0xb6, 0x83, 0x1c, 0x03, 0xbb, 0xda, 0xba, 0xd2, 0xcf, 0x28, 0xbf, 0x48, 0xd7, 0x25,
0x45, 0x97, 0x12, 0x90, 0xd2, 0xbd, 0xc4, 0x7a, 0xc9, 0x02, 0x7c, 0x69, 0x74, 0x83, 0xa7, 0x3a,
0x7a, 0x03, 0xe6, 0xac, 0x80, 0x1d, 0xb9, 0x5b, 0x47, 0xfa, 0x3a, 0xd6, 0x8c, 0x35, 0xcb, 0x76,
0x70, 0x45, 0xab, 0x1a, 0x26, 0x76, 0x95, 0x8b, 0x8c, 0xe2, 0x0c, 0x3d, 0x60, 0x18, 0x3c, 0x13,
0xa0, 0xd3, 0x14, 0x8c, 0x07, 0x3a, 0x87, 0xe4, 0xb6, 0x44, 0xbc, 0xd4, 0x61, 0xde, 0x0c, 0xf8,
0xbe, 0x24, 0x5f, 0xa8, 0x3b, 0xf6, 0x1a, 0xad, 0x2d, 0x34, 0xaf, 0x5e, 0x41, 0x04, 0xf3, 0xf9,
0xfa, 0xa3, 0x8c, 0xfb, 0x12, 0x4d, 0x37, 0x23, 0xad, 0x65, 0xa6, 0xc4, 0xe7, 0xe6, 0x41, 0xcd,
0x5b, 0x80, 0x73, 0xee, 0xdc, 0xe4, 0x06, 0x42, 0xba, 0x09, 0x8b, 0x2c, 0x82, 0xb7, 0x24, 0xb9,
0xd7, 0x34, 0x6a, 0x06, 0xd1, 0x56, 0x91, 0x55, 0xd9, 0x32, 0x2a, 0x64, 0x5d, 0x33, 0x2c, 0xcd,
0x44, 0x96, 0x32, 0xc0, 0x86, 0x64, 0x8e, 0xd5, 0x72, 0x54, 0x63, 0x22, 0x52, 0x98, 0xb1, 0x66,
0x91, 0x95, 0xd4, 0xdf, 0x79, 0xec, 0x80, 0x61, 0x11, 0x99, 0x02, 0x6f, 0x4a, 0x32, 0xa8, 0x19,
0x96, 0xb6, 0x6e, 0xd7, 0xb0, 0x56, 0x31, 0xdc, 0x0d, 0xad, 0xea, 0x60, 0xac, 0xa8, 0x83, 0xd2,
0xd0, 0xa9, 0xd1, 0x8e, 0xab, 0xc1, 0x45, 0xd7, 0xd5, 0x45, 0xe3, 0x75, 0x3c, 0xf1, 0xc2, 0x27,
0xbe, 0x7a, 0x84, 0xee, 0xea, 0x9a, 0x61, 0xbd, 0x68, 0xd7, 0xf0, 0x94, 0xe1, 0x6e, 0x4c, 0x3b,
0x18, 0xc7, 0xab, 0x23, 0x23, 0xe7, 0xf7, 0xc1, 0xe0, 0x65, 0xea, 0xc8, 0xb1, 0x91, 0xc1, 0xcb,
0x30, 0xdb, 0x1c, 0xdc, 0x97, 0xe4, 0x8e, 0x68, 0xbd, 0xb3, 0x53, 0x60, 0x90, 0x9d, 0x02, 0x7f,
0x66, 0x19, 0x48, 0xb4, 0x68, 0x83, 0xb3, 0xe0, 0x94, 0x93, 0x7c, 0xb6, 0x7d, 0x75, 0x2a, 0x2a,
0x00, 0x22, 0x99, 0xe0, 0x5c, 0x08, 0x77, 0x80, 0x9b, 0x09, 0xf1, 0x35, 0x4c, 0xd0, 0xd5, 0xd7,
0x5c, 0xdb, 0xa2, 0xa1, 0x34, 0x65, 0x36, 0xfd, 0xb9, 0xbf, 0x5b, 0x1e, 0x7a, 0x50, 0x53, 0x34,
0x5d, 0xe1, 0xfc, 0x85, 0x89, 0x1d, 0xc7, 0x04, 0x2b, 0x72, 0x17, 0x32, 0xb7, 0x68, 0x31, 0x14,
0x14, 0xf7, 0x16, 0x26, 0xae, 0xf2, 0x18, 0xbb, 0x53, 0xa3, 0x35, 0xe8, 0x99, 0x00, 0x64, 0x45,
0xf2, 0x3c, 0x26, 0x74, 0xe1, 0xf7, 0x04, 0x11, 0x26, 0x25, 0x2f, 0xc1, 0xac, 0x22, 0xf8, 0x9f,
0x24, 0x0f, 0xd9, 0x9b, 0xd8, 0xd9, 0x72, 0x0c, 0x42, 0x03, 0x47, 0xcd, 0x26, 0x58, 0xab, 0xe0,
0x4d, 0x43, 0xc7, 0x9a, 0x85, 0x6a, 0xd8, 0xd5, 0x6c, 0x4b, 0x0b, 0xeb, 0x12, 0xa5, 0x94, 0xdc,
0xf6, 0xf4, 0xdd, 0x8d, 0x1a, 0x41, 0xd6, 0x66, 0x0a, 0x6f, 0xce, 0x53, 0xf5, 0x96, 0xaf, 0x5e,
0xb2, 0x73, 0x90, 0xa1, 0x63, 0x86, 0xde, 0xb5, 0x26, 0x03, 0x53, 0x6d, 0x5f, 0x7d, 0x8e, 0x39,
0xf8, 0x00, 0xba, 0xc5, 0x8b, 0x92, 0x16, 0x55, 0x05, 0x7e, 0xc0, 0x07, 0xf1, 0x02, 0x7c, 0x53,
0x3e, 0x47, 0xc3, 0x98, 0x66, 0x58, 0x15, 0xbc, 0xad, 0xd1, 0x95, 0xbc, 0x6a, 0xda, 0xfa, 0x86,
0xab, 0x5c, 0x62, 0x5b, 0x9a, 0x2e, 0x1a, 0x40, 0x15, 0x66, 0x28, 0x3e, 0x67, 0x58, 0x13, 0x0c,
0x8d, 0x2f, 0x51, 0xf3, 0x90, 0x30, 0x71, 0x0d, 0xd2, 0x51, 0x28, 0xb0, 0x04, 0xfe, 0x45, 0xb3,
0x4f, 0x0b, 0xe9, 0x1b, 0xb8, 0xa2, 0x59, 0x36, 0x31, 0xaa, 0x86, 0x8e, 0x82, 0xeb, 0x80, 0x8a,
0xab, 0x94, 0xd9, 0xfc, 0xbe, 0x4f, 0x87, 0xbb, 0x77, 0x39, 0x50, 0x9a, 0xe7, 0x74, 0x66, 0xa6,
0xe8, 0x68, 0xf7, 0x7a, 0x42, 0xa4, 0xed, 0xab, 0xfd, 0x41, 0x68, 0x17, 0xc1, 0xec, 0xea, 0x50,
0x88, 0xb4, 0x77, 0xcb, 0x05, 0x16, 0x1b, 0xcd, 0x72, 0x81, 0x17, 0x50, 0xd8, 0xa2, 0xe2, 0x02,
0x28, 0x9f, 0x26, 0x0e, 0xaa, 0x56, 0x0d, 0x5d, 0xd3, 0x4d, 0xe4, 0xba, 0xca, 0x65, 0x36, 0xac,
0xcf, 0xd0, 0xf2, 0x35, 0x04, 0x26, 0xa9, 0xbc, 0xed, 0xab, 0x20, 0x18, 0x50, 0x4e, 0x18, 0xdf,
0x9b, 0xa4, 0x54, 0xc1, 0x1b, 0x72, 0x77, 0x38, 0xc4, 0x5a, 0xd5, 0x36, 0x2b, 0xd8, 0xd1, 0xea,
0x88, 0xac, 0x2b, 0x8f, 0xb3, 0x5d, 0x7f, 0x67, 0xcf, 0x57, 0xfb, 0xa7, 0x70, 0xdd, 0xc1, 0x3a,
0x22, 0xb8, 0x32, 0x15, 0x28, 0x4e, 0x33, 0xbd, 0x05, 0x44, 0xd6, 0x5b, 0xbe, 0x2a, 0x3d, 0x13,
0x17, 0xcb, 0x95, 0x2c, 0x7c, 0xc5, 0xae, 0x19, 0x74, 0x92, 0xc8, 0x4e, 0x49, 0x91, 0x60, 0x57,
0x0e, 0x07, 0x1b, 0xf2, 0x59, 0x17, 0x13, 0xcd, 0xb4, 0xb7, 0xb4, 0xba, 0x63, 0xd8, 0x8e, 0x41,
0x76, 0x94, 0x27, 0xd8, 0xa6, 0x18, 0x6f, 0xf9, 0x6a, 0xa7, 0x8b, 0xc9, 0xac, 0xbd, 0xb5, 0x10,
0x22, 0x71, 0x64, 0x4b, 0x8b, 0x0b, 0xcb, 0xf2, 0x4c, 0x73, 0xf0, 0x81, 0x24, 0xf7, 0xd6, 0xd0,
0x76, 0x44, 0x53, 0xb7, 0x2d, 0xdd, 0x73, 0x1c, 0x6c, 0xe9, 0x3b, 0xca, 0x10, 0x1b, 0x47, 0x97,
0xdd, 0x7d, 0xa0, 0xad, 0x39, 0xb4, 0x1d, 0xf8, 0x38, 0x99, 0xa8, 0xd0, 0x23, 0xbf, 0x26, 0x90,
0xc7, 0x47, 0xbe, 0x08, 0x8c, 0x86, 0x9c, 0x5d, 0x56, 0x88, 0xed, 0x42, 0xa1, 0x55, 0xf0, 0xa9,
0x24, 0x77, 0xeb, 0x0e, 0x72, 0xd7, 0x33, 0x29, 0xf9, 0x93, 0x6c, 0x5a, 0x3e, 0x64, 0x29, 0xf9,
0x64, 0x94, 0x92, 0xeb, 0x61, 0x4a, 0x3e, 0x1d, 0x9c, 0xcd, 0xb4, 0x59, 0x92, 0x1c, 0x0b, 0xc3,
0x30, 0xd3, 0xc9, 0xa7, 0xd9, 0x4c, 0x4c, 0xd7, 0x72, 0x57, 0xce, 0x08, 0x4d, 0xd6, 0xf5, 0x30,
0x59, 0x2f, 0x3f, 0x88, 0x19, 0x9a, 0xae, 0x4f, 0x06, 0xe9, 0x7a, 0xc6, 0x98, 0x63, 0x82, 0x9f,
0x4a, 0x72, 0x5f, 0x96, 0x5e, 0x74, 0x4b, 0xf2, 0x14, 0x9b, 0x7f, 0x63, 0xcf, 0x57, 0x4f, 0x4e,
0x42, 0xee, 0x82, 0x3f, 0x6d, 0x25, 0x7b, 0xc1, 0x2f, 0x44, 0x8b, 0x96, 0x46, 0xa3, 0x59, 0x4e,
0x6c, 0x43, 0xb1, 0x65, 0xf0, 0x6d, 0x49, 0xee, 0x75, 0x89, 0x67, 0x69, 0x34, 0x73, 0x42, 0xa6,
0xb1, 0x89, 0xb5, 0xe0, 0xee, 0xc8, 0x55, 0x9e, 0x8e, 0xf3, 0xd1, 0x6e, 0xaa, 0x71, 0x27, 0x52,
0x58, 0xa4, 0xf8, 0x62, 0x9c, 0x25, 0x09, 0xb0, 0x74, 0x6e, 0xcd, 0x05, 0xb4, 0x63, 0x23, 0xb7,
0x87, 0xa1, 0xc8, 0x1a, 0x2d, 0x59, 0x33, 0x6e, 0xd0, 0xb8, 0xea, 0x2a, 0x57, 0x98, 0x13, 0x2f,
0xd1, 0x44, 0x2d, 0xd5, 0x6c, 0xce, 0xb0, 0x92, 0xd4, 0x3e, 0x87, 0xf0, 0x39, 0x62, 0x2a, 0xa0,
0x8e, 0x0e, 0xc3, 0xbc, 0x1d, 0x9a, 0x95, 0x77, 0xb0, 0xde, 0xa3, 0x77, 0xa7, 0x67, 0x58, 0x0c,
0xad, 0xec, 0xf9, 0x6a, 0x27, 0x44, 0x5b, 0x8b, 0xc4, 0xe3, 0x5e, 0x9c, 0x4e, 0xb9, 0xc9, 0x67,
0x7c, 0x37, 0x94, 0xc8, 0x0e, 0x7d, 0x15, 0xcb, 0x58, 0x84, 0xbc, 0x3d, 0xb0, 0x29, 0x9f, 0xa1,
0x55, 0xe0, 0x2a, 0x72, 0xb1, 0x16, 0x3c, 0x01, 0x2a, 0x57, 0x07, 0xa5, 0xa1, 0xce, 0xd1, 0xce,
0x28, 0x2d, 0x5a, 0x62, 0x52, 0x76, 0x99, 0xd7, 0x19, 0xa9, 0x06, 0xb2, 0x38, 0x72, 0xa4, 0xc5,
0xa5, 0x41, 0x07, 0xb3, 0x29, 0x0d, 0x97, 0xc7, 0x9b, 0xcd, 0xb2, 0x04, 0x33, 0x4d, 0xc1, 0xbb,
0x47, 0xe5, 0x4b, 0x34, 0x6a, 0xc4, 0xe1, 0x82, 0xd6, 0x94, 0xba, 0x5d, 0xa3, 0x4b, 0xd6, 0xc1,
0xf7, 0x3c, 0xec, 0x12, 0x6d, 0xc3, 0x58, 0x55, 0xae, 0xb1, 0xe9, 0xf8, 0xab, 0x14, 0x3e, 0x1d,
0xce, 0xa1, 0xed, 0xc9, 0x19, 0x18, 0xe0, 0x77, 0x8c, 0x89, 0x96, 0xaf, 0xaa, 0x35, 0xb4, 0x1d,
0x6f, 0x71, 0x32, 0x13, 0xda, 0x48, 0x54, 0xe2, 0x53, 0xf0, 0x10, 0x3d, 0xae, 0x1e, 0x3b, 0xd4,
0xe4, 0xe1, 0x2a, 0xe1, 0x63, 0x64, 0xc6, 0x5d, 0x78, 0x48, 0xb3, 0x55, 0xf0, 0xb9, 0x24, 0xf7,
0xc6, 0x2f, 0x22, 0x26, 0xe2, 0xdf, 0x50, 0x87, 0xd9, 0x06, 0xfe, 0x88, 0x8e, 0x44, 0x4f, 0xf4,
0xa2, 0x30, 0x3b, 0x3e, 0xcf, 0x3f, 0xa3, 0xf6, 0x20, 0x81, 0x3c, 0x4e, 0xa4, 0x45, 0xa0, 0xe8,
0x21, 0x4b, 0x68, 0xa4, 0x40, 0xce, 0x6d, 0x7d, 0xa1, 0x53, 0x30, 0x69, 0x85, 0xb8, 0x37, 0xd8,
0x4d, 0xf9, 0x02, 0x7b, 0xf4, 0xa8, 0x7a, 0xa6, 0x19, 0x66, 0x35, 0xb6, 0x15, 0x95, 0xa8, 0xca,
0x08, 0x63, 0x3a, 0x46, 0xb3, 0x06, 0xaa, 0x35, 0xed, 0x99, 0x26, 0xcb, 0x47, 0xee, 0x5a, 0x61,
0x51, 0xd9, 0xf6, 0xd5, 0x8b, 0xe1, 0x91, 0x25, 0x82, 0x4b, 0xb0, 0xa0, 0x1d, 0x78, 0x49, 0x3e,
0x5d, 0xc5, 0x88, 0x78, 0x0e, 0xd6, 0xaa, 0x26, 0x5a, 0x73, 0x95, 0x51, 0xb6, 0xef, 0x2e, 0xd3,
0x93, 0x3e, 0x04, 0xa6, 0xa9, 0x3c, 0x7e, 0x20, 0xe1, 0x84, 0x25, 0x98, 0x52, 0x01, 0x5b, 0x72,
0x1f, 0xf7, 0x2e, 0x12, 0xd4, 0x38, 0xd8, 0xb2, 0xbd, 0xb5, 0x75, 0xe5, 0x3a, 0x5b, 0xb4, 0xcf,
0xb3, 0xf0, 0x1a, 0xab, 0xcc, 0x52, 0x8d, 0x17, 0x98, 0x42, 0x9c, 0xf5, 0x08, 0xd1, 0x38, 0xa3,
0x10, 0x37, 0x06, 0x1b, 0x72, 0x4f, 0xae, 0xe3, 0x1a, 0xda, 0x56, 0x6e, 0xb0, 0x5e, 0x9f, 0xa3,
0xc9, 0x60, 0xa6, 0xe1, 0x1c, 0xda, 0x6e, 0xfb, 0xaa, 0x22, 0xea, 0x72, 0x0e, 0x6d, 0xc7, 0xfd,
0x09, 0x9a, 0x81, 0xb7, 0x8f, 0xca, 0x6a, 0x74, 0xd9, 0xa3, 0x21, 0x93, 0xa6, 0x14, 0xb6, 0x59,
0xd1, 0x88, 0xe9, 0x6a, 0x34, 0x7e, 0x18, 0xb6, 0xe5, 0x2a, 0x37, 0xd9, 0x7c, 0x7d, 0x4c, 0x57,
0x66, 0x7f, 0x74, 0xb5, 0x32, 0x4e, 0x55, 0xef, 0x9a, 0x95, 0xa5, 0xd9, 0xc5, 0x57, 0x42, 0xbd,
0x96, 0xaf, 0xf6, 0x1b, 0xc5, 0x70, 0x9c, 0xef, 0x1c, 0xa0, 0x43, 0xd7, 0xe7, 0x81, 0x36, 0x0e,
0x86, 0x1b, 0xcd, 0xf2, 0x41, 0x0e, 0xc2, 0x7c, 0x5b, 0xd3, 0x8d, 0x40, 0xd0, 0x94, 0xe4, 0x7e,
0x6e, 0xdc, 0xa3, 0xc4, 0x4a, 0x23, 0x7a, 0x9d, 0x95, 0xb3, 0xb7, 0xd8, 0xf0, 0xbf, 0x43, 0x47,
0x41, 0x99, 0x8c, 0xf5, 0xa2, 0x34, 0x69, 0x69, 0x72, 0x61, 0x76, 0x7c, 0xbe, 0xe5, 0xab, 0x8a,
0x9e, 0xc7, 0xf4, 0x7a, 0x50, 0xf0, 0x3e, 0x9d, 0x99, 0xa1, 0xb4, 0xc2, 0x01, 0x49, 0x7b, 0xa3,
0x59, 0x2e, 0xec, 0x13, 0x16, 0xf6, 0x08, 0xfe, 0x21, 0xc9, 0x17, 0x45, 0x94, 0xee, 0x79, 0x86,
0xce, 0x38, 0x3d, 0xcb, 0x38, 0xbd, 0x4b, 0x39, 0x9d, 0xcf, 0xdb, 0x7f, 0x79, 0x79, 0x66, 0x32,
0x20, 0x75, 0x3e, 0xdf, 0xc5, 0xcb, 0x9e, 0xa1, 0x07, 0xac, 0xae, 0x14, 0xb0, 0x0a, 0x35, 0x0e,
0x38, 0x3a, 0x1b, 0xcd, 0x72, 0x71, 0xb7, 0xb0, 0xb8, 0xd3, 0x03, 0xe7, 0x6a, 0x0b, 0x59, 0xca,
0xed, 0xc3, 0xe6, 0x6a, 0xe5, 0x80, 0xb9, 0x5a, 0x39, 0x6c, 0xae, 0x56, 0x52, 0xa4, 0xae, 0xa7,
0x48, 0x5d, 0x2f, 0x9e, 0xab, 0x95, 0xc2, 0xb9, 0x5a, 0x39, 0x6c, 0xae, 0x28, 0xa7, 0xe7, 0x0e,
0x9d, 0xab, 0x95, 0x83, 0xe6, 0x6a, 0xe5, 0xd0, 0xb9, 0x4a, 0xd3, 0xba, 0x91, 0xa2, 0x75, 0xe3,
0x80, 0xb9, 0x5a, 0x29, 0x9e, 0x2b, 0x4a, 0xac, 0x21, 0xc9, 0xe7, 0x45, 0xc4, 0xd8, 0x6b, 0xa3,
0x32, 0xc6, 0x58, 0xbd, 0xd2, 0xf2, 0xd5, 0xbe, 0xbc, 0x09, 0xf6, 0x52, 0x99, 0xe4, 0xaa, 0x62,
0x9c, 0xbf, 0xb4, 0x4a, 0xf9, 0x7c, 0x73, 0x18, 0x16, 0xd9, 0x04, 0x7f, 0x92, 0xe4, 0xcb, 0x22,
0xa7, 0xe2, 0x1b, 0xcc, 0x75, 0x07, 0xbb, 0xeb, 0xb6, 0x59, 0x51, 0xbe, 0xc0, 0x1c, 0x7c, 0xad,
0xe5, 0xab, 0x02, 0x07, 0xc2, 0x73, 0x67, 0x29, 0xd2, 0x6e, 0xfb, 0xea, 0x8d, 0x02, 0x5f, 0xb3,
0xaa, 0x9c, 0xdb, 0xbc, 0xd7, 0xd2, 0x30, 0x7c, 0x80, 0xc6, 0xe0, 0xeb, 0x72, 0x87, 0x57, 0xb7,
0xea, 0x71, 0xf6, 0xff, 0xcb, 0x69, 0x16, 0xa3, 0xbf, 0xb2, 0xe7, 0xab, 0xe7, 0x92, 0xc2, 0x73,
0x79, 0xc1, 0x5a, 0x48, 0x4a, 0x01, 0x56, 0x72, 0x86, 0xd5, 0x78, 0xdd, 0xaa, 0x87, 0x00, 0x57,
0x6c, 0x36, 0x9a, 0x65, 0x71, 0x63, 0x45, 0x82, 0xa7, 0xb8, 0x26, 0xe0, 0xe7, 0x52, 0xd8, 0x7d,
0xf4, 0xf4, 0xf9, 0xc1, 0x34, 0x1b, 0xa5, 0x37, 0x59, 0xf2, 0x92, 0x36, 0x11, 0x3f, 0x83, 0xb2,
0xee, 0x07, 0xe3, 0xee, 0xf9, 0xe7, 0x4b, 0xce, 0x87, 0x24, 0x4b, 0xbb, 0x50, 0xac, 0x45, 0xb3,
0x11, 0x51, 0x2f, 0x8a, 0x04, 0xe5, 0xa4, 0x15, 0xf8, 0xad, 0x24, 0x77, 0x32, 0x37, 0x93, 0x47,
0xce, 0x5f, 0x05, 0x8e, 0x7e, 0x97, 0x5d, 0x66, 0xa4, 0x4d, 0x70, 0x0f, 0x9e, 0xcc, 0xd5, 0x52,
0xec, 0x6a, 0xfa, 0x89, 0x52, 0xe8, 0xec, 0xc5, 0x83, 0xf4, 0x1a, 0xcd, 0x72, 0x41, 0x5f, 0x8a,
0x04, 0x3b, 0xf8, 0x96, 0x89, 0xcb, 0xc9, 0x53, 0xe6, 0x87, 0xc5, 0x2e, 0x73, 0xcf, 0x9a, 0x19,
0x97, 0xd3, 0x0f, 0x91, 0xc5, 0x2e, 0x17, 0xe9, 0xe5, 0x5d, 0x8e, 0x34, 0x23, 0x97, 0xe3, 0x97,
0xcb, 0xaa, 0x1c, 0xfc, 0x65, 0x22, 0xae, 0x75, 0x7e, 0x3d, 0xcd, 0x92, 0xae, 0x2f, 0xa5, 0xfd,
0x65, 0xfb, 0x2e, 0x29, 0x7a, 0xb8, 0xc5, 0xe8, 0x24, 0x48, 0xfa, 0xe6, 0xa3, 0x83, 0x43, 0x5c,
0x76, 0xd3, 0x9c, 0xbf, 0xe4, 0xd5, 0xea, 0x3a, 0x51, 0x3e, 0xa2, 0x43, 0x24, 0x4d, 0xcc, 0xed,
0xf9, 0xea, 0xc5, 0xa4, 0xc7, 0xb9, 0xf4, 0x15, 0xed, 0x82, 0x4e, 0xd2, 0xe3, 0x54, 0xcb, 0xe1,
0xe9, 0xee, 0x41, 0x5e, 0x81, 0x16, 0x76, 0x3d, 0x99, 0xb2, 0xc6, 0xd5, 0x91, 0xe5, 0x2a, 0xbf,
0x09, 0x66, 0x69, 0x29, 0xe3, 0x02, 0x5f, 0x0e, 0x2c, 0x52, 0xc5, 0x8c, 0x0b, 0x39, 0x3c, 0x3f,
0x55, 0xcc, 0x93, 0x9c, 0xde, 0xc4, 0x9d, 0x4f, 0x3e, 0x1b, 0x38, 0xd2, 0xfc, 0x6c, 0xe0, 0xc8,
0x27, 0x7b, 0x03, 0x52, 0x73, 0x6f, 0x40, 0x7a, 0xe7, 0xfe, 0xc0, 0x91, 0xf7, 0xef, 0x0f, 0x48,
0xcd, 0xfb, 0x03, 0x47, 0xfe, 0x79, 0x7f, 0xe0, 0xc8, 0xab, 0x4f, 0xae, 0x19, 0x64, 0xdd, 0x5b,
0xbd, 0xaa, 0xdb, 0xb5, 0x6b, 0xf1, 0x65, 0x03, 0xf7, 0x2b, 0xf9, 0x0f, 0xe8, 0xea, 0x09, 0xf6,
0xa7, 0xcf, 0xeb, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x24, 0x93, 0x46, 0x60, 0x2a, 0x00,
0x00,
}
func (m *OptionsConfiguration) Marshal() (dAtA []byte, err error) {
@@ -435,6 +458,48 @@ func (m *OptionsConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0xc0
}
if m.ConnectionPriorityUpgradeThreshold != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityUpgradeThreshold))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xd8
}
if m.ConnectionPriorityRelay != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityRelay))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xd0
}
if m.ConnectionPriorityQUICWAN != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityQUICWAN))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xc8
}
if m.ConnectionPriorityTCPWAN != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityTCPWAN))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xc0
}
if m.ConnectionPriorityQUICLAN != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityQUICLAN))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xb8
}
if m.ConnectionPriorityTCPLAN != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionPriorityTCPLAN))
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xb0
}
if m.InsecureAllowOldTLSVersions {
i--
if m.InsecureAllowOldTLSVersions {
@@ -1076,6 +1141,24 @@ func (m *OptionsConfiguration) ProtoSize() (n int) {
if m.InsecureAllowOldTLSVersions {
n += 3
}
if m.ConnectionPriorityTCPLAN != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityTCPLAN))
}
if m.ConnectionPriorityQUICLAN != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityQUICLAN))
}
if m.ConnectionPriorityTCPWAN != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityTCPWAN))
}
if m.ConnectionPriorityQUICWAN != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityQUICWAN))
}
if m.ConnectionPriorityRelay != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityRelay))
}
if m.ConnectionPriorityUpgradeThreshold != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionPriorityUpgradeThreshold))
}
if m.DeprecatedUPnPEnabled {
n += 4
}
@@ -2292,6 +2375,120 @@ func (m *OptionsConfiguration) Unmarshal(dAtA []byte) error {
}
}
m.InsecureAllowOldTLSVersions = bool(v != 0)
case 54:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityTCPLAN", wireType)
}
m.ConnectionPriorityTCPLAN = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityTCPLAN |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 55:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityQUICLAN", wireType)
}
m.ConnectionPriorityQUICLAN = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityQUICLAN |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 56:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityTCPWAN", wireType)
}
m.ConnectionPriorityTCPWAN = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityTCPWAN |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 57:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityQUICWAN", wireType)
}
m.ConnectionPriorityQUICWAN = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityQUICWAN |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 58:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityRelay", wireType)
}
m.ConnectionPriorityRelay = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityRelay |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 59:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPriorityUpgradeThreshold", wireType)
}
m.ConnectionPriorityUpgradeThreshold = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ConnectionPriorityUpgradeThreshold |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 9000:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedUPnPEnabled", wireType)

View File

@@ -45,6 +45,11 @@
<unackedNotificationID>asdfasdf</unackedNotificationID>
<announceLANAddresses>false</announceLANAddresses>
<featureFlag>feature</featureFlag>
<connectionPriorityTcpLan>40</connectionPriorityTcpLan>
<connectionPriorityQuicLan>45</connectionPriorityQuicLan>
<connectionPriorityTcpWan>50</connectionPriorityTcpWan>
<connectionPriorityQuicWan>55</connectionPriorityQuicWan>
<connectionPriorityRelay>9000</connectionPriorityRelay>
</options>
<defaults>
<folder id="" label="" path="/media/syncthing" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">

View File

@@ -293,6 +293,9 @@ func (w *wrapper) Serve(ctx context.Context) error {
}
func (w *wrapper) serveSave() {
if w.path == "" {
return
}
if err := w.Save(); err != nil {
l.Warnln("Failed to save config:", err)
}

View File

@@ -261,7 +261,7 @@ func TestNextDialRegistryCleanup(t *testing.T) {
},
// Threshold reached, but outside of cooldown delay
{
attempts: dialCoolDownMaxAttemps,
attempts: dialCoolDownMaxAttempts,
coolDownIntervalStart: firsts[2],
},
} {
@@ -281,11 +281,11 @@ func TestNextDialRegistryCleanup(t *testing.T) {
},
// attempts at threshold, inside delay
{
attempts: dialCoolDownMaxAttemps,
attempts: dialCoolDownMaxAttempts,
coolDownIntervalStart: firsts[0],
},
{
attempts: dialCoolDownMaxAttemps,
attempts: dialCoolDownMaxAttempts,
coolDownIntervalStart: firsts[1],
},
} {
@@ -439,7 +439,8 @@ func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h
}
natSvc := nat.NewService(deviceId, wcfg)
conns := make(chan internalConn, 1)
listenSvc := lf.New(uri, wcfg, tlsCfg, conns, natSvc, registry.New())
lanChecker := &lanChecker{wcfg}
listenSvc := lf.New(uri, wcfg, tlsCfg, conns, natSvc, registry.New(), lanChecker)
supervisor.Add(listenSvc)
var addr *url.URL
@@ -459,7 +460,7 @@ func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h
b.Fatal(err)
}
// Purposely using a different registry: Don't want to reuse port between dialer and listener on the same device
dialer := df.New(cfg.Options, tlsCfg, registry.New())
dialer := df.New(cfg.Options, tlsCfg, registry.New(), lanChecker)
// Relays might take some time to register the device, so dial multiple times
clientConn, err := dialer.Dial(ctx, deviceId, addr)

View File

@@ -38,7 +38,7 @@ func TestIsLANHost(t *testing.T) {
AlwaysLocalNets: []string{"10.20.30.0/24"},
},
}, protocol.LocalDeviceID, events.NoopLogger)
s := &service{cfg: cfg}
s := &lanChecker{cfg: cfg}
for _, tc := range cases {
res := s.isLANHost(tc.addr)

View File

@@ -25,8 +25,6 @@ import (
)
const (
quicPriority = 100
// The timeout for connecting, accepting and creating the various
// streams.
quicOperationTimeout = 10 * time.Second
@@ -92,12 +90,17 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
return internalConn{}, fmt.Errorf("open stream: %w", err)
}
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority), nil
priority := d.wanPriority
isLocal := d.lanChecker.isLAN(session.RemoteAddr())
if isLocal {
priority = d.lanPriority
}
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, isLocal, priority), nil
}
type quicDialerFactory struct{}
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry) genericDialer {
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry, lanChecker *lanChecker) genericDialer {
// So the idea is that we should probably try dialing every 20 seconds.
// However it would still be nice if this was adjustable/proportional to ReconnectIntervalS
// But prevent something silly like 1/3 = 0 etc.
@@ -109,15 +112,14 @@ func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Confi
commonDialer: commonDialer{
reconnectInterval: time.Duration(quicInterval) * time.Second,
tlsCfg: tlsCfg,
lanPriority: opts.ConnectionPriorityQUICLAN,
wanPriority: opts.ConnectionPriorityQUICWAN,
lanChecker: lanChecker,
},
registry: registry,
}
}
func (quicDialerFactory) Priority() int {
return quicPriority
}
func (quicDialerFactory) AlwaysWAN() bool {
return false
}

View File

@@ -40,12 +40,13 @@ type quicListener struct {
onAddressesChangedNotifier
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
lanChecker *lanChecker
address *url.URL
laddr net.Addr
@@ -168,7 +169,12 @@ func (t *quicListener) serve(ctx context.Context) error {
continue
}
t.conns <- newInternalConn(&quicTlsConn{session, stream, nil}, connTypeQUICServer, quicPriority)
priority := t.cfg.Options().ConnectionPriorityQUICWAN
isLocal := t.lanChecker.isLAN(session.RemoteAddr())
if isLocal {
priority = t.cfg.Options().ConnectionPriorityQUICLAN
}
t.conns <- newInternalConn(&quicTlsConn{session, stream, nil}, connTypeQUICServer, isLocal, priority)
}
}
@@ -218,14 +224,15 @@ func (*quicListenerFactory) Valid(config.Configuration) error {
return nil
}
func (f *quicListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, _ *nat.Service, registry *registry.Registry) genericListener {
func (f *quicListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, _ *nat.Service, registry *registry.Registry, lanChecker *lanChecker) genericListener {
l := &quicListener{
uri: fixupPort(uri, config.DefaultQUICPort),
cfg: cfg,
tlsCfg: tlsCfg,
conns: conns,
factory: f,
registry: registry,
uri: fixupPort(uri, config.DefaultQUICPort),
cfg: cfg,
tlsCfg: tlsCfg,
conns: conns,
factory: f,
registry: registry,
lanChecker: lanChecker,
}
l.ServiceWithError = svcutil.AsService(l.serve, l.String())
l.nat.Store(uint64(stun.NATUnknown))

View File

@@ -19,8 +19,6 @@ import (
"github.com/syncthing/syncthing/lib/relay/client"
)
const relayPriority = 200
func init() {
dialers["relay"] = relayDialerFactory{}
}
@@ -64,23 +62,25 @@ func (d *relayDialer) Dial(ctx context.Context, id protocol.DeviceID, uri *url.U
return internalConn{}, err
}
return newInternalConn(tc, connTypeRelayClient, relayPriority), nil
return newInternalConn(tc, connTypeRelayClient, false, d.wanPriority), nil
}
func (d *relayDialer) Priority(host string) int {
return d.wanPriority
}
type relayDialerFactory struct{}
func (relayDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, _ *registry.Registry) genericDialer {
func (relayDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, _ *registry.Registry, _ *lanChecker) genericDialer {
return &relayDialer{commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.RelayReconnectIntervalM) * time.Minute,
tlsCfg: tlsCfg,
wanPriority: opts.ConnectionPriorityRelay,
lanPriority: opts.ConnectionPriorityRelay,
}}
}
func (relayDialerFactory) Priority() int {
return relayPriority
}
func (relayDialerFactory) AlwaysWAN() bool {
return true
}

View File

@@ -106,7 +106,7 @@ func (t *relayListener) handleInvitations(ctx context.Context, clnt client.Relay
continue
}
t.conns <- newInternalConn(tc, connTypeRelayServer, relayPriority)
t.conns <- newInternalConn(tc, connTypeRelayServer, false, t.cfg.Options().ConnectionPriorityRelay)
// Poor mans notifier that informs the connection service that the
// relay URI has changed. This can only happen when we connect to a
@@ -177,7 +177,7 @@ func (*relayListener) NATType() string {
type relayListenerFactory struct{}
func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, _ *nat.Service, _ *registry.Registry) genericListener {
func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, _ *nat.Service, _ *registry.Registry, _ *lanChecker) genericListener {
t := &relayListener{
uri: uri,
cfg: cfg,

View File

@@ -162,6 +162,7 @@ type service struct {
evLogger events.Logger
registry *registry.Registry
keyGen *protocol.KeyGenerator
lanChecker *lanChecker
dialNow chan struct{}
dialNowDevices map[protocol.DeviceID]struct{}
@@ -192,6 +193,7 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
evLogger: evLogger,
registry: registry,
keyGen: keyGen,
lanChecker: &lanChecker{cfg},
dialNowDevicesMut: sync.NewMutex(),
dialNow: make(chan struct{}, 1),
@@ -405,9 +407,6 @@ func (s *service) handleHellos(ctx context.Context) error {
continue
}
// Determine only once whether a connection is considered local
// according to our configuration, then cache the decision.
c.isLocal = s.isLAN(c.RemoteAddr())
// Wrap the connection in rate limiters. The limiter itself will
// keep up with config changes to the rate and whether or not LAN
// connections are limited.
@@ -496,13 +495,14 @@ func (s *service) connect(ctx context.Context) error {
}
}
func (*service) bestDialerPriority(cfg config.Configuration) int {
func (s *service) bestDialerPriority(cfg config.Configuration) int {
bestDialerPriority := worstDialerPriority
for _, df := range dialers {
if df.Valid(cfg) != nil {
continue
}
if prio := df.Priority(); prio < bestDialerPriority {
prio := df.New(cfg.Options, s.tlsCfg, s.registry, s.lanChecker).Priority("127.0.0.1")
if prio < bestDialerPriority {
bestDialerPriority = prio
}
}
@@ -544,7 +544,14 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
priorityCutoff := worstDialerPriority
connection, connected := s.model.Connection(deviceCfg.DeviceID)
if connected {
// Set the priority cutoff to the current connection's priority,
// so that we don't attempt any dialers with worse priority.
priorityCutoff = connection.Priority()
// 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 {
// Our best dialer is not any better than what we already
// have, so nothing to do here.
@@ -564,7 +571,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
}
// Sort the queue in an order we think will be useful (most recent
// first, deprioriting unstable devices, randomizing those we haven't
// first, deprioritising unstable devices, randomizing those we haven't
// seen in a long while). If we don't do connection limiting the sorting
// doesn't have much effect, but it may result in getting up and running
// quicker if only a subset of configured devices are actually reachable
@@ -657,24 +664,15 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
continue
}
priority := dialerFactory.Priority()
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, dialerFactory.Priority(), priorityCutoff)
l.Debugf("Not dialing using %s as priority is not better than current connection (%d >= %d)", dialerFactory, priority, priorityCutoff)
continue
}
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg, s.registry)
nextDialAt.set(deviceID, addr, now.Add(dialer.RedialFrequency()))
// For LAN addresses, increase the priority so that we
// try these first.
switch {
case dialerFactory.AlwaysWAN():
// Do nothing.
case s.isLANHost(uri.Host):
priority--
}
dialTargets = append(dialTargets, dialTarget{
addr: addr,
dialer: dialer,
@@ -703,7 +701,11 @@ func (s *service) resolveDeviceAddrs(ctx context.Context, cfg config.DeviceConfi
return util.UniqueTrimmedStrings(addrs)
}
func (s *service) isLANHost(host string) bool {
type lanChecker struct {
cfg config.Wrapper
}
func (s *lanChecker) isLANHost(host string) bool {
// Probably we are called with an ip:port combo which we can resolve as
// a TCP address.
if addr, err := net.ResolveTCPAddr("tcp", host); err == nil {
@@ -717,7 +719,7 @@ func (s *service) isLANHost(host string) bool {
return false
}
func (s *service) isLAN(addr net.Addr) bool {
func (s *lanChecker) isLAN(addr net.Addr) bool {
var ip net.IP
switch addr := addr.(type) {
@@ -763,7 +765,7 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
l.Debugln("Starting listener", uri)
listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService, s.registry)
listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService, s.registry, s.lanChecker)
listener.OnAddressesChanged(s.logListenAddressesChangedEvent)
// Retrying a listener many times in rapid succession is unlikely to help,
@@ -1194,9 +1196,9 @@ func (r nextDialRegistry) get(device protocol.DeviceID, addr string) time.Time {
}
const (
dialCoolDownInterval = 2 * time.Minute
dialCoolDownDelay = 5 * time.Minute
dialCoolDownMaxAttemps = 3
dialCoolDownInterval = 2 * time.Minute
dialCoolDownDelay = 5 * time.Minute
dialCoolDownMaxAttempts = 3
)
// redialDevice marks the device for immediate redial, unless the remote keeps
@@ -1215,7 +1217,7 @@ func (r nextDialRegistry) redialDevice(device protocol.DeviceID, now time.Time)
return
}
if dev.attempts == 0 || now.Before(dev.coolDownIntervalStart.Add(dialCoolDownInterval)) {
if dev.attempts >= dialCoolDownMaxAttemps {
if dev.attempts >= dialCoolDownMaxAttempts {
// Device has been force redialed too often - let it cool down.
return
}
@@ -1226,7 +1228,7 @@ func (r nextDialRegistry) redialDevice(device protocol.DeviceID, now time.Time)
dev.nextDial = make(map[string]time.Time)
return
}
if dev.attempts >= dialCoolDownMaxAttemps && now.Before(dev.coolDownIntervalStart.Add(dialCoolDownDelay)) {
if dev.attempts >= dialCoolDownMaxAttempts && now.Before(dev.coolDownIntervalStart.Add(dialCoolDownDelay)) {
return // Still cooling down
}
delete(r, device)
@@ -1254,7 +1256,7 @@ func (r nextDialRegistry) sleepDurationAndCleanup(now time.Time) time.Duration {
}
if dev.attempts > 0 {
interval := dialCoolDownInterval
if dev.attempts >= dialCoolDownMaxAttemps {
if dev.attempts >= dialCoolDownMaxAttempts {
interval = dialCoolDownDelay
}
if now.After(dev.coolDownIntervalStart.Add(interval)) {

View File

@@ -87,10 +87,11 @@ func (t connType) Transport() string {
}
}
func newInternalConn(tc tlsConn, connType connType, priority int) internalConn {
func newInternalConn(tc tlsConn, connType connType, isLocal bool, priority int) internalConn {
return internalConn{
tlsConn: tc,
connType: connType,
isLocal: isLocal,
priority: priority,
establishedAt: time.Now().Truncate(time.Second),
}
@@ -138,12 +139,15 @@ func (c internalConn) EstablishedAt() time.Time {
}
func (c internalConn) String() string {
return fmt.Sprintf("%s-%s/%s/%s", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto())
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())
}
type dialerFactory interface {
New(config.OptionsConfiguration, *tls.Config, *registry.Registry) genericDialer
Priority() int
New(config.OptionsConfiguration, *tls.Config, *registry.Registry, *lanChecker) genericDialer
AlwaysWAN() bool
Valid(config.Configuration) error
String() string
@@ -153,19 +157,30 @@ type commonDialer struct {
trafficClass int
reconnectInterval time.Duration
tlsCfg *tls.Config
lanChecker *lanChecker
lanPriority int
wanPriority int
}
func (d *commonDialer) RedialFrequency() time.Duration {
return d.reconnectInterval
}
func (d *commonDialer) Priority(host string) int {
if d.lanChecker.isLANHost(host) {
return d.lanPriority
}
return d.wanPriority
}
type genericDialer interface {
Dial(context.Context, protocol.DeviceID, *url.URL) (internalConn, error)
RedialFrequency() time.Duration
Priority(host string) int
}
type listenerFactory interface {
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service, *registry.Registry) genericListener
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service, *registry.Registry, *lanChecker) genericListener
Valid(config.Configuration) error
}

View File

@@ -18,8 +18,6 @@ import (
"github.com/syncthing/syncthing/lib/protocol"
)
const tcpPriority = 10
func init() {
factory := &tcpDialerFactory{}
for _, scheme := range []string{"tcp", "tcp4", "tcp6"} {
@@ -59,26 +57,30 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
return internalConn{}, err
}
return newInternalConn(tc, connTypeTCPClient, tcpPriority), nil
priority := d.wanPriority
isLocal := d.lanChecker.isLAN(conn.RemoteAddr())
if isLocal {
priority = d.lanPriority
}
return newInternalConn(tc, connTypeTCPClient, isLocal, priority), nil
}
type tcpDialerFactory struct{}
func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry) genericDialer {
func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry, lanChecker *lanChecker) genericDialer {
return &tcpDialer{
commonDialer: commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
lanPriority: opts.ConnectionPriorityTCPLAN,
wanPriority: opts.ConnectionPriorityTCPWAN,
lanChecker: lanChecker,
},
registry: registry,
}
}
func (tcpDialerFactory) Priority() int {
return tcpPriority
}
func (tcpDialerFactory) AlwaysWAN() bool {
return false
}

View File

@@ -32,12 +32,13 @@ type tcpListener struct {
svcutil.ServiceWithError
onAddressesChangedNotifier
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
lanChecker *lanChecker
natService *nat.Service
mapping *nat.Mapping
@@ -148,7 +149,12 @@ func (t *tcpListener) serve(ctx context.Context) error {
continue
}
t.conns <- newInternalConn(tc, connTypeTCPServer, tcpPriority)
priority := t.cfg.Options().ConnectionPriorityTCPWAN
isLocal := t.lanChecker.isLAN(conn.RemoteAddr())
if isLocal {
priority = t.cfg.Options().ConnectionPriorityTCPLAN
}
t.conns <- newInternalConn(tc, connTypeTCPServer, isLocal, priority)
}
}
@@ -214,7 +220,7 @@ func (t *tcpListener) NATType() string {
type tcpListenerFactory struct{}
func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service, registry *registry.Registry) genericListener {
func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service, registry *registry.Registry, lanChecker *lanChecker) genericListener {
l := &tcpListener{
uri: fixupPort(uri, config.DefaultTCPPort),
cfg: cfg,
@@ -223,6 +229,7 @@ func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.C
natService: natService,
factory: f,
registry: registry,
lanChecker: lanChecker,
}
l.ServiceWithError = svcutil.AsService(l.serve, l.String())
return l

View File

@@ -51,8 +51,10 @@ type BasicFilesystem struct {
groupCache *groupCache
}
type userCache = valueCache[string, *user.User]
type groupCache = valueCache[string, *user.Group]
type (
userCache = valueCache[string, *user.User]
groupCache = valueCache[string, *user.Group]
)
func newBasicFilesystem(root string, opts ...Option) *BasicFilesystem {
if root == "" {
@@ -62,11 +64,11 @@ func newBasicFilesystem(root string, opts ...Option) *BasicFilesystem {
// The reason it's done like this:
// C: -> C:\ -> C:\ (issue that this is trying to fix)
// C:\somedir -> C:\somedir\ -> C:\somedir
// C:\somedir\ -> C:\somedir\\ -> C:\somedir
// C:\somedir\ -> C:\somedir\ -> C:\somedir
// This way in the tests, we get away without OS specific separators
// in the test configs.
sep := string(filepath.Separator)
root = filepath.Dir(root + sep)
root = filepath.Clean(filepath.Dir(root + sep))
// Attempt tilde expansion; leave unchanged in case of error
if path, err := ExpandTilde(root); err == nil {
@@ -215,7 +217,7 @@ func (f *BasicFilesystem) DirNames(name string) ([]string, error) {
if err != nil {
return nil, err
}
fd, err := os.OpenFile(name, OptReadOnly, 0777)
fd, err := os.OpenFile(name, OptReadOnly, 0o777)
if err != nil {
return nil, err
}

View File

@@ -12,29 +12,46 @@ package fs
import (
"os"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
)
func TestWindowsPaths(t *testing.T) {
testCases := []struct {
type testCase struct {
input string
expectedRoot string
expectedURI string
}{
}
testCases := []testCase{
{`e:`, `\\?\e:\`, `e:\`},
{`e:\`, `\\?\e:\`, `e:\`},
{`e:\\`, `\\?\e:\`, `e:\`},
{`\\?\e:`, `\\?\e:\`, `e:\`},
{`\\?\e:\`, `\\?\e:\`, `e:\`},
{`\\?\e:\\`, `\\?\e:\`, `e:\`},
{`e:\x`, `\\?\e:\x`, `e:\x`},
{`e:\x\`, `\\?\e:\x`, `e:\x`},
{`e:\x\\`, `\\?\e:\x`, `e:\x`},
{`\\192.0.2.22\network\share`, `\\192.0.2.22\network\share`, `\\192.0.2.22\network\share`},
}
for _, testCase := range testCases {
if runtime.Version() >= "go1.20" {
testCases = append(testCases,
testCase{`\\.\e:`, `\\.\e:\`, `e:\`},
testCase{`\\.\e:\`, `\\.\e:\`, `e:\`},
testCase{`\\.\e:\\`, `\\.\e:\`, `e:\`},
)
}
for i, testCase := range testCases {
fs := newBasicFilesystem(testCase.input)
if fs.root != testCase.expectedRoot {
t.Errorf("root %q != %q", fs.root, testCase.expectedRoot)
t.Errorf("test %d: root: expected `%s`, got `%s`", i, testCase.expectedRoot, fs.root)
}
if fs.URI() != testCase.expectedURI {
t.Errorf("uri %q != %q", fs.URI(), testCase.expectedURI)
t.Errorf("test %d: uri: expected `%s`, got `%s`", i, testCase.expectedURI, fs.URI())
}
}
@@ -195,7 +212,7 @@ func TestGetFinalPath(t *testing.T) {
}
func TestRemoveWindowsDirIcon(t *testing.T) {
//Try to delete a folder with a custom icon with os.Remove (simulated by the readonly file attribute)
// Try to delete a folder with a custom icon with os.Remove (simulated by the readonly file attribute)
fs, dir := setup(t)
relativePath := "folder_with_icon"

View File

@@ -1,33 +0,0 @@
// Copyright (C) 2017 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/.
//go:build !windows
// +build !windows
package fs
import (
"os"
"path/filepath"
)
// DebugSymlinkForTestsOnly is not and should not be used in Syncthing code,
// hence the cumbersome name to make it obvious if this ever leaks. Its
// reason for existence is the Windows version, which allows creating
// symlinks when non-elevated.
func DebugSymlinkForTestsOnly(oldFs, newFs Filesystem, oldname, newname string) error {
if fs, ok := unwrapFilesystem(newFs, filesystemWrapperTypeCase); ok {
caseFs := fs.(*caseFilesystem)
if err := caseFs.checkCase(newname); err != nil {
return err
}
caseFs.dropCache()
}
if err := os.Symlink(filepath.Join(oldFs.URI(), oldname), filepath.Join(newFs.URI(), newname)); err != nil {
return err
}
return nil
}

View File

@@ -1,140 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"os"
"path/filepath"
"syscall"
)
// DebugSymlinkForTestsOnly is os.Symlink taken from the 1.9.2 stdlib,
// hacked with the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag to
// create symlinks when not elevated.
//
// This is not and should not be used in Syncthing code, hence the
// cumbersome name to make it obvious if this ever leaks. Nonetheless it's
// useful in tests.
func DebugSymlinkForTestsOnly(oldFs, newFS Filesystem, oldname, newname string) error {
oldname = filepath.Join(oldFs.URI(), oldname)
newname = filepath.Join(newFS.URI(), newname)
// CreateSymbolicLink is not supported before Windows Vista
if syscall.LoadCreateSymbolicLink() != nil {
return &os.LinkError{"symlink", oldname, newname, syscall.EWINDOWS}
}
// '/' does not work in link's content
oldname = filepath.FromSlash(oldname)
// need the exact location of the oldname when it's relative to determine if it's a directory
destpath := oldname
if !filepath.IsAbs(oldname) {
destpath = filepath.Dir(newname) + `\` + oldname
}
fi, err := os.Lstat(destpath)
isdir := err == nil && fi.IsDir()
n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
if err != nil {
return &os.LinkError{"symlink", oldname, newname, err}
}
o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
if err != nil {
return &os.LinkError{"symlink", oldname, newname, err}
}
var flags uint32
if isdir {
flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
}
flags |= 0x02 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
err = syscall.CreateSymbolicLink(n, o, flags)
if err != nil {
return &os.LinkError{"symlink", oldname, newname, err}
}
return nil
}
// fixLongPath returns the extended-length (\\?\-prefixed) form of
// path when needed, in order to avoid the default 260 character file
// path limit imposed by Windows. If path is not easily converted to
// the extended-length form (for example, if path is a relative path
// or contains .. elements), or is short enough, fixLongPath returns
// path unmodified.
//
// See https://docs.microsoft.com/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
func fixLongPath(path string) string {
// Do nothing (and don't allocate) if the path is "short".
// Empirically (at least on the Windows Server 2013 builder),
// the kernel is arbitrarily okay with < 248 bytes. That
// matches what the docs above say:
// "When using an API to create a directory, the specified
// path cannot be so long that you cannot append an 8.3 file
// name (that is, the directory name cannot exceed MAX_PATH
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
//
// The MS docs appear to say that a normal path that is 248 bytes long
// will work; empirically the path must be less than 248 bytes long.
if len(path) < 248 {
// Don't fix. (This is how Go 1.7 and earlier worked,
// not automatically generating the \\?\ form)
return path
}
// The extended form begins with \\?\, as in
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
// The extended form disables evaluation of . and .. path
// elements and disables the interpretation of / as equivalent
// to \. The conversion here rewrites / to \ and elides
// . elements as well as trailing or duplicate separators. For
// simplicity it avoids the conversion entirely for relative
// paths or paths containing .. elements. For now,
// \\server\share paths are not converted to
// \\?\UNC\server\share paths because the rules for doing so
// are less well-specified.
if len(path) >= 2 && path[:2] == `\\` {
// Don't canonicalize UNC paths.
return path
}
if !filepath.IsAbs(path) {
// Relative path
return path
}
const prefix = `\\?`
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
copy(pathbuf, prefix)
n := len(path)
r, w := 0, len(prefix)
for r < n {
switch {
case os.IsPathSeparator(path[r]):
// empty block
r++
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
// /./
r++
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
// /../ is currently unhandled
return path
default:
pathbuf[w] = '\\'
w++
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
pathbuf[w] = path[r]
w++
}
}
}
// A drive's root directory needs a trailing \
if w == len(`\\?\c:`) {
pathbuf[w] = '\\'
w++
}
return string(pathbuf[:w])
}

View File

@@ -52,6 +52,8 @@ const randomBlockShift = 14 // 128k
// seed=n to set the initial random seed (default 0)
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
// content=true to save actual file contents instead of generating pseudorandomly; n.b. memory usage
// nostfolder=true skip the creation of .stfolder
//
// - Two fakeFS:s pointing at the same root path see the same files.
type fakeFS struct {
@@ -108,7 +110,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
root: &fakeEntry{
name: "/",
entryType: fakeEntryTypeDir,
mode: 0700,
mode: 0o700,
mtime: time.Now(),
children: make(map[string]*fakeEntry),
},
@@ -123,6 +125,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
fs.insens = params.Get("insens") == "true"
fs.withContent = params.Get("content") == "true"
nostfolder := params.Get("nostfolder") == "true"
if sizeavg == 0 {
sizeavg = 1 << 20
@@ -139,7 +142,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) {
dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255)))
file := fmt.Sprintf("%016x", rng.Int63())
fs.MkdirAll(dir, 0755)
fs.MkdirAll(dir, 0o755)
fd, _ := fs.Create(filepath.Join(dir, file))
createdFiles++
@@ -153,8 +156,10 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
}
}
// Also create a default folder marker for good measure
fs.Mkdir(".stfolder", 0700)
if !nostfolder {
// Also create a default folder marker for good measure
fs.Mkdir(".stfolder", 0o700)
}
// We only set the latency after doing the operations required to create
// the filesystem initially.
@@ -187,7 +192,6 @@ type fakeEntry struct {
}
func (fs *fakeFS) entryForName(name string) *fakeEntry {
// bug: lookup doesn't work through symlinks.
if fs.insens {
name = UnicodeLowercaseNormalized(name)
}
@@ -200,7 +204,7 @@ func (fs *fakeFS) entryForName(name string) *fakeEntry {
name = strings.Trim(name, "/")
comps := strings.Split(name, "/")
entry := fs.root
for _, comp := range comps {
for i, comp := range comps {
if entry.entryType != fakeEntryTypeDir {
return nil
}
@@ -209,6 +213,12 @@ func (fs *fakeFS) entryForName(name string) *fakeEntry {
if !ok {
return nil
}
if i < len(comps)-1 && entry.entryType == fakeEntryTypeSymlink {
// only absolute link targets are supported, and we assume
// lookup is Lstat-kind so we only resolve symlinks when they
// are not the last path component.
return fs.entryForName(entry.dest)
}
}
return entry
}
@@ -267,7 +277,7 @@ func (fs *fakeFS) create(name string) (*fakeEntry, error) {
}
entry.size = 0
entry.mtime = time.Now()
entry.mode = 0666
entry.mode = 0o666
entry.content = nil
if fs.withContent {
entry.content = make([]byte, 0)
@@ -283,7 +293,7 @@ func (fs *fakeFS) create(name string) (*fakeEntry, error) {
}
new := &fakeEntry{
name: base,
mode: 0666,
mode: 0o666,
mtime: time.Now(),
}
@@ -305,9 +315,9 @@ func (fs *fakeFS) Create(name string) (File, error) {
return nil, err
}
if fs.insens {
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name), mut: &fs.mut}, nil
}
return &fakeFile{fakeEntry: entry}, nil
return &fakeFile{fakeEntry: entry, mut: &fs.mut}, nil
}
func (fs *fakeFS) CreateSymlink(target, name string) error {
@@ -441,9 +451,9 @@ func (fs *fakeFS) Open(name string) (File, error) {
}
if fs.insens {
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name), mut: &fs.mut}, nil
}
return &fakeFile{fakeEntry: entry}, nil
return &fakeFile{fakeEntry: entry, mut: &fs.mut}, nil
}
func (fs *fakeFS) OpenFile(name string, flags int, mode FileMode) (File, error) {
@@ -486,7 +496,7 @@ func (fs *fakeFS) OpenFile(name string, flags int, mode FileMode) (File, error)
}
entry.children[key] = newEntry
return &fakeFile{fakeEntry: newEntry}, nil
return &fakeFile{fakeEntry: newEntry, mut: &fs.mut}, nil
}
func (fs *fakeFS) ReadSymlink(name string) (string, error) {
@@ -630,9 +640,31 @@ func (*fakeFS) SetXattr(_ string, _ []protocol.Xattr, _ XattrFilter) error {
return nil
}
func (*fakeFS) Glob(_ string) ([]string, error) {
// gnnh we don't seem to actually require this in practice
return nil, errors.New("not implemented")
// A basic glob-impelementation that should be able to handle
// simple test cases.
func (fs *fakeFS) Glob(pattern string) ([]string, error) {
dir := filepath.Dir(pattern)
file := filepath.Base(pattern)
if _, err := fs.Lstat(dir); err != nil {
return nil, errPathInvalid
}
var matches []string
names, err := fs.DirNames(dir)
if err != nil {
return nil, err
}
for _, n := range names {
matched, err := filepath.Match(file, n)
if err != nil {
return nil, err
}
if matched {
matches = append(matches, filepath.Join(dir, n))
}
}
return matches, err
}
func (*fakeFS) Roots() ([]string, error) {
@@ -703,7 +735,7 @@ func (fs *fakeFS) reportMetricsPer(b *testing.B, divisor float64, unit string) {
// opened for reading or writing, it's all good.
type fakeFile struct {
*fakeEntry
mut sync.Mutex
mut *sync.Mutex
rng io.Reader
seed int64
offset int64

View File

@@ -172,21 +172,23 @@ var (
// Equivalents from os package.
const ModePerm = FileMode(os.ModePerm)
const ModeSetgid = FileMode(os.ModeSetgid)
const ModeSetuid = FileMode(os.ModeSetuid)
const ModeSticky = FileMode(os.ModeSticky)
const ModeSymlink = FileMode(os.ModeSymlink)
const ModeType = FileMode(os.ModeType)
const PathSeparator = os.PathSeparator
const OptAppend = os.O_APPEND
const OptCreate = os.O_CREATE
const OptExclusive = os.O_EXCL
const OptReadOnly = os.O_RDONLY
const OptReadWrite = os.O_RDWR
const OptSync = os.O_SYNC
const OptTruncate = os.O_TRUNC
const OptWriteOnly = os.O_WRONLY
const (
ModePerm = FileMode(os.ModePerm)
ModeSetgid = FileMode(os.ModeSetgid)
ModeSetuid = FileMode(os.ModeSetuid)
ModeSticky = FileMode(os.ModeSticky)
ModeSymlink = FileMode(os.ModeSymlink)
ModeType = FileMode(os.ModeType)
PathSeparator = os.PathSeparator
OptAppend = os.O_APPEND
OptCreate = os.O_CREATE
OptExclusive = os.O_EXCL
OptReadOnly = os.O_RDONLY
OptReadWrite = os.O_RDWR
OptSync = os.O_SYNC
OptTruncate = os.O_TRUNC
OptWriteOnly = os.O_WRONLY
)
// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
@@ -354,3 +356,20 @@ func unwrapFilesystem(fs Filesystem, wrapperType filesystemWrapperType) (Filesys
}
}
}
// WriteFile writes data to the named file, creating it if necessary.
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
// otherwise WriteFile truncates it before writing, without changing permissions.
// Since Writefile requires multiple system calls to complete, a failure mid-operation
// can leave the file in a partially written state.
func WriteFile(fs Filesystem, name string, data []byte, perm FileMode) error {
f, err := fs.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
_, err = f.Write(data)
if err1 := f.Close(); err1 != nil && err == nil {
err = err1
}
return err
}

View File

@@ -39,7 +39,7 @@ func TestIsInternal(t *testing.T) {
for _, tc := range cases {
res := IsInternal(filepath.FromSlash(tc.file))
if res != tc.internal {
t.Errorf("Unexpected result: IsInteral(%q): %v should be %v", tc.file, res, tc.internal)
t.Errorf("Unexpected result: IsInternal(%q): %v should be %v", tc.file, res, tc.internal)
}
}
}

View File

@@ -17,17 +17,16 @@ import (
)
func TestMtimeFS(t *testing.T) {
os.RemoveAll("testdata")
defer os.RemoveAll("testdata")
os.Mkdir("testdata", 0755)
os.WriteFile("testdata/exists0", []byte("hello"), 0644)
os.WriteFile("testdata/exists1", []byte("hello"), 0644)
os.WriteFile("testdata/exists2", []byte("hello"), 0644)
td := t.TempDir()
os.Mkdir(filepath.Join(td, "testdata"), 0o755)
os.WriteFile(filepath.Join(td, "testdata", "exists0"), []byte("hello"), 0o644)
os.WriteFile(filepath.Join(td, "testdata", "exists1"), []byte("hello"), 0o644)
os.WriteFile(filepath.Join(td, "testdata", "exists2"), []byte("hello"), 0o644)
// a random time with nanosecond precision
testTime := time.Unix(1234567890, 123456789)
mtimefs := newMtimeFS(".", make(mapStore))
mtimefs := newMtimeFS(td, make(mapStore))
// Do one Chtimes call that will go through to the normal filesystem
mtimefs.chtimes = os.Chtimes
@@ -62,7 +61,7 @@ func TestMtimeFS(t *testing.T) {
// when looking directly on disk though.
for _, file := range []string{"testdata/exists1", "testdata/exists2"} {
if info, err := os.Lstat(file); err != nil {
if info, err := os.Lstat(filepath.Join(td, file)); err != nil {
t.Error("Lstat shouldn't fail:", err)
} else if info.ModTime().Equal(testTime) {
t.Errorf("Unexpected time match; %v == %v", info.ModTime(), testTime)
@@ -74,7 +73,7 @@ func TestMtimeFS(t *testing.T) {
// filesystems.
testTime = time.Now().Add(5 * time.Hour).Truncate(time.Minute)
os.Chtimes("testdata/exists0", testTime, testTime)
os.Chtimes(filepath.Join(td, "testdata/exists0"), testTime, testTime)
if info, err := mtimefs.Lstat("testdata/exists0"); err != nil {
t.Error("Lstat shouldn't fail:", err)
} else if !info.ModTime().Equal(testTime) {
@@ -89,7 +88,7 @@ func TestMtimeFSWalk(t *testing.T) {
underlying := mtimefs.Filesystem
mtimefs.chtimes = failChtimes
if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil {
if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0o644); err != nil {
t.Fatal(err)
}
@@ -139,7 +138,7 @@ func TestMtimeFSOpen(t *testing.T) {
underlying := mtimefs.Filesystem
mtimefs.chtimes = failChtimes
if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil {
if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0o644); err != nil {
t.Fatal(err)
}
@@ -190,10 +189,10 @@ func TestMtimeFSInsensitive(t *testing.T) {
}
theTest := func(t *testing.T, fs *mtimeFS, shouldSucceed bool) {
os.RemoveAll("testdata")
defer os.RemoveAll("testdata")
os.Mkdir("testdata", 0755)
os.WriteFile("testdata/FiLe", []byte("hello"), 0644)
fs.RemoveAll("testdata")
defer fs.RemoveAll("testdata")
fs.Mkdir("testdata", 0o755)
WriteFile(fs, "testdata/FiLe", []byte("hello"), 0o644)
// a random time with nanosecond precision
testTime := time.Unix(1234567890, 123456789)
@@ -216,12 +215,12 @@ func TestMtimeFSInsensitive(t *testing.T) {
// The test should fail with a case sensitive mtimefs
t.Run("with case sensitive mtimefs", func(t *testing.T) {
theTest(t, newMtimeFS(".", make(mapStore)), false)
theTest(t, newMtimeFS(t.TempDir(), make(mapStore)), false)
})
// And succeed with a case insensitive one.
t.Run("with case insensitive mtimefs", func(t *testing.T) {
theTest(t, newMtimeFS(".", make(mapStore), WithCaseInsensitivity(true)), true)
theTest(t, newMtimeFS(t.TempDir(), make(mapStore), WithCaseInsensitivity(true)), true)
})
}

View File

@@ -406,11 +406,19 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
}
if cd.Seen(filesystem, file) {
return nil, parseError(fmt.Errorf("multiple include of ignore file %q", file))
return nil, errors.New("multiple include")
}
fd, info, err := loadIgnoreFile(filesystem, file)
if err != nil {
// isNotExist is considered "ok" in a sense of that a folder doesn't have to act
// upon it. This is because it is allowed for .stignore to not exist. However,
// included ignore files are not allowed to be missing and these errors should be
// acted upon on. So we don't perserve the error chain here and manually set an
// error instead, if the file is missing.
if fs.IsNotExist(err) {
err = errors.New("file not found")
}
return nil, err
}
defer fd.Close()

View File

@@ -10,7 +10,6 @@ import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
@@ -19,16 +18,43 @@ import (
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/rand"
)
var testFiles = map[string]string{
".stignore": `#include excludes
bfile
dir1/cfile
**/efile
/ffile
lost+found
`,
"excludes": "dir2/dfile\n#include further-excludes\n",
"further-excludes": "dir3\n",
}
func newTestFS() fs.Filesystem {
testFS := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true&nostfolder=true")
// Add some data expected by the tests, previously existing on disk.
testFS.Mkdir("dir3", 0o777)
for name, content := range testFiles {
fs.WriteFile(testFS, name, []byte(content), 0o666)
}
return testFS
}
func TestIgnore(t *testing.T) {
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"), WithCache(true))
testFs := newTestFS()
pats := New(testFs, WithCache(true))
err := pats.Load(".stignore")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
tests := []struct {
f string
r bool
}{
@@ -65,6 +91,8 @@ func TestIgnore(t *testing.T) {
}
func TestExcludes(t *testing.T) {
testFs := newTestFS()
stignore := `
!iex2
!ign1/ex
@@ -72,13 +100,13 @@ func TestExcludes(t *testing.T) {
i*2
!ign2
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
tests := []struct {
f string
r bool
}{
@@ -103,6 +131,8 @@ func TestExcludes(t *testing.T) {
}
func TestFlagOrder(t *testing.T) {
testFs := newTestFS()
stignore := `
## Ok cases
(?i)(?d)!ign1
@@ -117,7 +147,7 @@ func TestFlagOrder(t *testing.T) {
(?i)(?d)(?d)!ign9
(?d)(?d)!ign10
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -142,6 +172,8 @@ func TestFlagOrder(t *testing.T) {
}
func TestDeletables(t *testing.T) {
testFs := newTestFS()
stignore := `
(?d)ign1
(?d)(?i)ign2
@@ -152,13 +184,13 @@ func TestDeletables(t *testing.T) {
ign7
(?i)ign8
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
tests := []struct {
f string
i bool
d bool
@@ -181,7 +213,10 @@ func TestDeletables(t *testing.T) {
}
func TestBadPatterns(t *testing.T) {
var badPatterns = []string{
testFs := newTestFS()
t.Skip("to fix: bad pattern not happening")
badPatterns := []string{
"[",
"/[",
"**/[",
@@ -190,18 +225,25 @@ func TestBadPatterns(t *testing.T) {
}
for _, pat := range badPatterns {
err := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true)).Parse(bytes.NewBufferString(pat), ".stignore")
err := New(testFs, WithCache(true)).Parse(bytes.NewBufferString(pat), ".stignore")
if err == nil {
t.Errorf("No error for pattern %q", pat)
}
if !IsParseError(err) {
t.Error("Should have been a parse error:", err)
}
if strings.HasPrefix(pat, "#include") {
if fs.IsNotExist(err) {
t.Error("Includes should not toss a regular isNotExist error")
}
}
}
}
func TestCaseSensitivity(t *testing.T) {
ign := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
testFs := newTestFS()
ign := New(testFs, WithCache(true))
err := ign.Parse(bytes.NewBufferString("test"), ".stignore")
if err != nil {
t.Error(err)
@@ -230,9 +272,7 @@ func TestCaseSensitivity(t *testing.T) {
}
func TestCaching(t *testing.T) {
dir := t.TempDir()
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
fd1, err := osutil.TempFile(fs, "", "")
if err != nil {
@@ -352,6 +392,8 @@ func TestCaching(t *testing.T) {
}
func TestCommentsAndBlankLines(t *testing.T) {
testFs := newTestFS()
stignore := `
// foo
//bar
@@ -363,7 +405,7 @@ func TestCommentsAndBlankLines(t *testing.T) {
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Error(err)
@@ -376,6 +418,8 @@ func TestCommentsAndBlankLines(t *testing.T) {
var result Result
func BenchmarkMatch(b *testing.B) {
testFs := newTestFS()
stignore := `
.frog
.frog*
@@ -391,7 +435,7 @@ flamingo
*.crow
*.crow
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."))
pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
b.Error(err)
@@ -420,9 +464,8 @@ flamingo
*.crow
`
// Caches per file, hence write the patterns to a file.
dir := b.TempDir()
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
fd, err := osutil.TempFile(fs, "", "")
if err != nil {
@@ -458,9 +501,7 @@ flamingo
}
func TestCacheReload(t *testing.T) {
dir := t.TempDir()
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
fd, err := osutil.TempFile(fs, "", "")
if err != nil {
@@ -532,13 +573,15 @@ func TestCacheReload(t *testing.T) {
}
func TestHash(t *testing.T) {
p1 := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
err := p1.Load("testdata/.stignore")
testFs := newTestFS()
p1 := New(testFs, WithCache(true))
err := p1.Load(".stignore")
if err != nil {
t.Fatal(err)
}
// Same list of patterns as testdata/.stignore, after expansion
// Same list of patterns as .stignore, after expansion
stignore := `
dir2/dfile
dir3
@@ -548,7 +591,7 @@ func TestHash(t *testing.T) {
/ffile
lost+found
`
p2 := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
p2 := New(testFs, WithCache(true))
err = p2.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -563,7 +606,7 @@ func TestHash(t *testing.T) {
/ffile
lost+found
`
p3 := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
p3 := New(testFs, WithCache(true))
err = p3.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -587,8 +630,11 @@ func TestHash(t *testing.T) {
}
func TestHashOfEmpty(t *testing.T) {
p1 := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
err := p1.Load("testdata/.stignore")
testFs := newTestFS()
p1 := New(testFs, WithCache(true))
err := p1.Load(".stignore")
if err != nil {
t.Fatal(err)
}
@@ -615,6 +661,8 @@ func TestHashOfEmpty(t *testing.T) {
}
func TestWindowsPatterns(t *testing.T) {
testFs := newTestFS()
// We should accept patterns as both a/b and a\b and match that against
// both kinds of slash as well.
if !build.IsWindows {
@@ -626,7 +674,8 @@ func TestWindowsPatterns(t *testing.T) {
a/b
c\d
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -641,6 +690,8 @@ func TestWindowsPatterns(t *testing.T) {
}
func TestAutomaticCaseInsensitivity(t *testing.T) {
testFs := newTestFS()
// We should do case insensitive matching by default on some platforms.
if !build.IsWindows && !build.IsDarwin {
t.Skip("Windows/Mac specific test")
@@ -651,7 +702,8 @@ func TestAutomaticCaseInsensitivity(t *testing.T) {
A/B
c/d
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -666,11 +718,14 @@ func TestAutomaticCaseInsensitivity(t *testing.T) {
}
func TestCommas(t *testing.T) {
testFs := newTestFS()
stignore := `
foo,bar.txt
{baz,quux}.txt
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -696,12 +751,15 @@ func TestCommas(t *testing.T) {
}
func TestIssue3164(t *testing.T) {
testFs := newTestFS()
stignore := `
(?d)(?i)*.part
(?d)(?i)/foo
(?d)(?i)**/bar
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -734,10 +792,13 @@ func TestIssue3164(t *testing.T) {
}
func TestIssue3174(t *testing.T) {
testFs := newTestFS()
stignore := `
*ä*
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -749,10 +810,13 @@ func TestIssue3174(t *testing.T) {
}
func TestIssue3639(t *testing.T) {
testFs := newTestFS()
stignore := `
foo/
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -768,6 +832,8 @@ func TestIssue3639(t *testing.T) {
}
func TestIssue3674(t *testing.T) {
testFs := newTestFS()
stignore := `
a*b
a**c
@@ -785,7 +851,8 @@ func TestIssue3674(t *testing.T) {
{"as/dc", true},
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -800,6 +867,8 @@ func TestIssue3674(t *testing.T) {
}
func TestGobwasGlobIssue18(t *testing.T) {
testFs := newTestFS()
stignore := `
a?b
bb?
@@ -817,7 +886,8 @@ func TestGobwasGlobIssue18(t *testing.T) {
{"bbaa", false},
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -832,6 +902,8 @@ func TestGobwasGlobIssue18(t *testing.T) {
}
func TestRoot(t *testing.T) {
testFs := newTestFS()
stignore := `
!/a
/*
@@ -846,7 +918,8 @@ func TestRoot(t *testing.T) {
{"b", true},
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -861,15 +934,18 @@ func TestRoot(t *testing.T) {
}
func TestLines(t *testing.T) {
testFs := newTestFS()
stignore := `
#include testdata/excludes
#include excludes
!/a
/*
!/a
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -877,7 +953,7 @@ func TestLines(t *testing.T) {
expectedLines := []string{
"",
"#include testdata/excludes",
"#include excludes",
"",
"!/a",
"/*",
@@ -897,6 +973,8 @@ func TestLines(t *testing.T) {
}
func TestDuplicateLines(t *testing.T) {
testFs := newTestFS()
stignore := `
!/a
/*
@@ -907,7 +985,7 @@ func TestDuplicateLines(t *testing.T) {
/*
`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
@@ -926,6 +1004,8 @@ func TestDuplicateLines(t *testing.T) {
}
func TestIssue4680(t *testing.T) {
testFs := newTestFS()
stignore := `
#snapshot
`
@@ -938,7 +1018,8 @@ func TestIssue4680(t *testing.T) {
{"#snapshot/foo", true},
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -953,9 +1034,12 @@ func TestIssue4680(t *testing.T) {
}
func TestIssue4689(t *testing.T) {
testFs := newTestFS()
stignore := `// orig`
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
@@ -978,18 +1062,23 @@ func TestIssue4689(t *testing.T) {
}
func TestIssue4901(t *testing.T) {
dir := t.TempDir()
testFs := newTestFS()
stignore := `
#include unicorn-lazor-death
puppy
`
if err := os.WriteFile(filepath.Join(dir, ".stignore"), []byte(stignore), 0777); err != nil {
pats := New(testFs, WithCache(true))
fd, err := pats.fs.Create(".stignore")
if err != nil {
t.Fatalf(err.Error())
}
if _, err := fd.Write([]byte(stignore)); err != nil {
t.Fatal(err)
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, dir), WithCache(true))
// Cache does not suddenly make the load succeed.
for i := 0; i < 2; i++ {
err := pats.Load(".stignore")
@@ -1004,11 +1093,15 @@ func TestIssue4901(t *testing.T) {
}
}
if err := os.WriteFile(filepath.Join(dir, "unicorn-lazor-death"), []byte(" "), 0777); err != nil {
fd, err = pats.fs.Create("unicorn-lazor-death")
if err != nil {
t.Fatalf(err.Error())
}
if _, err := fd.Write([]byte(" ")); err != nil {
t.Fatal(err)
}
err := pats.Load(".stignore")
err = pats.Load(".stignore")
if err != nil {
t.Fatalf("unexpected error: %s", err.Error())
}
@@ -1017,7 +1110,9 @@ func TestIssue4901(t *testing.T) {
// TestIssue5009 checks that ignored dirs are only skipped if there are no include patterns.
// https://github.com/syncthing/syncthing/issues/5009 (rc-only bug)
func TestIssue5009(t *testing.T) {
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
testFs := newTestFS()
pats := New(testFs, WithCache(true))
stignore := `
ign1
@@ -1048,7 +1143,9 @@ func TestIssue5009(t *testing.T) {
}
func TestSpecialChars(t *testing.T) {
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
testFs := newTestFS()
pats := New(testFs, WithCache(true))
stignore := `(?i)/#recycle
(?i)/#nosync
@@ -1073,7 +1170,9 @@ func TestSpecialChars(t *testing.T) {
}
func TestIntlWildcards(t *testing.T) {
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
testFs := newTestFS()
pats := New(testFs, WithCache(true))
stignore := `1000春
200?春
@@ -1098,9 +1197,12 @@ func TestIntlWildcards(t *testing.T) {
}
func TestPartialIncludeLine(t *testing.T) {
testFs := newTestFS()
// Loading a partial #include line (no file mentioned) should error but not crash.
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
pats := New(testFs, WithCache(true))
cases := []string{
"#include",
"#include\n",
@@ -1121,6 +1223,8 @@ func TestPartialIncludeLine(t *testing.T) {
}
func TestSkipIgnoredDirs(t *testing.T) {
testFs := newTestFS()
tcs := []struct {
pattern string
expected bool
@@ -1156,7 +1260,7 @@ func TestSkipIgnoredDirs(t *testing.T) {
}
}
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"), WithCache(true))
pats := New(testFs, WithCache(true))
stignore := `
/foo/ign*
@@ -1184,6 +1288,8 @@ func TestSkipIgnoredDirs(t *testing.T) {
}
func TestEmptyPatterns(t *testing.T) {
testFs := newTestFS()
// These patterns are all invalid and should be rejected as such (without panicking...)
tcs := []string{
"!",
@@ -1192,7 +1298,7 @@ func TestEmptyPatterns(t *testing.T) {
}
for _, tc := range tcs {
m := New(fs.NewFilesystem(fs.FilesystemTypeFake, ""))
m := New(testFs)
err := m.Parse(strings.NewReader(tc), ".stignore")
if err == nil {
t.Error("Should reject invalid pattern", tc)
@@ -1204,24 +1310,22 @@ func TestEmptyPatterns(t *testing.T) {
}
func TestWindowsLineEndings(t *testing.T) {
testFs := newTestFS()
if !build.IsWindows {
t.Skip("Windows specific")
}
lines := "foo\nbar\nbaz\n"
dir := t.TempDir()
ffs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
m := New(ffs)
m := New(testFs)
if err := m.Parse(strings.NewReader(lines), ".stignore"); err != nil {
t.Fatal(err)
}
if err := WriteIgnores(ffs, ".stignore", m.Lines()); err != nil {
if err := WriteIgnores(testFs, ".stignore", m.Lines()); err != nil {
t.Fatal(err)
}
fd, err := ffs.Open(".stignore")
fd, err := testFs.Open(".stignore")
if err != nil {
t.Fatal(err)
}

View File

@@ -1,7 +0,0 @@
#include excludes
bfile
dir1/cfile
**/efile
/ffile
lost+found

View File

@@ -1 +0,0 @@
baz

View File

@@ -1 +0,0 @@
quux

View File

@@ -1,2 +0,0 @@
dir2/dfile
#include further-excludes

View File

@@ -1 +0,0 @@
dir3

View File

@@ -329,10 +329,12 @@ func (f *folder) getHealthErrorWithoutIgnores() error {
return err
}
dbPath := locations.Get(locations.Database)
if usage, err := fs.NewFilesystem(fs.FilesystemTypeBasic, dbPath).Usage("."); err == nil {
if err = config.CheckFreeSpace(f.model.cfg.Options().MinHomeDiskFree, usage); err != nil {
return fmt.Errorf("insufficient space on disk for database (%v): %w", dbPath, err)
if minFree := f.model.cfg.Options().MinHomeDiskFree; minFree.Value > 0 {
dbPath := locations.Get(locations.Database)
if usage, err := fs.NewFilesystem(fs.FilesystemTypeBasic, dbPath).Usage("."); err == nil {
if err = config.CheckFreeSpace(minFree, usage); err != nil {
return fmt.Errorf("insufficient space on disk for database (%v): %w", dbPath, err)
}
}
}

View File

@@ -35,11 +35,11 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
// Create some test data
for _, dir := range []string{".stfolder", "ignDir", "unknownDir"} {
must(t, ffs.MkdirAll(dir, 0755))
must(t, ffs.MkdirAll(dir, 0o755))
}
writeFilePerm(t, ffs, "ignDir/ignFile", []byte("hello\n"), 0644)
writeFilePerm(t, ffs, "unknownDir/unknownFile", []byte("hello\n"), 0644)
writeFilePerm(t, ffs, ".stignore", []byte("ignDir\n"), 0644)
writeFilePerm(t, ffs, "ignDir/ignFile", []byte("hello\n"), 0o644)
writeFilePerm(t, ffs, "unknownDir/unknownFile", []byte("hello\n"), 0o644)
writeFilePerm(t, ffs, ".stignore", []byte("ignDir\n"), 0o644)
knownFiles := setupKnownFiles(t, ffs, []byte("hello\n"))
@@ -116,7 +116,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
must(t, ffs.MkdirAll(".stfolder", 0o755))
oldData := []byte("hello\n")
knownFiles := setupKnownFiles(t, ffs, oldData)
@@ -151,7 +151,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
// Update the file.
newData := []byte("totally different data\n")
writeFilePerm(t, ffs, "knownDir/knownFile", newData, 0644)
writeFilePerm(t, ffs, "knownDir/knownFile", newData, 0o644)
// Rescan.
@@ -206,7 +206,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
must(t, ffs.MkdirAll(".stfolder", 0o755))
oldData := []byte("hello\n")
knownFiles := setupKnownFiles(t, ffs, oldData)
@@ -241,8 +241,8 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
// Create a file and modify another
const file = "foo"
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
writeFilePerm(t, ffs, "knownDir/knownFile", []byte("bye\n"), 0644)
writeFilePerm(t, ffs, file, []byte("hello\n"), 0o644)
writeFilePerm(t, ffs, "knownDir/knownFile", []byte("bye\n"), 0o644)
must(t, m.ScanFolder("ro"))
@@ -254,7 +254,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
// Remove the file again and undo the modification
must(t, ffs.Remove(file))
writeFilePerm(t, ffs, "knownDir/knownFile", oldData, 0644)
writeFilePerm(t, ffs, "knownDir/knownFile", oldData, 0o644)
must(t, ffs.Chtimes("knownDir/knownFile", knownFiles[1].ModTime(), knownFiles[1].ModTime()))
must(t, m.ScanFolder("ro"))
@@ -276,7 +276,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
must(t, ffs.MkdirAll(".stfolder", 0o755))
oldData := []byte("hello\n")
knownFiles := setupKnownFiles(t, ffs, oldData)
@@ -341,7 +341,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
must(t, ffs.MkdirAll(".stfolder", 0o755))
oldData := []byte("hello\n")
knownFiles := setupKnownFiles(t, ffs, oldData)
@@ -377,8 +377,8 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
const file = "foo"
knownFile := filepath.Join("knownDir", "knownFile")
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
writeFilePerm(t, ffs, knownFile, []byte("bye\n"), 0644)
writeFilePerm(t, ffs, file, []byte("hello\n"), 0o644)
writeFilePerm(t, ffs, knownFile, []byte("bye\n"), 0o644)
must(t, m.ScanFolder("ro"))
@@ -431,10 +431,10 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
must(t, ffs.MkdirAll(".stfolder", 0o755))
data := []byte("hello\n")
name := "foo"
writeFilePerm(t, ffs, name, data, 0644)
writeFilePerm(t, ffs, name, data, 0o644)
// Make sure the file is scanned and locally changed
must(t, m.ScanFolder("ro"))
@@ -483,8 +483,8 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.FileInfo {
t.Helper()
must(t, ffs.MkdirAll("knownDir", 0755))
writeFilePerm(t, ffs, "knownDir/knownFile", data, 0644)
must(t, ffs.MkdirAll("knownDir", 0o755))
writeFilePerm(t, ffs, "knownDir/knownFile", data, 0o644)
t0 := time.Now().Add(-1 * time.Minute)
must(t, ffs.Chtimes("knownDir/knownFile", t0, t0))
@@ -498,14 +498,14 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
{
Name: "knownDir",
Type: protocol.FileInfoTypeDirectory,
Permissions: 0755,
Permissions: 0o755,
Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
Sequence: 42,
},
{
Name: "knownDir/knownFile",
Type: protocol.FileInfoTypeFile,
Permissions: 0644,
Permissions: 0o644,
Size: fi.Size(),
ModifiedS: fi.ModTime().Unix(),
ModifiedNs: int(fi.ModTime().UnixNano() % 1e9),
@@ -521,9 +521,9 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.CancelFunc) {
t.Helper()
w, cancel := createTmpWrapper(defaultCfg)
w, cancel := newConfigWrapper(defaultCfg)
cfg := w.RawCopy()
fcfg := testFolderConfigFake()
fcfg := newFolderConfig()
fcfg.ID = "ro"
fcfg.Label = "ro"
fcfg.Type = config.FolderTypeReceiveOnly

View File

@@ -622,7 +622,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot,
// not MkdirAll because the parent should already exist.
mkdir := func(path string) error {
err = f.mtimefs.Mkdir(path, mode)
if err != nil || f.IgnorePerms || file.NoPermissions {
if err != nil || f.IgnorePerms {
return err
}
@@ -631,6 +631,10 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot,
return err
}
if file.NoPermissions {
return nil
}
// Stat the directory so we can check its permissions.
info, err := f.mtimefs.Lstat(path)
if err != nil {

View File

@@ -9,7 +9,6 @@ package model
import (
"bytes"
"context"
"crypto/rand"
"errors"
"fmt"
"io"
@@ -25,8 +24,8 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/scanner"
"github.com/syncthing/syncthing/lib/sync"
)
@@ -43,6 +42,28 @@ var blocks = []protocol.BlockInfo{
{Offset: 917504, Size: 0x20000, Hash: []uint8{0x96, 0x6b, 0x15, 0x6b, 0xc4, 0xf, 0x19, 0x18, 0xca, 0xbb, 0x5f, 0xd6, 0xbb, 0xa2, 0xc6, 0x2a, 0xac, 0xbb, 0x8a, 0xb9, 0xce, 0xec, 0x4c, 0xdb, 0x78, 0xec, 0x57, 0x5d, 0x33, 0xf9, 0x8e, 0xaf}},
}
func prepareTmpFile(to fs.Filesystem) (string, error) {
tmpName := fs.TempName("file")
in, err := os.Open("testdata/tmpfile")
if err != nil {
return "", err
}
defer in.Close()
out, err := to.Create(tmpName)
if err != nil {
return "", err
}
defer out.Close()
if _, err = io.Copy(out, in); err != nil {
return "", err
}
future := time.Now().Add(time.Hour)
if err := to.Chtimes(tmpName, future, future); err != nil {
return "", err
}
return tmpName, nil
}
var folders = []string{"default"}
var diffTestData = []struct {
@@ -91,7 +112,7 @@ func createEmptyFileInfo(t *testing.T, name string, fs fs.Filesystem) protocol.F
// Sets up a folder and model, but makes sure the services aren't actually running.
func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testModel, *sendReceiveFolder, context.CancelFunc) {
w, fcfg, wCancel := tmpDefaultWrapper(t)
w, fcfg, wCancel := newDefaultCfgWrapper()
// Initialise model and stop immediately.
model := setupModel(t, w)
model.cancel()
@@ -108,12 +129,6 @@ func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testMode
return model, f, wCancel
}
func cleanupSRFolder(f *sendReceiveFolder, m *testModel, wrapperCancel context.CancelFunc) {
wrapperCancel()
os.Remove(m.cfg.ConfigPath())
os.RemoveAll(f.Filesystem(nil).URI())
}
// Layout of the files: (indexes from the above array)
// 12345678 - Required file
// 02005008 - Existing file (currently in the index)
@@ -129,8 +144,8 @@ func TestHandleFile(t *testing.T) {
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer wcfgCancel()
copyChan := make(chan copyBlocksState, 1)
@@ -171,8 +186,8 @@ func TestHandleFileWithTemp(t *testing.T) {
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer wcfgCancel()
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
t.Fatal(err)
@@ -205,111 +220,101 @@ func TestHandleFileWithTemp(t *testing.T) {
}
func TestCopierFinder(t *testing.T) {
methods := []fs.CopyRangeMethod{fs.CopyRangeMethodStandard, fs.CopyRangeMethodAllWithFallback}
if build.IsLinux {
methods = append(methods, fs.CopyRangeMethodSendFile)
// After diff between required and existing we should:
// Copy: 1, 2, 3, 4, 6, 7, 8
// Since there is no existing file, nor a temp file
// After dropping out blocks found locally:
// Pull: 1, 5, 6, 8
tempFile := fs.TempName("file2")
existingBlocks := []int{0, 2, 3, 4, 0, 0, 7, 0}
existingFile := setupFile(fs.TempName("file"), existingBlocks)
existingFile.Size = 1
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
requiredFile.Name = "file2"
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer wcfgCancel()
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
t.Fatal(err)
}
for _, method := range methods {
t.Run(method.String(), func(t *testing.T) {
// After diff between required and existing we should:
// Copy: 1, 2, 3, 4, 6, 7, 8
// Since there is no existing file, nor a temp file
// After dropping out blocks found locally:
// Pull: 1, 5, 6, 8
copyChan := make(chan copyBlocksState)
pullChan := make(chan pullBlockState, 4)
finisherChan := make(chan *sharedPullerState, 1)
tempFile := fs.TempName("file2")
// Run a single fetcher routine
go f.copierRoutine(copyChan, pullChan, finisherChan)
defer close(copyChan)
existingBlocks := []int{0, 2, 3, 4, 0, 0, 7, 0}
existingFile := setupFile(fs.TempName("file"), existingBlocks)
existingFile.Size = 1
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
requiredFile.Name = "file2"
f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan)
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
f.CopyRangeMethod = method
timeout := time.After(10 * time.Second)
pulls := make([]pullBlockState, 4)
for i := 0; i < 4; i++ {
select {
case pulls[i] = <-pullChan:
case <-timeout:
t.Fatalf("Timed out before receiving all 4 states on pullChan (already got %v)", i)
}
}
var finish *sharedPullerState
select {
case finish = <-finisherChan:
case <-timeout:
t.Fatal("Timed out before receiving 4 states on pullChan")
}
defer cleanupSRFolder(f, m, wcfgCancel)
defer cleanupSharedPullerState(finish)
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
t.Fatal(err)
select {
case <-pullChan:
t.Fatal("Pull channel has data to be read")
case <-finisherChan:
t.Fatal("Finisher channel has data to be read")
default:
}
// Verify that the right blocks went into the pull list.
// They are pulled in random order.
for _, idx := range []int{1, 5, 6, 8} {
found := false
block := blocks[idx]
for _, pulledBlock := range pulls {
if bytes.Equal(pulledBlock.block.Hash, block.Hash) {
found = true
break
}
}
if !found {
t.Errorf("Did not find block %s", block.String())
}
if !bytes.Equal(finish.file.Blocks[idx-1].Hash, blocks[idx].Hash) {
t.Errorf("Block %d mismatch: %s != %s", idx, finish.file.Blocks[idx-1].String(), blocks[idx].String())
}
}
copyChan := make(chan copyBlocksState)
pullChan := make(chan pullBlockState, 4)
finisherChan := make(chan *sharedPullerState, 1)
// Verify that the fetched blocks have actually been written to the temp file
blks, err := scanner.HashFile(context.TODO(), f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false)
if err != nil {
t.Log(err)
}
// Run a single fetcher routine
go f.copierRoutine(copyChan, pullChan, finisherChan)
defer close(copyChan)
f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan)
timeout := time.After(10 * time.Second)
pulls := make([]pullBlockState, 4)
for i := 0; i < 4; i++ {
select {
case pulls[i] = <-pullChan:
case <-timeout:
t.Fatalf("Timed out before receiving all 4 states on pullChan (already got %v)", i)
}
}
var finish *sharedPullerState
select {
case finish = <-finisherChan:
case <-timeout:
t.Fatal("Timed out before receiving 4 states on pullChan")
}
defer cleanupSharedPullerState(finish)
select {
case <-pullChan:
t.Fatal("Pull channel has data to be read")
case <-finisherChan:
t.Fatal("Finisher channel has data to be read")
default:
}
// Verify that the right blocks went into the pull list.
// They are pulled in random order.
for _, idx := range []int{1, 5, 6, 8} {
found := false
block := blocks[idx]
for _, pulledBlock := range pulls {
if bytes.Equal(pulledBlock.block.Hash, block.Hash) {
found = true
break
}
}
if !found {
t.Errorf("Did not find block %s", block.String())
}
if !bytes.Equal(finish.file.Blocks[idx-1].Hash, blocks[idx].Hash) {
t.Errorf("Block %d mismatch: %s != %s", idx, finish.file.Blocks[idx-1].String(), blocks[idx].String())
}
}
// Verify that the fetched blocks have actually been written to the temp file
blks, err := scanner.HashFile(context.TODO(), f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false)
if err != nil {
t.Log(err)
}
for _, eq := range []int{2, 3, 4, 7} {
if !bytes.Equal(blks[eq-1].Hash, blocks[eq].Hash) {
t.Errorf("Block %d mismatch: %s != %s", eq, blks[eq-1].String(), blocks[eq].String())
}
}
})
for _, eq := range []int{2, 3, 4, 7} {
if !bytes.Equal(blks[eq-1].Hash, blocks[eq].Hash) {
t.Errorf("Block %d mismatch: %s != %s", eq, blks[eq-1].String(), blocks[eq].String())
}
}
}
func TestWeakHash(t *testing.T) {
// Setup the model/pull environment
model, fo, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(fo, model, wcfgCancel)
_, fo, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := fo.Filesystem(nil)
tempFile := fs.TempName("weakhash")
@@ -438,7 +443,7 @@ func TestCopierCleanup(t *testing.T) {
file := setupFile("test", []int{0})
file.Size = 1
m, f, wcfgCancel := setupSendReceiveFolder(t, file)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
file.Blocks = []protocol.BlockInfo{blocks[1]}
file.Version = file.Version.Update(myID.Short())
@@ -471,7 +476,7 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
// Set up our evet subscription early
s := m.evLogger.Subscribe(events.ItemFinished)
@@ -571,7 +576,7 @@ func TestDeregisterOnFailInPull(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
// Set up our evet subscription early
s := m.evLogger.Subscribe(events.ItemFinished)
@@ -673,16 +678,15 @@ func TestDeregisterOnFailInPull(t *testing.T) {
}
func TestIssue3164(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
tmpDir := ffs.URI()
ignDir := filepath.Join("issue3164", "oktodelete")
subDir := filepath.Join(ignDir, "foobar")
must(t, ffs.MkdirAll(subDir, 0777))
must(t, os.WriteFile(filepath.Join(tmpDir, subDir, "file"), []byte("Hello"), 0644))
must(t, os.WriteFile(filepath.Join(tmpDir, ignDir, "file"), []byte("Hello"), 0644))
must(t, ffs.MkdirAll(subDir, 0o777))
must(t, fs.WriteFile(ffs, filepath.Join(subDir, "file"), []byte("Hello"), 0o644))
must(t, fs.WriteFile(ffs, filepath.Join(ignDir, "file"), []byte("Hello"), 0o644))
file := protocol.FileInfo{
Name: "issue3164",
}
@@ -764,8 +768,8 @@ func TestDiffEmpty(t *testing.T) {
// option is true and the permissions do not match between the file on disk and
// in the db.
func TestDeleteIgnorePerms(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
f.IgnorePerms = true
@@ -780,7 +784,7 @@ func TestDeleteIgnorePerms(t *testing.T) {
must(t, err)
fi, err := scanner.CreateFileInfo(stat, name, ffs, false, false, config.XattrFilter{})
must(t, err)
ffs.Chmod(name, 0600)
ffs.Chmod(name, 0o600)
if info, err := ffs.Stat(name); err == nil {
fi.InodeChangeNs = info.InodeChangeTime().UnixNano()
}
@@ -806,7 +810,7 @@ func TestCopyOwner(t *testing.T) {
// filesystem.
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
f.folder.FolderConfiguration = newFolderConfiguration(m.cfg, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner")
f.folder.FolderConfiguration.CopyOwnershipFromParent = true
@@ -815,13 +819,13 @@ func TestCopyOwner(t *testing.T) {
// Create a parent dir with a certain owner/group.
f.mtimefs.Mkdir("foo", 0755)
f.mtimefs.Mkdir("foo", 0o755)
f.mtimefs.Lchown("foo", strconv.Itoa(expOwner), strconv.Itoa(expGroup))
dir := protocol.FileInfo{
Name: "foo/bar",
Type: protocol.FileInfoTypeDirectory,
Permissions: 0755,
Permissions: 0o755,
}
// Have the folder create a subdirectory, verify that it's the correct
@@ -851,7 +855,7 @@ func TestCopyOwner(t *testing.T) {
file := protocol.FileInfo{
Name: "foo/bar/baz",
Type: protocol.FileInfoTypeFile,
Permissions: 0644,
Permissions: 0o644,
}
// Wire some stuff. The flow here is handleFile() -[copierChan]->
@@ -885,7 +889,7 @@ func TestCopyOwner(t *testing.T) {
symlink := protocol.FileInfo{
Name: "foo/bar/sym",
Type: protocol.FileInfoTypeSymlink,
Permissions: 0644,
Permissions: 0o644,
SymlinkTarget: "over the rainbow",
}
@@ -908,8 +912,8 @@ func TestCopyOwner(t *testing.T) {
// TestSRConflictReplaceFileByDir checks that a conflict is created when an existing file
// is replaced with a directory and versions are conflicting
func TestSRConflictReplaceFileByDir(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
name := "foo"
@@ -940,8 +944,8 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
// TestSRConflictReplaceFileByLink checks that a conflict is created when an existing file
// is replaced with a link and versions are conflicting
func TestSRConflictReplaceFileByLink(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
name := "foo"
@@ -973,30 +977,19 @@ func TestSRConflictReplaceFileByLink(t *testing.T) {
// TestDeleteBehindSymlink checks that we don't delete or schedule a scan
// when trying to delete a file behind a symlink.
func TestDeleteBehindSymlink(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
destDir := t.TempDir()
destFs := fs.NewFilesystem(fs.FilesystemTypeBasic, destDir)
link := "link"
file := filepath.Join(link, "file")
linkFile := filepath.Join(link, "file")
must(t, ffs.MkdirAll(link, 0755))
fi := createEmptyFileInfo(t, file, ffs)
must(t, ffs.MkdirAll(link, 0o755))
fi := createEmptyFileInfo(t, linkFile, ffs)
f.updateLocalsFromScanning([]protocol.FileInfo{fi})
must(t, osutil.RenameOrCopy(fs.CopyRangeMethodStandard, ffs, destFs, file, "file"))
must(t, ffs.Rename(linkFile, "file"))
must(t, ffs.RemoveAll(link))
if err := fs.DebugSymlinkForTestsOnly(destFs, ffs, "", link); err != nil {
if build.IsWindows {
// Probably we require permissions we don't have.
t.Skip("Need admin permissions or developer mode to run symlink test on Windows: " + err.Error())
} else {
t.Fatal(err)
}
}
must(t, ffs.CreateSymlink("/", link))
fi.Deleted = true
fi.Version = fi.Version.Update(device1.Short())
@@ -1016,15 +1009,15 @@ func TestDeleteBehindSymlink(t *testing.T) {
default:
t.Fatalf("No db update received")
}
if _, err := destFs.Stat("file"); err != nil {
if _, err := ffs.Stat("file"); err != nil {
t.Errorf("Expected no error when stating file behind symlink, got %v", err)
}
}
// Reproduces https://github.com/syncthing/syncthing/issues/6559
func TestPullCtxCancel(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
pullChan := make(chan pullBlockState)
finisherChan := make(chan *sharedPullerState)
@@ -1065,12 +1058,12 @@ func TestPullCtxCancel(t *testing.T) {
}
func TestPullDeleteUnscannedDir(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
ffs := f.Filesystem(nil)
dir := "foobar"
must(t, ffs.MkdirAll(dir, 0777))
must(t, ffs.MkdirAll(dir, 0o777))
fi := protocol.FileInfo{
Name: dir,
}
@@ -1095,7 +1088,7 @@ func TestPullDeleteUnscannedDir(t *testing.T) {
func TestPullCaseOnlyPerformFinish(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
ffs := f.Filesystem(nil)
name := "foo"
@@ -1157,12 +1150,12 @@ func TestPullCaseOnlySymlink(t *testing.T) {
func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
ffs := f.Filesystem(nil)
name := "foo"
if dir {
must(t, ffs.Mkdir(name, 0777))
must(t, ffs.Mkdir(name, 0o777))
} else {
must(t, ffs.CreateSymlink("target", name))
}
@@ -1212,8 +1205,8 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
}
func TestPullTempFileCaseConflict(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
copyChan := make(chan copyBlocksState, 1)
@@ -1241,7 +1234,7 @@ func TestPullTempFileCaseConflict(t *testing.T) {
func TestPullCaseOnlyRename(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
// tempNameConfl := fs.TempName(confl)
@@ -1284,7 +1277,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
}
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
defer wcfgCancel()
addFakeConn(m, device1, f.ID)
name := "foo"
@@ -1325,8 +1318,8 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
}
func TestPullDeleteCaseConflict(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
name := "foo"
fi := protocol.FileInfo{Name: "Foo"}
@@ -1359,8 +1352,8 @@ func TestPullDeleteCaseConflict(t *testing.T) {
}
func TestPullDeleteIgnoreChildDir(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
_, f, wcfgCancel := setupSendReceiveFolder(t)
defer wcfgCancel()
parent := "parent"
del := "ignored"
@@ -1372,9 +1365,9 @@ func TestPullDeleteIgnoreChildDir(t *testing.T) {
`, child, del)), ""))
f.ignores = matcher
must(t, f.mtimefs.Mkdir(parent, 0777))
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del), 0777))
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del, child), 0777))
must(t, f.mtimefs.Mkdir(parent, 0o777))
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del), 0o777))
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del, child), 0o777))
scanChan := make(chan string, 2)

View File

@@ -16,6 +16,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
)
type unifySubsCase struct {
@@ -156,8 +157,7 @@ func TestSetPlatformData(t *testing.T) {
// Checks that setPlatformData runs without error when applied to a temp
// file, named differently than the given FileInfo.
dir := t.TempDir()
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
if fd, err := fs.Create("file.tmp"); err != nil {
t.Fatal(err)
} else {
@@ -167,7 +167,7 @@ func TestSetPlatformData(t *testing.T) {
xattr := []protocol.Xattr{{Name: "user.foo", Value: []byte("bar")}}
fi := &protocol.FileInfo{
Name: "should be ignored",
Permissions: 0400,
Permissions: 0o400,
ModifiedS: 1234567890,
Platform: protocol.PlatformData{
Linux: &protocol.XattrData{Xattrs: xattr},

View File

@@ -1220,7 +1220,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
haveFcfg := cfg.FolderMap()
for _, folder := range cm.Folders {
from, ok := haveFcfg[folder.ID]
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Defaults.Folder.Path); changed {
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Defaults.Folder); changed {
changedFcfg[folder.ID] = to
}
}
@@ -1664,19 +1664,34 @@ func (*model) handleDeintroductions(introducerCfg config.DeviceConfiguration, fo
// handleAutoAccepts handles adding and sharing folders for devices that have
// AutoAcceptFolders set to true.
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultFolderCfg config.FolderConfiguration) (config.FolderConfiguration, bool) {
if !haveCfg {
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
pathAlternatives := []string{
fs.SanitizePath(folder.Label),
fs.SanitizePath(folder.ID),
defaultPathFs := fs.NewFilesystem(defaultFolderCfg.FilesystemType, defaultFolderCfg.Path)
var pathAlternatives []string
if alt := fs.SanitizePath(folder.Label); alt != "" {
pathAlternatives = append(pathAlternatives, alt)
}
if alt := fs.SanitizePath(folder.ID); alt != "" {
pathAlternatives = append(pathAlternatives, alt)
}
if len(pathAlternatives) == 0 {
l.Infof("Failed to auto-accept folder %s from %s due to lack of path alternatives", folder.Description(), deviceID)
return config.FolderConfiguration{}, false
}
for _, path := range pathAlternatives {
// Make sure the folder path doesn't already exist.
if _, err := defaultPathFs.Lstat(path); !fs.IsNotExist(err) {
continue
}
fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, fs.FilesystemTypeBasic, filepath.Join(defaultPath, path))
// Attempt to create it to make sure it does, now.
fullPath := filepath.Join(defaultFolderCfg.Path, path)
if err := defaultPathFs.MkdirAll(path, 0o700); err != nil {
l.Warnf("Failed to create path for auto-accepted folder %s at path %s: %v", folder.Description(), fullPath, err)
continue
}
fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, defaultFolderCfg.FilesystemType, fullPath)
fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{
DeviceID: deviceID,
})

View File

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,7 @@ func TestProgressEmitter(t *testing.T) {
w := evLogger.Subscribe(events.DownloadProgress)
c, cfgCancel := createTmpWrapper(config.Configuration{})
c, cfgCancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
defer os.Remove(c.ConfigPath())
defer cfgCancel()
waiter, err := c.Modify(func(cfg *config.Configuration) {
@@ -110,11 +110,10 @@ func TestProgressEmitter(t *testing.T) {
expectEvent(w, t, 0)
expectTimeout(w, t)
}
func TestSendDownloadProgressMessages(t *testing.T) {
c, cfgCancel := createTmpWrapper(config.Configuration{})
c, cfgCancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
defer os.Remove(c.ConfigPath())
defer cfgCancel()
waiter, err := c.Modify(func(cfg *config.Configuration) {
@@ -455,8 +454,8 @@ func TestSendDownloadProgressMessages(t *testing.T) {
// See progressemitter.go for explanation why this is commented out.
// Search for state.cleanup
//expect(-1, state2, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, false)
//expect(-1, state4, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, true)
// expect(-1, state2, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, false)
// expect(-1, state4, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, true)
expectEmpty()

View File

@@ -10,7 +10,7 @@ import (
"bytes"
"context"
"errors"
"os"
"io"
"path/filepath"
"strconv"
"strings"
@@ -56,6 +56,7 @@ func TestRequestSimple(t *testing.T) {
// Send an update for the test file, wait for it to sync and be reported back.
contents := []byte("test file contents\n")
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
fc.sendIndexUpdate()
select {
case <-done:
@@ -64,7 +65,7 @@ func TestRequestSimple(t *testing.T) {
}
// Verify the contents
if err := equalContents(filepath.Join(tfs.URI(), "testfile"), contents); err != nil {
if err := equalContents(tfs, "testfile", contents); err != nil {
t.Error("File did not sync correctly:", err)
}
}
@@ -213,76 +214,6 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
}
}
func TestRequestVersioningSymlinkAttack(t *testing.T) {
if build.IsWindows {
t.Skip("no symlink support on Windows")
}
// Sets up a folder with trashcan versioning and tries to use a
// deleted symlink to escape
w, fcfg, wCancel := tmpDefaultWrapper(t)
defer wCancel()
defer func() {
os.RemoveAll(fcfg.Filesystem(nil).URI())
os.Remove(w.ConfigPath())
}()
fcfg.Versioning = config.VersioningConfiguration{Type: "trashcan"}
setFolder(t, w, fcfg)
m, fc := setupModelWithConnectionFromWrapper(t, w)
defer cleanupModel(m)
// Create a temporary directory that we will use as target to see if
// we can escape to it
tmpdir := t.TempDir()
// We listen for incoming index updates and trigger when we see one for
// the expected test file.
idx := make(chan int)
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
idx <- len(fs)
return nil
})
waitForIdx := func() {
select {
case c := <-idx:
if c == 0 {
t.Fatal("Got empty index update")
}
case <-time.After(5 * time.Second):
t.Fatal("timed out before receiving index update")
}
}
// Send an update for the test file, wait for it to sync and be reported back.
fc.addFile("foo", 0o644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
fc.sendIndexUpdate()
waitForIdx()
// Delete the symlink, hoping for it to get versioned
fc.deleteFile("foo")
fc.sendIndexUpdate()
waitForIdx()
// Recreate foo and a file in it with some data
fc.updateFile("foo", 0o755, protocol.FileInfoTypeDirectory, nil)
fc.addFile("foo/test", 0o644, protocol.FileInfoTypeFile, []byte("testtesttest"))
fc.sendIndexUpdate()
waitForIdx()
// Remove the test file and see if it escaped
fc.deleteFile("foo/test")
fc.sendIndexUpdate()
waitForIdx()
path := filepath.Join(tmpdir, "test")
if _, err := os.Lstat(path); !os.IsNotExist(err) {
t.Fatal("File escaped to", path)
}
}
func TestPullInvalidIgnoredSO(t *testing.T) {
t.Skip("flaky")
pullInvalidIgnored(t, config.FolderTypeSendOnly)
@@ -295,9 +226,9 @@ func TestPullInvalidIgnoredSR(t *testing.T) {
// This test checks that (un-)ignored/invalid/deleted files are treated as expected.
func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
w, wCancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
w, wCancel := newConfigWrapper(defaultCfgWrapper.RawCopy())
defer wCancel()
fcfg := testFolderConfig(t.TempDir())
fcfg := w.FolderList()[0]
fss := fcfg.Filesystem(nil)
fcfg.Type = ft
setFolder(t, w, fcfg)
@@ -676,10 +607,17 @@ func TestRequestSymlinkWindows(t *testing.T) {
}
}
func equalContents(path string, contents []byte) error {
if bs, err := os.ReadFile(path); err != nil {
func equalContents(fs fs.Filesystem, path string, contents []byte) error {
fd, err := fs.Open(path)
defer fd.Close()
if err != nil {
return err
} else if !bytes.Equal(bs, contents) {
}
bs, err := io.ReadAll(fd)
if err != nil {
return err
}
if !bytes.Equal(bs, contents) {
return errors.New("incorrect data")
}
return nil
@@ -691,8 +629,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem(nil)
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tfs.URI())
defer cleanupModel(m)
received := make(chan []protocol.FileInfo)
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
@@ -727,7 +664,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
}
for _, n := range [2]string{a, b} {
must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
must(t, equalContents(tfs, n, data[n]))
}
var gotA, gotB, gotConfl bool
@@ -806,11 +743,11 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
case path == a:
t.Errorf(`File "a" was not removed`)
case path == b:
if err := equalContents(filepath.Join(tmpDir, b), data[a]); err != nil {
if err := equalContents(tfs, b, data[a]); err != nil {
t.Error(`File "b" has unexpected content (renamed from a on remote)`)
}
case strings.HasPrefix(path, b+".sync-conflict-"):
if err := equalContents(filepath.Join(tmpDir, path), otherData); err != nil {
if err := equalContents(tfs, path, otherData); err != nil {
t.Error(`Sync conflict of "b" has unexptected content`)
}
case path == "." || strings.HasPrefix(path, ".stfolder"):
@@ -825,8 +762,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem(nil)
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
defer cleanupModel(m)
recv := make(chan int)
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
@@ -855,7 +791,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
}
for _, n := range [2]string{a, b} {
must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
must(t, equalContents(tfs, n, data[n]))
}
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
@@ -983,9 +919,7 @@ func TestRequestDeleteChanged(t *testing.T) {
func TestNeedFolderFiles(t *testing.T) {
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem(nil)
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
defer cleanupModel(m)
sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
defer sub.Unsubscribe()
@@ -1028,12 +962,11 @@ func TestNeedFolderFiles(t *testing.T) {
// propagated upon un-ignoring.
// https://github.com/syncthing/syncthing/issues/6038
func TestIgnoreDeleteUnignore(t *testing.T) {
w, fcfg, wCancel := tmpDefaultWrapper(t)
w, fcfg, wCancel := newDefaultCfgWrapper()
defer wCancel()
m := setupModel(t, w)
fss := fcfg.Filesystem(nil)
tmpDir := fss.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
defer cleanupModel(m)
folderIgnoresAlwaysReload(t, m, fcfg)
m.ScanFolders()
@@ -1272,7 +1205,7 @@ func TestRequestIndexSenderPause(t *testing.T) {
}
func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
w, fcfg, wCancel := tmpDefaultWrapper(t)
w, fcfg, wCancel := newDefaultCfgWrapper()
defer wCancel()
tfs := fcfg.Filesystem(nil)
dir1 := "foo"
@@ -1339,15 +1272,13 @@ func TestRequestReceiveEncrypted(t *testing.T) {
t.Skip("skipping on short testing - scrypt is too slow")
}
w, fcfg, wCancel := tmpDefaultWrapper(t)
w, fcfg, wCancel := newDefaultCfgWrapper()
defer wCancel()
tfs := fcfg.Filesystem(nil)
fcfg.Type = config.FolderTypeReceiveEncrypted
setFolder(t, w, fcfg)
keyGen := protocol.NewKeyGenerator()
encToken := protocol.PasswordToken(keyGen, fcfg.ID, "pw")
must(t, tfs.Mkdir(config.DefaultMarkerName, 0o777))
encToken := protocol.PasswordToken(protocol.NewKeyGenerator(), fcfg.ID, "pw")
must(t, writeEncryptionToken(encToken, fcfg))
m := setupModel(t, w)

View File

@@ -7,25 +7,21 @@
package model
import (
"os"
"testing"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sync"
)
// Test creating temporary file inside read-only directory
func TestReadOnlyDir(t *testing.T) {
// Create a read only directory, clean it up afterwards.
tmpDir := t.TempDir()
if err := os.Chmod(tmpDir, 0555); err != nil {
t.Fatal(err)
}
defer os.Chmod(tmpDir, 0755)
ffs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
ffs.Mkdir("testdir", 0o555)
s := sharedPullerState{
fs: fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir),
tempName: ".temp_name",
fs: ffs,
tempName: "testdir/.temp_name",
mut: sync.NewRWMutex(),
}

View File

@@ -1 +0,0 @@
foobarbaz

View File

@@ -1 +0,0 @@
baazquux

View File

View File

@@ -1 +0,0 @@
foobar

View File

@@ -7,9 +7,6 @@
package model
import (
"os"
"time"
"github.com/syncthing/syncthing/lib/fs"
)
@@ -19,10 +16,6 @@ type fatal interface {
Helper()
}
type fatalOs struct {
fatal
}
func must(f fatal, err error) {
f.Helper()
if err != nil {
@@ -36,56 +29,3 @@ func mustRemove(f fatal, err error) {
f.Fatal(err)
}
}
func (f *fatalOs) Chmod(name string, mode os.FileMode) {
f.Helper()
must(f, os.Chmod(name, mode))
}
func (f *fatalOs) Chtimes(name string, atime time.Time, mtime time.Time) {
f.Helper()
must(f, os.Chtimes(name, atime, mtime))
}
func (f *fatalOs) Create(name string) *os.File {
f.Helper()
file, err := os.Create(name)
must(f, err)
return file
}
func (f *fatalOs) Mkdir(name string, perm os.FileMode) {
f.Helper()
must(f, os.Mkdir(name, perm))
}
func (f *fatalOs) MkdirAll(name string, perm os.FileMode) {
f.Helper()
must(f, os.MkdirAll(name, perm))
}
func (f *fatalOs) Remove(name string) {
f.Helper()
if err := os.Remove(name); err != nil && !os.IsNotExist(err) {
f.Fatal(err)
}
}
func (f *fatalOs) RemoveAll(name string) {
f.Helper()
if err := os.RemoveAll(name); err != nil && !os.IsNotExist(err) {
f.Fatal(err)
}
}
func (f *fatalOs) Rename(oldname, newname string) {
f.Helper()
must(f, os.Rename(oldname, newname))
}
func (f *fatalOs) Stat(name string) os.FileInfo {
f.Helper()
info, err := os.Stat(name)
must(f, err)
return info
}

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