Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed6b56d48f | ||
|
|
f145da2613 | ||
|
|
2f55d28b5c | ||
|
|
733f63592f | ||
|
|
b8a255cc96 | ||
|
|
703b185982 | ||
|
|
119d5e72ef | ||
|
|
3993f62beb | ||
|
|
130850097d | ||
|
|
ec35a95318 | ||
|
|
b15ff42d52 | ||
|
|
2f81bf5d34 | ||
|
|
39b0bd20d0 | ||
|
|
977f790143 | ||
|
|
5fa3d9f418 | ||
|
|
d73c00799b | ||
|
|
9f7b4d4104 | ||
|
|
d710874903 | ||
|
|
578cc5a165 | ||
|
|
a0171e3a9b | ||
|
|
1722f0a7b2 | ||
|
|
c904140b3c | ||
|
|
c1d8045d86 | ||
|
|
d4cffd848e | ||
|
|
5277033906 | ||
|
|
ef4629ea56 | ||
|
|
35576881ff | ||
|
|
466d30bd70 | ||
|
|
2775f424f2 |
@@ -50,6 +50,7 @@ Alexandre Alves <alexandrealvesdb.contact@gmail.com>
|
||||
Aman Gupta <aman@tmm1.net>
|
||||
Andreas Sommer <andreas.sommer87@googlemail.com>
|
||||
andresvia <andres.via@gmail.com>
|
||||
Andrew Gunnerson <accounts+github@chiller3.com>
|
||||
Andrew Rabert (nvllsvm) <ar@nullsum.net> <6550543+nvllsvm@users.noreply.github.com>
|
||||
Andrey D (scienmind) <scintertech@cryptolab.net> <scienmind@users.noreply.github.com>
|
||||
andyleap <andyleap@gmail.com>
|
||||
@@ -134,6 +135,7 @@ Felix Lampe <mail@flampe.de>
|
||||
Felix Unterpaintner (bigbear2nd) <bigbear2nd@gmail.com>
|
||||
Francois-Xavier Gsell (zukoo) <fxgsell@gmail.com>
|
||||
Frank Isemann (fti7) <frank@isemann.name>
|
||||
Frederic <1665799+Finomosec@users.noreply.github.com>
|
||||
Gahl Saraf <saraf.gahl@gmail.com> <gahl@raftt.io>
|
||||
georgespatton <georgespatton@users.noreply.github.com>
|
||||
ghjklw <malo@jaffre.info>
|
||||
@@ -277,6 +279,7 @@ red_led <red-led@users.noreply.github.com>
|
||||
Robert Carosi (nov1n) <robert@carosi.nl>
|
||||
Roberto Santalla <roobre@users.noreply.github.com>
|
||||
Robin Schoonover <robin@cornhooves.org>
|
||||
Rohit Tanwar <31792358+rohitanwar@users.noreply.github.com>
|
||||
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
|
||||
rubenbe <github-com-00ff86@vandamme.email>
|
||||
Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com>
|
||||
@@ -302,6 +305,7 @@ Sven Bachmann <dev@mcbachmann.de>
|
||||
Sébastien WENSKE <sebastien@wenske.fr>
|
||||
Tao <mail@steadytao.com>
|
||||
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
|
||||
tbodt <tbodt@tbodt.com>
|
||||
Terrance <git@terrance.allofti.me>
|
||||
TheCreeper <TheCreeper@users.noreply.github.com>
|
||||
Thomas <9749173+uhthomas@users.noreply.github.com>
|
||||
|
||||
+18
-33
@@ -1,25 +1,14 @@
|
||||
## Reporting Bugs
|
||||
## Reporting Bugs & Feature Requests
|
||||
|
||||
Please file bugs in the [GitHub Issue
|
||||
Tracker](https://github.com/syncthing/syncthing/issues). Include at
|
||||
least the following:
|
||||
For general discussion and troubleshooting, please use the [Support
|
||||
Forum](https://forum.syncthing.net/). For actionable bug reports and feature
|
||||
requests, please file issues in the [GitHub Issue
|
||||
Tracker](https://github.com/syncthing/syncthing/issues), using one of the
|
||||
provided templates.
|
||||
|
||||
- What happened
|
||||
|
||||
- What did you expect to happen instead of what *did* happen, if it's
|
||||
not crazy obvious
|
||||
|
||||
- What operating system, operating system version and version of
|
||||
Syncthing you are running
|
||||
|
||||
- The same for other connected devices, where relevant
|
||||
|
||||
- Screenshot if the issue concerns something visible in the GUI
|
||||
|
||||
- Console log entries, where possible and relevant
|
||||
|
||||
If you're not sure whether something is relevant, erring on the side of
|
||||
too much information will never get you yelled at. :)
|
||||
:warning: Do not submit AI generated issues or comments. Report any details
|
||||
you experienced as a human, and let anyone who wants to perform AI analysis
|
||||
do so on their own.
|
||||
|
||||
## Contributing Translations
|
||||
|
||||
@@ -42,20 +31,17 @@ repository](https://github.com/syncthing/docs).
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Every contribution is welcome. If you want to contribute but are unsure
|
||||
where to start, any open issues are fair game! Here's a short rundown of
|
||||
what you need to keep in mind:
|
||||
We welcome contributions. However:
|
||||
|
||||
- Don't worry. You are not expected to get everything right on the first
|
||||
attempt, we'll guide you through it.
|
||||
:warning: We do not accept contributions that are wholly or mostly AI
|
||||
generated. You may assume that we are fully capable of prompting an AI
|
||||
ourselves when so inclined.
|
||||
|
||||
- Make sure there is an
|
||||
[issue](https://github.com/syncthing/syncthing/issues) that describes the
|
||||
change you want to do. If the thing you want to do does not have an issue
|
||||
yet, please file one before starting work on it.
|
||||
|
||||
- Fork the repository and make your changes in a new branch. Once it's ready
|
||||
for review, create a pull request.
|
||||
:warning: We do not accept unsolicited pull requests from new contributors.
|
||||
Most such contributions fall afoul of the previous rule. If yours does not,
|
||||
please post a message to the relevant issue with a link to your branch and a
|
||||
short description of the reasoning behind it. We'll take care of onboarding
|
||||
you.
|
||||
|
||||
### Authorship
|
||||
|
||||
@@ -205,4 +191,3 @@ otherwise stated this means MPLv2, but there are exceptions:
|
||||
|
||||
Regardless of the license in effect, you retain the copyright to your
|
||||
contribution.
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ func (l *githubSourceCodeLoader) Load(filename string, line, context int) ([][]b
|
||||
metricSourceCodeCacheSize.Set(float64(l.cache.Len()))
|
||||
}()
|
||||
|
||||
knownPrefixes := []string{"/lib/", "/cmd/"}
|
||||
knownPrefixes := []string{"/internal/", "/lib/", "/cmd/"}
|
||||
var idx int
|
||||
for _, pref := range knownPrefixes {
|
||||
idx = strings.Index(filename, pref)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/assets"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/geoip"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/relay/client"
|
||||
@@ -318,11 +320,12 @@ func handleEndpointFull(rw http.ResponseWriter, r *http.Request) {
|
||||
relays := make([]*relay, len(permanentRelays)+len(knownRelays))
|
||||
n := copy(relays, permanentRelays)
|
||||
copy(relays[n:], knownRelays)
|
||||
mut.RUnlock()
|
||||
|
||||
_ = json.NewEncoder(rw).Encode(map[string][]*relay{
|
||||
bs, _ := json.Marshal(map[string][]*relay{
|
||||
"relays": relays,
|
||||
})
|
||||
mut.RUnlock()
|
||||
|
||||
_, _ = rw.Write(bs)
|
||||
}
|
||||
|
||||
// handleEndpointShort returns the relay list with only the URL.
|
||||
@@ -332,7 +335,10 @@ func handleEndpointShort(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
mut.RLock()
|
||||
relays := make([]relayShort, 0, len(permanentRelays)+len(knownRelays))
|
||||
for _, r := range append(permanentRelays, knownRelays...) {
|
||||
for _, r := range permanentRelays {
|
||||
relays = append(relays, relayShort{URL: slimURL(r.URL)})
|
||||
}
|
||||
for _, r := range knownRelays {
|
||||
relays = append(relays, relayShort{URL: slimURL(r.URL)})
|
||||
}
|
||||
mut.RUnlock()
|
||||
@@ -544,7 +550,7 @@ found:
|
||||
|
||||
mut.Unlock()
|
||||
|
||||
if err := saveRelays(knownRelaysFile, knownRelays); err != nil {
|
||||
if err := saveKnownRelays(knownRelaysFile); err != nil {
|
||||
log.Println("Failed to write known relays: " + err.Error())
|
||||
}
|
||||
|
||||
@@ -607,12 +613,22 @@ func loadRelays(file string, geoip *geoip.Provider) []*relay {
|
||||
return relays
|
||||
}
|
||||
|
||||
func saveRelays(file string, relays []*relay) error {
|
||||
var content string
|
||||
for _, relay := range relays {
|
||||
content += relay.uri.String() + "\n"
|
||||
func saveKnownRelays(file string) error {
|
||||
var buf bytes.Buffer
|
||||
mut.RLock()
|
||||
for _, relay := range knownRelays {
|
||||
fmt.Fprintln(&buf, relay.uri.String())
|
||||
}
|
||||
return os.WriteFile(file, []byte(content), 0o666)
|
||||
mut.RUnlock()
|
||||
|
||||
fd, err := osutil.CreateAtomic(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fd.Write(buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
return fd.Close()
|
||||
}
|
||||
|
||||
func createTestCertificate() tls.Certificate {
|
||||
|
||||
@@ -4,15 +4,15 @@ go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/AudriusButkevicius/recli v0.0.7
|
||||
github.com/alecthomas/kong v1.15.0
|
||||
github.com/alecthomas/kong v1.16.0
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/calmh/incontainer v1.0.0
|
||||
github.com/calmh/xdr v1.2.0
|
||||
github.com/ccding/go-stun v0.1.5
|
||||
github.com/ccding/go-stun v0.1.6
|
||||
github.com/coreos/go-semver v0.3.1
|
||||
github.com/d4l3k/messagediff v1.2.1
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.13
|
||||
github.com/go-ldap/ldap/v3 v3.4.14
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gofrs/flock v0.13.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
@@ -22,17 +22,17 @@ require (
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/maruel/panicparse/v2 v2.5.0
|
||||
github.com/mattn/go-sqlite3 v1.14.45
|
||||
github.com/mattn/go-sqlite3 v1.14.48
|
||||
github.com/maxmind/geoipupdate/v6 v6.1.0
|
||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
|
||||
github.com/oschwald/geoip2-golang v1.13.0
|
||||
github.com/pierrec/lz4/v4 v4.1.27
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/prometheus/client_golang v1.24.0
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||
github.com/quic-go/quic-go v0.60.0
|
||||
github.com/rabbitmq/amqp091-go v1.11.0
|
||||
github.com/rabbitmq/amqp091-go v1.13.0
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
|
||||
github.com/shirou/gopsutil/v4 v4.26.5
|
||||
github.com/shirou/gopsutil/v4 v4.26.6
|
||||
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
|
||||
github.com/thejerf/suture/v4 v4.0.6
|
||||
@@ -40,19 +40,19 @@ require (
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
||||
github.com/willabides/kongplete v0.4.0
|
||||
github.com/wlynxg/anet v0.0.5
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/text v0.38.0
|
||||
golang.org/x/crypto v0.54.0
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef
|
||||
golang.org/x/net v0.57.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/text v0.40.0
|
||||
golang.org/x/time v0.15.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
modernc.org/sqlite v1.52.0
|
||||
modernc.org/sqlite v1.54.0
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.1.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.1.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
@@ -62,7 +62,7 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.10.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||
@@ -82,8 +82,8 @@ require (
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/prometheus/common v0.70.0 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
@@ -93,13 +93,13 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20260610154732-fb80ec83bdd9 // indirect
|
||||
golang.org/x/tools v0.46.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959 // indirect
|
||||
golang.org/x/tools v0.48.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.72.3 // indirect
|
||||
modernc.org/libc v1.74.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,13 +2,13 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AudriusButkevicius/recli v0.0.7 h1:9zjbYlTupi+W5SJXm2cR2sV2mJAIg1sIfDcsW7hrkPM=
|
||||
github.com/AudriusButkevicius/recli v0.0.7/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
|
||||
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
|
||||
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||
github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw=
|
||||
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/kong v1.15.0 h1:BVJstKbpO73zKpmIu+m/aLRrNmWwxXPIGTNin9VmLVI=
|
||||
github.com/alecthomas/kong v1.15.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
|
||||
github.com/alecthomas/kong v1.16.0 h1:g92/kUxBcdcTPOM79yE63viJgtcp5dNyrB3/O2cjYT4=
|
||||
github.com/alecthomas/kong v1.16.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
||||
@@ -23,8 +23,8 @@ github.com/calmh/incontainer v1.0.0 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/
|
||||
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
|
||||
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
|
||||
github.com/calmh/xdr v1.2.0/go.mod h1:vO5+lCx/8xz7Ekd/ZLf+xuy7c1x6FMO1pBJyjDebwyg=
|
||||
github.com/ccding/go-stun v0.1.5 h1:qEM367nnezmj7dv+SdT52prv5x6HUTG3nlrjX5aitlo=
|
||||
github.com/ccding/go-stun v0.1.5/go.mod h1:cCZjJ1J3WFSJV6Wj8Y9Di8JMTsEXh6uv2eNmLzKaUeM=
|
||||
github.com/ccding/go-stun v0.1.6 h1:gWPd1V50Uv2RAnIlmWgoUxjdWvvXp2w1W99oGDtYvzQ=
|
||||
github.com/ccding/go-stun v0.1.6/go.mod h1:cCZjJ1J3WFSJV6Wj8Y9Di8JMTsEXh6uv2eNmLzKaUeM=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
|
||||
@@ -55,10 +55,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ=
|
||||
github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
@@ -128,8 +128,8 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
|
||||
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -147,8 +147,8 @@ github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPb
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.45 h1:6KA/spDguL3KV8rnybG7ezSaE4SeMR3KC9VbUoAQaIk=
|
||||
github.com/mattn/go-sqlite3 v1.14.45/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
|
||||
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0 h1:aOeI7xAOVdK+R6xbVsZuU9HmCZYmQVmZgPf9xJUd2Sg=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0/go.mod h1:0hZWbtfeCYUQeAQdPLUzETiBhUSns7O6LDj9vH88xKA=
|
||||
github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA=
|
||||
@@ -190,22 +190,22 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI=
|
||||
github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI=
|
||||
github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
|
||||
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
|
||||
github.com/rabbitmq/amqp091-go v1.11.0 h1:HxIctVm9Gid/Vtn706necmZ7Wj6pgGI2eqplRbEY8O8=
|
||||
github.com/rabbitmq/amqp091-go v1.11.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rabbitmq/amqp091-go v1.13.0 h1:L8NA1WtF76C6KA3LAoufjfLgbist/If1UQYcsOjtxXA=
|
||||
github.com/rabbitmq/amqp091-go v1.13.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -218,8 +218,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
|
||||
github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
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=
|
||||
@@ -260,20 +260,20 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef h1:LkZ48HFgy/TvhTI0bcWkjgFkgLyKUwcTbDjS0DUjw+A=
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
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=
|
||||
@@ -282,13 +282,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
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.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -311,25 +311,25 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20260610154732-fb80ec83bdd9 h1:FjUup8XrRy7lv+XHONi6KKUSizeF2NnVrTnz/HhbohQ=
|
||||
golang.org/x/telemetry v0.0.0-20260610154732-fb80ec83bdd9/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959 h1:RJhm5l6Fo4rmEIcndxDllNhhf/fAx8qIm4t6A7vpm2A=
|
||||
golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959/go.mod h1:LV7u5Oco+Z/g6XI7PqN+EUUUGGkEcmB1uj2ceI0fOVg=
|
||||
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.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
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=
|
||||
@@ -360,20 +360,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
|
||||
modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
|
||||
modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A=
|
||||
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
|
||||
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
|
||||
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
|
||||
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU=
|
||||
modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs=
|
||||
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
|
||||
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -382,8 +382,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.52.0 h1:p4dhYh2tXZCiyaqHwRVJDjIGKWyXayiQpThxgDzJaxo=
|
||||
modernc.org/sqlite v1.52.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
|
||||
modernc.org/sqlite v1.54.0 h1:JCxR4qwkJvOaqAoYcgDoO25Nc+ROg6EJ2LfBVzdrgog=
|
||||
modernc.org/sqlite v1.54.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -445,6 +445,20 @@ ul.three-columns li, ul.two-columns li {
|
||||
-moz-column-count: 1;
|
||||
column-count: 1;
|
||||
}
|
||||
/* All buttons, except panel headings, get bottom margin, as they
|
||||
won't fit beside each other anymore. Reduce footer padding to
|
||||
compensate for the margin. */
|
||||
.btn:not(.panel-heading),
|
||||
.btn:not(.panel-heading) + .btn:not(.panel-heading) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.modal-footer {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:479px) {
|
||||
@@ -477,20 +491,6 @@ ul.three-columns li, ul.two-columns li {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* All buttons, except panel headings, get bottom margin, as they
|
||||
won't fit beside each other anymore. Reduce footer padding to
|
||||
compensate for the margin. */
|
||||
.btn:not(.panel-heading),
|
||||
.btn:not(.panel-heading) + .btn:not(.panel-heading) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.panel-footer {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.modal-footer {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
table.table-auto td,
|
||||
table.table-auto th,
|
||||
table.table-condensed td,
|
||||
@@ -552,4 +552,4 @@ html[lang|="ko"] i {
|
||||
.select-on-click {
|
||||
-webkit-user-select: all;
|
||||
user-select: all;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,8 @@
|
||||
"Discovery Status": "Discovery Status",
|
||||
"Dismiss": "Dismiss",
|
||||
"Do not add it to the ignore list, so this notification may recur.": "Do not add it to the ignore list, so this notification may recur.",
|
||||
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Do not add it to the ignore list, so this notification will reappear if the device connects again.",
|
||||
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.",
|
||||
"Do not restore": "Do not restore",
|
||||
"Do not restore all": "Do not restore all",
|
||||
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabético",
|
||||
"Altered by ignoring deletes.": "Alterado, ignorando eliminaciones.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Siempre habilitado cuando el tipo de carpeta es \"{{foldertype}}\".",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Siempre habilitado cuando el tipo de carpeta es «{{foldertype}}».",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Un comando externo maneja las versiones. Tienes que eliminar el archivo de la carpeta compartida. Si la ruta a la aplicación contiene espacios, ésta debe estar entre comillas.",
|
||||
"Anonymous Usage Reporting": "Informe de uso anónimo",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe de uso anónimo a cambiado. ¿Le gustaría pasar al nuevo formato?",
|
||||
@@ -54,7 +54,7 @@
|
||||
"Body:": "Contenido:",
|
||||
"Bugs": "Errores",
|
||||
"Cancel": "Cancelar",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No se puede habilitar cuando el tipo de carpeta es \"{{foldertype}}\".",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No se puede habilitar cuando el tipo de carpeta es «{{foldertype}}».",
|
||||
"Changelog": "Registro de cambios",
|
||||
"Clean out after": "Limpiar tras",
|
||||
"Cleaning Versions": "Limpiando versiones",
|
||||
@@ -72,7 +72,7 @@
|
||||
"Connection Management": "Gestión de conexiones",
|
||||
"Connection Type": "Tipo de conexión",
|
||||
"Connections": "Conexiones",
|
||||
"Connections via relays might be rate limited by the relay": "Las conexiones a través de relés pueden estar limitadas por la velocidad del relé",
|
||||
"Connections via relays might be rate limited by the relay": "Las conexiones a través de retransmisores pueden estar limitadas por la velocidad del servidor de retransmisión",
|
||||
"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.": "Ahora está disponible en Syncthing el control de cambios continuo. Se detectarán los cambios en disco y se hará un escaneado sólo en las rutas modificadas. Los beneficios son que los cambios se propagan más rápido y que se requieren menos escaneos completos.",
|
||||
"Copied from elsewhere": "Copiado de otro sitio",
|
||||
"Copied from original": "Copiado del original",
|
||||
@@ -98,7 +98,7 @@
|
||||
"Deselect devices to stop sharing this folder with.": "Deselecciona dispositivos para dejar de compartir esta carpeta.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselecciona carpetas para dejar de compartir con este dispositivo.",
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo «{{name}}» ({{device}} en la dirección {{address}}) quiere conectarse. ¿Añadir nuevo dispositivo?",
|
||||
"Device Certificate": "Certificado del dispositivo",
|
||||
"Device Group": "Grupo dispositivo",
|
||||
"Device ID": "ID del dispositivo",
|
||||
@@ -143,12 +143,12 @@
|
||||
"Enable Relaying": "Habilitar retransmisión",
|
||||
"Enabled": "Activado",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Permite enviar atributos extendidos a otros dispositivos y aplicar atributos extendidos entrantes. Puede ser necesaria la ejecución con privilegios elevados.",
|
||||
"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.": "Permite enviar atributos extendidos a otros dispositivos, pero no aplica los atributos extendidos entrantes. Puede tener impacto en el rendimiento. Siempre se habilita cuando \"Sincronizar atributos extendidos\" está habilitado.",
|
||||
"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.": "Permite enviar atributos extendidos a otros dispositivos, pero no aplica los atributos extendidos entrantes. Puede tener impacto en el rendimiento. Siempre se habilita cuando «Sincronizar atributos extendidos» está habilitado.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Permite enviar información de propietario a otros dispositivos y que se aplique la información de propietario recibida. Puede ser necesaria la ejecución con privilegios elevados.",
|
||||
"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.": "Permite enviar información de propietario a otros dispositivos, pero no aplicar la información de propiedad entrante. Puede tener impacto en el rendimiento. Siempre se habilita cuando \"Sincronizar propietario\" está habilitado.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduce un número no negativo (por ejemplo, \"2.35\") y selecciona una unidad. Los porcentajes son como parte del tamaño total del disco.",
|
||||
"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.": "Permite enviar información de propietario a otros dispositivos, pero no aplicar la información de propiedad entrante. Puede tener impacto en el rendimiento. Siempre se habilita cuando «Sincronizar propietario» está habilitado.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduce un número no negativo (por ejemplo, «2.35») y selecciona una unidad. Los porcentajes se calculan sobre el tamaño total del disco.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Introduce un puerto sin privilegios (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduce direcciones separadas por comas (\"tcp://ip:port\", \"tcp://host:port\") o \"dynamic\" para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduce direcciones separadas por comas («tcp://ip:port», «tcp://host:port») o «dynamic» para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introduce patrones a ignorar, uno por línea.",
|
||||
"Enter up to three octal digits.": "Introduce hasta tres dígitos octales.",
|
||||
"Error": "Error",
|
||||
@@ -177,8 +177,8 @@
|
||||
"Folder Path": "Ruta de la carpeta",
|
||||
"Folder Status": "Estado de la carpeta",
|
||||
"Folder Type": "Tipo de carpeta",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "El tipo de carpeta \"{{receiveEncrypted}}\" sólo puede ser seleccionado al añadir una nueva carpeta.",
|
||||
"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 tipo de carpeta \"{{receiveEncrypted}}\" no se puede cambiar después de añadir la carpeta. Es necesario eliminar la carpeta, eliminar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "El tipo de carpeta «{{receiveEncrypted}}» solo puede ser seleccionado al añadir una nueva carpeta.",
|
||||
"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 tipo de carpeta «{{receiveEncrypted}}» no se puede cambiar después de añadir la carpeta. Es necesario eliminar la carpeta, eliminar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
"Folders": "Carpetas",
|
||||
"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.": "Se ha producido un error al comenzar a buscar cambios para las siguientes carpetas. Se hará un reintento cada minuto, por lo que los errores podrían desaparecer pronto. Si persisten trata de arreglar el problema subyacente o pide ayuda.",
|
||||
"Forever": "Para siempre",
|
||||
@@ -197,7 +197,7 @@
|
||||
"Global Discovery Servers": "Servidores globales de descubrimiento",
|
||||
"Global State": "Estado global",
|
||||
"Help": "Ayuda",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Sugerencia: sólo se detectan reglas para denegar y el valor predeterminado es denegar. Considera añadir \"permitir cualquiera\" como última regla.",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Sugerencia: sólo se detectan reglas para denegar y el valor predeterminado es denegar. Considera añadir «permitir cualquiera» como última regla.",
|
||||
"Home page": "Página de inicio",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo tu configuración actual indica que puedes no querer habilitarlo. Hemos desactivado los informes automáticos de fallos por tí.",
|
||||
"Identification": "Identificador",
|
||||
@@ -219,7 +219,7 @@
|
||||
"Introduced By": "Presentado por",
|
||||
"Introducer": "Presentador",
|
||||
"Introduction": "Introducción",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversión de la condición dada (por ejemplo, \"no excluir\")",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Invertir la condición dada (por ejemplo «no excluir»)",
|
||||
"Keep Versions": "Versiones a conservar",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "Mayor tamaño primero",
|
||||
@@ -324,8 +324,8 @@
|
||||
"Received data is already encrypted": "Los datos recibidos ya están cifrados",
|
||||
"Recent Changes": "Cambios recientes",
|
||||
"Reduced by ignore patterns": "Reducido por patrones a ignorar",
|
||||
"Relay LAN": "Relé LAN",
|
||||
"Relay WAN": "Relé WAN",
|
||||
"Relay LAN": "Retransmisión LAN",
|
||||
"Relay WAN": "Retransmisión WAN",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Las versiones candidatas contienen las últimas funcionalidades y correcciones. Son similares a las tradicionales versiones bisemanales de Syncthing.",
|
||||
"Remote Devices": "Dispositivos Remotos",
|
||||
@@ -400,7 +400,7 @@
|
||||
"Statistics": "Estadísticas",
|
||||
"Stay logged in": "Permanecer conectado",
|
||||
"Stopped": "Detenido",
|
||||
"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.": "Sólo almacena y sincroniza datos cifrados. Las carpetas de todos los dispositivos conectados deben estar configuradas con la misma contraseña o ser del tipo \"{{receiveEncrypted}}\".",
|
||||
"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.": "Solo almacena y sincroniza datos cifrados. Las carpetas de todos los dispositivos conectados deben estar configuradas con la misma contraseña o ser del tipo «{{receiveEncrypted}}».",
|
||||
"Subject:": "Asunto:",
|
||||
"Support": "Soporte",
|
||||
"Support Bundle": "Paquete de soporte",
|
||||
@@ -409,7 +409,7 @@
|
||||
"Sync Protocol Listen Addresses": "Direcciones de escucha del protocolo de sincronización",
|
||||
"Sync Status": "Estado de la sincronización",
|
||||
"Syncing": "Sincronizando",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "ID del dispositivo de sincronización para \"{{devicename}}\"",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "ID del dispositivo de sincronización para «{{devicename}}»",
|
||||
"Syncthing has been shut down.": "Syncthing se ha detenido.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing incluye el siguiente software o partes de él:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es software libre de código abierto con licencia MPL v2.0.",
|
||||
@@ -432,7 +432,7 @@
|
||||
"The cleanup interval cannot be blank.": "El intervalo de limpieza no puede estar vacío.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido guardada pero no activada. Syncthing debe reiniciarse para activar la nueva configuración.",
|
||||
"The device ID cannot be blank.": "El ID del dispositivo no puede estar vacío.",
|
||||
"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).": "El ID del dispositivo a introducir aquí se puede encontrar en el diálogo \"Acciones > Mostrar ID\" del otro dispositivo. Los espacios y barras son opcionales (ignorados).",
|
||||
"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).": "El ID del dispositivo a introducir aquí se puede encontrar en el diálogo «Acciones > Mostrar ID» del otro dispositivo. Los espacios y barras son opcionales (ignorados).",
|
||||
"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.": "El informe encriptado de uso se envía diariamente. Se usa para rastrear plataformas comunes, tamaños de carpetas y versiones de la aplicación. Si el conjunto de datos enviados en el informes se cambia, se le pedirá a usted autorización de nuevo.",
|
||||
"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 dispositivo introducido no parece válido. Debe ser una cadena de 52 ó 56 caracteres formada por letras y números, con espacios y guiones opcionales.",
|
||||
"The folder ID cannot be blank.": "El ID de la carpeta no puede estar vacío.",
|
||||
@@ -473,7 +473,7 @@
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Determina el espacio libre necesario en el disco principal (con la. base de datos de índices).",
|
||||
"Time": "Hora",
|
||||
"Time the item was last modified": "Hora de última modificación del elemento",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Para conectarse con el dispositivo Syncthing llamado \"{{devicename}}\", añada un nuevo dispositivo remoto en su extremo con este ID:",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Para conectarse con el dispositivo Syncthing llamado «{{devicename}}», añada un nuevo dispositivo remoto en su extremo con este ID:",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Para permitir una regla, marca la casilla. Para denegar una regla, déjala sin marcar.",
|
||||
"Today": "Hoy",
|
||||
"Trash Can": "Papelera",
|
||||
@@ -516,10 +516,10 @@
|
||||
"Waiting to Scan": "Esperando para escanear",
|
||||
"Waiting to Sync": "Esperando para sincronizar",
|
||||
"Warning": "Atención",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un directorio principal de la carpeta ya existente \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, esta ruta es una carpeta principal de una carpeta existente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un directorio principal de la carpeta ya existente «{{otherFolder}}».",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, esta ruta es una carpeta principal de una carpeta existente «{{otherFolderLabel}}» ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un subdirectorio de una carpeta ya existente llamada «{{otherFolder}}».",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, esta ruta es un subdirectorio de una carpeta ya existente llamada «{{otherFolderLabel}}» ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Atención: si estás utilizando un observador externo como {{syncthingInotify}}, debes asegurarte de que está desactivado.",
|
||||
"Watch for Changes": "Detectar cambios",
|
||||
"Watching for Changes": "Detectando cambios",
|
||||
@@ -537,7 +537,7 @@
|
||||
"You have no ignored folders.": "No hay carpetas ignoradas.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Hay cambios sin guardar. ¿Estás seguro de que quieres descartarlos?",
|
||||
"You must keep at least one version.": "Debes conservar al menos una versión.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debes añadir o cambiar nada localmente en una carpeta \"{{receiveEncrypted}}\".",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debes añadir o cambiar nada localmente en una carpeta «{{receiveEncrypted}}».",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Tu aplicación de SMS debería abrirse para permitirte elegir el destinatario y enviarlo desde tu propio número.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Tu aplicación de email debería abrirse para permitirte elegir el destinatario y enviarlo desde tu propia dirección.",
|
||||
"days": "días",
|
||||
@@ -561,7 +561,7 @@
|
||||
}
|
||||
},
|
||||
"unknown device": "dispositivo desconocido",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta «{{folder}}».",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta «{{folderlabel}}» ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} puede presentar de nuevo este dispositivo."
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Crea o condividi automaticamente le cartelle che questo dispositivo presenta sul percorso predefinito.",
|
||||
"Available debug logging facilities:": "Servizi di debug disponibili:",
|
||||
"Be careful!": "Fai attenzione!",
|
||||
"Block Indexing": "Indicizzazione a blocchi",
|
||||
"Body:": "Corpo:",
|
||||
"Bugs": "Bug",
|
||||
"Cancel": "Annulla",
|
||||
@@ -99,6 +100,7 @@
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Il dispositivo \"{{name}}\" ({{device}} - {{address}}) chiede di connettersi. Aggiungere il nuovo dispositivo?",
|
||||
"Device Certificate": "Certificato del dispositivo",
|
||||
"Device Group": "Gruppo del Dispositivo",
|
||||
"Device ID": "ID Dispositivo",
|
||||
"Device Identification": "Identificazione Dispositivo",
|
||||
"Device Name": "Nome Dispositivo",
|
||||
@@ -169,6 +171,7 @@
|
||||
"Filter by date": "Filtra per data",
|
||||
"Filter by name": "Filtra per nome",
|
||||
"Folder": "Cartella",
|
||||
"Folder Group": "Gruppo della Cartella",
|
||||
"Folder ID": "ID Cartella",
|
||||
"Folder Label": "Etichetta per la Cartella",
|
||||
"Folder Path": "Percorso Cartella",
|
||||
@@ -249,6 +252,7 @@
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Visualizzazione log in pausa. Scorri fino in fondo per continuare.",
|
||||
"Login failed, see Syncthing logs for details.": "Accesso non riuscito, vedi i log di Syncthing per i dettagli.",
|
||||
"Logs": "Log",
|
||||
"Maintain an index of all blocks in the folder, enabling reuse of blocks from other files when syncing changes. Disable to reduce database size at the cost of not being able to reuse blocks across files.": "Mantiene un indice di tutti i blocchi nella cartella, consentendo il riutilizzo dei blocchi da altri file durante la sincronizzazione delle modifiche. Disabilitare questa opzione per ridurre le dimensioni del database, a costo di non poter riutilizzare i blocchi tra i file.",
|
||||
"Major Upgrade": "Aggiornamento Principale",
|
||||
"Mass actions": "Azioni di massa",
|
||||
"Maximum Age": "Durata Massima",
|
||||
@@ -279,6 +283,8 @@
|
||||
"Off": "Disattiva",
|
||||
"Oldest First": "Prima il Meno Recente",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Etichetta descrittiva facoltativa della cartella. Può essere diversa su ogni dispositivo.",
|
||||
"Optional group for the device. Can be different on each device.": "Gruppo opzionale per il dispositivo. Può essere diverso per ogni dispositivo.",
|
||||
"Optional group for the folder. Can be different on each device.": "Gruppo opzionale per la cartella. Può essere diverso su ciascun dispositivo.",
|
||||
"Options": "Opzioni",
|
||||
"Out of Sync": "Non sincronizzato",
|
||||
"Out of Sync Items": "Elementi Non Sincronizzati",
|
||||
@@ -390,6 +396,7 @@
|
||||
"Staggered": "Sfalsato",
|
||||
"Staggered File Versioning": "Controllo Versione Cadenzato",
|
||||
"Start Browser": "Avvia Browser",
|
||||
"Starting": "In avvio",
|
||||
"Statistics": "Statistiche",
|
||||
"Stay logged in": "Resta connesso",
|
||||
"Stopped": "Fermato",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Автоматически создавать, используя путь по умолчанию, или делиться папками, о которых сообщает это устройство.",
|
||||
"Available debug logging facilities:": "Доступные средства отладочного журнала:",
|
||||
"Be careful!": "Будьте осторожны!",
|
||||
"Block Indexing": "Блочная индексация",
|
||||
"Body:": "Тело:",
|
||||
"Bugs": "Ошибки",
|
||||
"Cancel": "Отмена",
|
||||
@@ -99,6 +100,7 @@
|
||||
"Device": "Устройство",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство «{{name}}» ({{device}} на {{address}}) хочет подключиться. Добавить новое устройство?",
|
||||
"Device Certificate": "Сертификат устройства",
|
||||
"Device Group": "Группа устройства",
|
||||
"Device ID": "ID устройства",
|
||||
"Device Identification": "Идентификация устройства",
|
||||
"Device Name": "Имя устройства",
|
||||
@@ -132,9 +134,9 @@
|
||||
"Downloading": "Загрузка",
|
||||
"Edit": "Редактировать",
|
||||
"Edit Device": "Редактирование устройства",
|
||||
"Edit Device Defaults": "Изменить умолчания устройства",
|
||||
"Edit Device Defaults": "Изменить настройки устройств",
|
||||
"Edit Folder": "Редактирование папки",
|
||||
"Edit Folder Defaults": "Изменить умолчания папки",
|
||||
"Edit Folder Defaults": "Изменить настройки папок",
|
||||
"Editing {%path%}.": "Правка {{path}}.",
|
||||
"Enable Crash Reporting": "Включить отчёты о сбоях",
|
||||
"Enable NAT traversal": "Использовать обход NAT",
|
||||
@@ -169,8 +171,9 @@
|
||||
"Filter by date": "Отфильтровать по дате",
|
||||
"Filter by name": "Отфильтровать по имени",
|
||||
"Folder": "Папка",
|
||||
"Folder Group": "Группа папки",
|
||||
"Folder ID": "ID папки",
|
||||
"Folder Label": "Ярлык папки",
|
||||
"Folder Label": "Название папки",
|
||||
"Folder Path": "Путь к папке",
|
||||
"Folder Status": "Статус папки",
|
||||
"Folder Type": "Тип папки",
|
||||
@@ -182,8 +185,8 @@
|
||||
"Full Rescan Interval (s)": "Интервал полного сканирования (в секундах)",
|
||||
"GUI": "Интерфейс",
|
||||
"GUI / API HTTPS Certificate": "HTTPS-сертификат GUI/API",
|
||||
"GUI Authentication Password": "Пароль для доступа к панели управления",
|
||||
"GUI Authentication User": "Имя пользователя для доступа к панели управления",
|
||||
"GUI Authentication Password": "Пароль",
|
||||
"GUI Authentication User": "Имя пользователя",
|
||||
"GUI Authentication: Set User and Password": "GUI аутентификация: Установите имя пользователя и пароль",
|
||||
"GUI Listen Address": "Адрес GUI",
|
||||
"GUI Override Directory": "Каталог переопределения графического интерфейса",
|
||||
@@ -249,6 +252,7 @@
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Вывод журнала приостановлен. Прокрутите вниз, чтобы продолжить.",
|
||||
"Login failed, see Syncthing logs for details.": "Не удалось войти в систему. Посмотреть подробности можно в журнале Syncthing.",
|
||||
"Logs": "Журналы",
|
||||
"Maintain an index of all blocks in the folder, enabling reuse of blocks from other files when syncing changes. Disable to reduce database size at the cost of not being able to reuse blocks across files.": "Использовать индекс всех блоков в папке. Это позволит повторно использовать блоки из других файлов при синхронизации изменений. Отключите эту функцию, чтобы уменьшить размер базы данных. При этом вы не сможете повторно использовать блоки из разных файлов.",
|
||||
"Major Upgrade": "Обновление основной версии",
|
||||
"Mass actions": "Массовые действия",
|
||||
"Maximum Age": "Максимальный срок",
|
||||
@@ -271,14 +275,16 @@
|
||||
"No File Versioning": "Без управления версиями файлов",
|
||||
"No files will be deleted as a result of this operation.": "В результате этой операции никакие файлы не будут удалены.",
|
||||
"No rules set": "Правила не заданы",
|
||||
"No upgrades": "Нет обновлений",
|
||||
"No upgrades": "Выключено",
|
||||
"Not shared": "Нет общего доступа",
|
||||
"Notice": "Внимание",
|
||||
"Number of Connections": "Количество подключений",
|
||||
"OK": "ОК",
|
||||
"Off": "Отключить",
|
||||
"Oldest First": "Сначала старые",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Необязательное описательное название папки. Может различаться на разных устройствах.",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Необязательное название папки. Может различаться на разных устройствах.",
|
||||
"Optional group for the device. Can be different on each device.": "Необязательная группа для устройства. Может отличаться для каждого устройства.",
|
||||
"Optional group for the folder. Can be different on each device.": "Необязательная группа для папки. Может отличаться на каждом устройстве.",
|
||||
"Options": "Настройки",
|
||||
"Out of Sync": "Нет синхронизации",
|
||||
"Out of Sync Items": "Несинхронизированные элементы",
|
||||
@@ -398,7 +404,7 @@
|
||||
"Support": "Поддержка",
|
||||
"Support Bundle": "Данные для поддержки",
|
||||
"Sync Extended Attributes": "Синхронизировать расширенные атрибуты",
|
||||
"Sync Ownership": "Синхронизация владений",
|
||||
"Sync Ownership": "Синхронизировать владельца",
|
||||
"Sync Protocol Listen Addresses": "Адрес протокола синхронизации",
|
||||
"Sync Status": "Состояние синхронизации",
|
||||
"Syncing": "Синхронизация",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"A device with that ID is already added.": "En enhet med detta ID har redan lagts till.",
|
||||
"A negative number of days doesn't make sense.": "Ett negativt antal dagar är inte rimligt.",
|
||||
"A new major version may not be compatible with previous versions.": "En ny huvudversion kan eventuellt vara inkompatibel med tidigare versioner.",
|
||||
"A new major version may not be compatible with previous versions.": "En ny huvudversion kanske inte är kompatibel med tidigare versioner.",
|
||||
"API Key": "API-nyckel",
|
||||
"About": "Om",
|
||||
"Action": "Åtgärd",
|
||||
@@ -15,22 +15,22 @@
|
||||
"Add filter entry": "Lägg till filterregel",
|
||||
"Add ignore patterns": "Lägg till ignoreringsmönster",
|
||||
"Add new folder?": "Lägg till ny mapp?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dessutom kommer det fullständiga återkommande skanningsintervallet att höjas (60 gånger, d.v.s. ny standard på 1h). Du kan också konfigurera den manuellt för varje mapp senare efter att du har valt Nej.",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dessutom förlängs intervallet för fullständig omskanning 60 gånger, till det nya standardvärdet 1 timme. Du kan också konfigurera det manuellt för varje mapp senare efter att du har valt Nej.",
|
||||
"Address": "Adress",
|
||||
"Addresses": "Adresser",
|
||||
"Advanced": "Avancerat",
|
||||
"Advanced Configuration": "Avancerad konfiguration",
|
||||
"All Data": "Alla data",
|
||||
"All Time": "All tid",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alla mappar som delas med denna enhet måste skyddas av ett lösenord, så att alla skickade data är oläsliga utan det angivna lösenordet.",
|
||||
"Allow Anonymous Usage Reporting?": "Tillåt anonym användarstatistiksrapportering?",
|
||||
"All Time": "Hela perioden",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alla mappar som delas med den här enheten måste skyddas av ett lösenord, så att alla skickade data är oläsliga utan det angivna lösenordet.",
|
||||
"Allow Anonymous Usage Reporting?": "Tillåt anonym användningsrapportering?",
|
||||
"Allowed Networks": "Tillåtna nätverk",
|
||||
"Alphabetic": "Alfabetisk",
|
||||
"Altered by ignoring deletes.": "Ändrad genom att ignorera borttagningar.",
|
||||
"Altered by ignoring deletes.": "Ändrad eftersom borttagningar ignoreras.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Alltid på när mapptypen är \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ett externt kommando hanterar versionen. Det måste ta bort filen från den delade mappen. Om sökvägen till applikationen innehåller mellanslag bör den citeras.",
|
||||
"Anonymous Usage Reporting": "Anonym användarstatistiksrapportering",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymt användningsrapportformat har ändrats. Vill du flytta till det nya formatet?",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ett externt kommando hanterar versionshanteringen. Det måste ta bort filen från den delade mappen. Om sökvägen till programmet innehåller mellanslag ska den omges av citattecken.",
|
||||
"Anonymous Usage Reporting": "Anonym användningsrapportering",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatet för anonym användningsrapportering har ändrats. Vill du byta till det nya formatet?",
|
||||
"Applied to LAN": "Tillämpas på LAN",
|
||||
"Apply": "Tillämpa",
|
||||
"Are you sure you want to override all remote changes?": "Är du säker på att du vill åsidosätta alla fjärrändringar?",
|
||||
@@ -46,18 +46,18 @@
|
||||
"Automatic Crash Reporting": "Automatisk kraschrapportering",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisk uppgradering erbjuder nu valet mellan stabila utgåvor och utgåvskandidater.",
|
||||
"Automatic upgrades": "Automatiska uppgraderingar",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatiska uppgraderingar är alltid aktiverade för kandidatutgåvor.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Skapa eller dela automatiskt mappar som denna enhet annonserar på standardsökvägen.",
|
||||
"Available debug logging facilities:": "Tillgängliga felsökningsfunktioner:",
|
||||
"Be careful!": "Var aktsam!",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatiska uppgraderingar är alltid aktiverade för utgåvskandidater.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Skapa eller dela automatiskt mappar som enheten erbjuder, med standardsökvägen.",
|
||||
"Available debug logging facilities:": "Tillgängliga funktioner för felsökningsloggning:",
|
||||
"Be careful!": "Var försiktig!",
|
||||
"Block Indexing": "Blockindexering",
|
||||
"Body:": "Meddelande:",
|
||||
"Bugs": "Felrapporter",
|
||||
"Cancel": "Avbryt",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kan inte aktiveras när mapptypen är \"{{foldertype}}\".",
|
||||
"Changelog": "Ändringslogg",
|
||||
"Clean out after": "Rensa efteråt",
|
||||
"Cleaning Versions": "Rensningsversioner",
|
||||
"Clean out after": "Rensa efter",
|
||||
"Cleaning Versions": "Rensar versioner",
|
||||
"Cleanup Interval": "Rensningsintervall",
|
||||
"Click to see full identification string and QR code.": "Klicka för att se fullständig identifieringssträng och QR-kod.",
|
||||
"Close": "Stäng",
|
||||
@@ -68,12 +68,12 @@
|
||||
"Configuration File": "Konfigurationsfil",
|
||||
"Configured": "Konfigurerad",
|
||||
"Connected (Unused)": "Ansluten (oanvänd)",
|
||||
"Connection Error": "Anslutningsproblem",
|
||||
"Connection Error": "Anslutningsfel",
|
||||
"Connection Management": "Anslutningshantering",
|
||||
"Connection Type": "Anslutningstyp",
|
||||
"Connections": "Anslutningar",
|
||||
"Connections via relays might be rate limited by the relay": "Anslutningar via reläer kan begränsas av reläen",
|
||||
"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.": "Kontinuerlig bevakning av ändringar är nu tillgängligt i Syncthing. Detta kommer att upptäcka ändringar på disken och utfärda en skanning på endast de ändrade sökvägarna. Fördelarna är att ändringar delas snabbare och att mindre fullständiga skanningar krävs.",
|
||||
"Connections via relays might be rate limited by the relay": "Anslutningar via reläer kan hastighetsbegränsas av reläet",
|
||||
"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.": "Kontinuerlig bevakning av ändringar är nu tillgänglig i Syncthing. Ändringar på disken upptäcks och endast de ändrade sökvägarna skannas. Det gör att ändringar sprids snabbare och att färre fullständiga skanningar behövs.",
|
||||
"Copied from elsewhere": "Kopierat från annanstans",
|
||||
"Copied from original": "Kopierat från original",
|
||||
"Copied!": "Kopierat!",
|
||||
@@ -89,32 +89,32 @@
|
||||
"Default Configuration": "Standardkonfiguration",
|
||||
"Default Device": "Standardenhet",
|
||||
"Default Folder": "Standardmapp",
|
||||
"Default Ignore Patterns": "Standard ignoreringsmönster",
|
||||
"Default Ignore Patterns": "Standardignoreringsmönster",
|
||||
"Defaults": "Standard",
|
||||
"Delete": "Ta bort",
|
||||
"Delete Unexpected Items": "Ta bort oväntade objekt",
|
||||
"Deleted {%file%}": "Tog bort {{file}}",
|
||||
"Deselect All": "Avmarkera alla",
|
||||
"Deselect devices to stop sharing this folder with.": "Avmarkera enheter som du vill sluta dela denna mapp med.",
|
||||
"Deselect folders to stop sharing with this device.": "Avmarkera mappar för att sluta dela med denna enhet.",
|
||||
"Deselect devices to stop sharing this folder with.": "Avmarkera de enheter som mappen inte längre ska delas med.",
|
||||
"Deselect folders to stop sharing with this device.": "Avmarkera de mappar som inte längre ska delas med den här enheten.",
|
||||
"Device": "Enhet",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) vill ansluta. Lägg till ny enhet?",
|
||||
"Device Certificate": "Enhetscertifikat",
|
||||
"Device Group": "Enhetsgrupp",
|
||||
"Device ID": "Enhets-ID",
|
||||
"Device Identification": "Enhetens identifikation",
|
||||
"Device Identification": "Enhetsidentifiering",
|
||||
"Device Name": "Enhetsnamn",
|
||||
"Device Status": "Enhetsstatus",
|
||||
"Device is untrusted, enter encryption password": "Enheten är otillförlitlig, ange krypteringslösenord",
|
||||
"Device rate limits": "Enhetshastighetsgränser",
|
||||
"Device is untrusted, enter encryption password": "Enheten är inte betrodd. Ange krypteringslösenordet.",
|
||||
"Device rate limits": "Hastighetsgränser för enheten",
|
||||
"Device that last modified the item": "Enhet som senast ändrade objektet",
|
||||
"Devices": "Enheter",
|
||||
"Disable Crash Reporting": "Inaktivera kraschrapportering",
|
||||
"Disabled": "Inaktiverad",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Inaktiverad periodisk skanning och inaktiverad bevakning av ändringar",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Inaktiverad periodisk skanning och aktiverad bevakning av ändringar",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Inaktiverad periodisk skanning och misslyckades med att ställa in bevakning av ändringar, försöker igen var 1:e minut:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Inaktiverar att jämföra och synkronisera filbehörigheter. Användbart för system med obefintliga eller anpassade behörigheter (t.ex. FAT, exFAT, Synology, Android).",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Periodisk skanning och bevakning av ändringar är inaktiverade",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Periodisk skanning är inaktiverad och bevakning av ändringar är aktiverad",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning är inaktiverad och bevakning av ändringar kunde inte konfigureras. Ett nytt försök görs varje minut:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Inaktiverar jämförelse och synkronisering av filbehörigheter. Användbart på system utan behörigheter eller med anpassade behörigheter, exempelvis FAT, exFAT, Synology och Android.",
|
||||
"Discard": "Kassera",
|
||||
"Disconnected": "Frånkopplad",
|
||||
"Disconnected (Inactive)": "Frånkopplad (inaktiv)",
|
||||
@@ -131,43 +131,43 @@
|
||||
"Documentation": "Dokumentation",
|
||||
"Download Rate": "Hämtningshastighet",
|
||||
"Downloaded": "Hämtat",
|
||||
"Downloading": "Tar emot",
|
||||
"Downloading": "Hämtar",
|
||||
"Edit": "Redigera",
|
||||
"Edit Device": "Redigera enhet",
|
||||
"Edit Device Defaults": "Redigera enhetsstandard",
|
||||
"Edit Device Defaults": "Redigera standardvärden för enheter",
|
||||
"Edit Folder": "Redigera mapp",
|
||||
"Edit Folder Defaults": "Redigera mappstandard",
|
||||
"Edit Folder Defaults": "Redigera standardvärden för mappar",
|
||||
"Editing {%path%}.": "Redigerar {{path}}.",
|
||||
"Enable Crash Reporting": "Aktivera kraschrapportering",
|
||||
"Enable NAT traversal": "Aktivera NAT-genompassering",
|
||||
"Enable Relaying": "Aktivera vidarebefordring",
|
||||
"Enable NAT traversal": "Aktivera NAT-traversering",
|
||||
"Enable Relaying": "Aktivera reläförmedling",
|
||||
"Enabled": "Aktiverad",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Möjliggör sändning av utökade attribut till andra enheter, och tillämpning av inkommande utökade attribut. Kan kräva körning med förhöjda behörigheter.",
|
||||
"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.": "Möjliggör sändning av utökade attribut till andra enheter, men inte att tillämpa inkommande utökade attribut. Detta kan ha en betydande inverkan på prestanda. Alltid aktiverad när \"Synkronisera utökade attribut\" är aktiverat.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Möjliggör att skicka ägarinformation till andra enheter och tillämpa inkommande ägarinformation. Kräver vanligtvis körning med förhöjda behörigheter.",
|
||||
"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.": "Möjliggör att skicka ägarinformation till andra enheter, men inte tillämpa inkommande ägarinformation. Detta kan ha en betydande inverkan på prestanda. Alltid aktiverat när \"Synkronisera ägande\" är aktiverat.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Ange ett icke-negativt antal (t.ex., \"2.35\") och välj en enhet. Procenttalen är som en del av den totala diskstorleken.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Ange ett icke-privilegierat portnummer (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Ange kommaseparerade (\"tcp://ip:port\", \"tcp://host:port\") adresser eller \"dynamic\" för att utföra automatisk upptäckt av adressen.",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Gör det möjligt att skicka utökade attribut till andra enheter och tillämpa inkommande utökade attribut. Kan kräva körning med utökade behörigheter.",
|
||||
"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.": "Gör det möjligt att skicka utökade attribut till andra enheter utan att tillämpa inkommande utökade attribut. Det kan påverka prestandan avsevärt. Funktionen är alltid aktiverad när ”Synkronisera utökade attribut” är aktiverat.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Gör det möjligt att skicka ägarinformation till andra enheter och tillämpa inkommande ägarinformation. Kräver vanligtvis körning med utökade behörigheter.",
|
||||
"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.": "Gör det möjligt att skicka ägarinformation till andra enheter utan att tillämpa inkommande ägarinformation. Det kan påverka prestandan avsevärt. Funktionen är alltid aktiverad när ”Synkronisera ägarskap” är aktiverat.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Ange ett icke-negativt tal, t.ex. \"2.35\", och välj en enhet. Procenttal räknas som en andel av den totala diskstorleken.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Ange ett oprivilegierat portnummer (1024–65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Ange kommaseparerade adresser (\"tcp://ip:port\", \"tcp://host:port\") eller \"dynamic\" för automatisk identifiering av adressen.",
|
||||
"Enter ignore patterns, one per line.": "Ange ignoreringsmönster, en per rad.",
|
||||
"Enter up to three octal digits.": "Ange upp till tre oktala siffror.",
|
||||
"Error": "Fel",
|
||||
"Extended Attributes": "Utökade attribut",
|
||||
"Extended Attributes Filter": "Utökat attributfilter",
|
||||
"Extended Attributes Filter": "Filter för utökade attribut",
|
||||
"External": "Extern",
|
||||
"External File Versioning": "Extern filversionshantering",
|
||||
"Failed Items": "Misslyckade objekt",
|
||||
"Failed to load file versions.": "Det gick inte att läsa in filversioner.",
|
||||
"Failed to load ignore patterns.": "Det gick inte att läsa in ignoreringsmönster.",
|
||||
"Failed to set up, retrying": "Det gick inte att ställa in, försöker igen",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Det går inte att ansluta till IPv6-servrar om det inte finns någon IPv6-anslutning.",
|
||||
"File Pull Order": "Filhämtningsprioritering",
|
||||
"Failed to set up, retrying": "Det gick inte att konfigurera, försöker igen",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Det är normalt att anslutningar till IPv6-servrar misslyckas om IPv6-anslutning saknas.",
|
||||
"File Pull Order": "Ordning för filhämtning",
|
||||
"File Versioning": "Filversionshantering",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Filer flyttas till .stversions-mappen när de ersätts eller tas bort av Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Filer flyttas till datumstämplade versioner i en .stversions-mapp när de ersätts eller tas bort av Syncthing.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer är skyddade från ändringar som görs på andra enheter, men ändringar som görs på den här enheten skickas till resten av klustret.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Filer synkroniseras från klustret, men alla ändringar som görs lokalt skickas inte till andra enheter.",
|
||||
"Filesystem Watcher Errors": "Fel i filsystembevakaren",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Filer synkroniseras från klustret, men inga lokala ändringar skickas till andra enheter.",
|
||||
"Filesystem Watcher Errors": "Fel vid bevakning av filsystemet",
|
||||
"Filter by date": "Filtrera efter datum",
|
||||
"Filter by name": "Filtrera efter namn",
|
||||
"Folder": "Mapp",
|
||||
@@ -177,19 +177,19 @@
|
||||
"Folder Path": "Mappsökväg",
|
||||
"Folder Status": "Mappstatus",
|
||||
"Folder Type": "Mapptyp",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mapptypen \"{{receiveEncrypted}}\" kan bara ställas in vid tilläggning av en ny mapp.",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mapptypen \"{{receiveEncrypted}}\" kan bara ställas in när en ny mapp läggs till.",
|
||||
"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.": "Mapptypen \"{{receiveEncrypted}}\" kan inte ändras efter att mappen har lagts till. Du måste ta bort mappen, ta bort eller dekryptera data på disken och lägga till mappen igen.",
|
||||
"Folders": "Mappar",
|
||||
"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.": "För följande mappar uppstod ett fel när du började bevaka ändringar. Det kommer att omförsökas varje minut, så felen kan försvinna snart. Om de fortsätter, försök att åtgärda det underliggande problemet och fråga om hjälp om du inte kan.",
|
||||
"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.": "För följande mappar uppstod ett fel när bevakning av ändringar skulle startas. Försöket upprepas varje minut, så felen kan snart försvinna. Om de fortsätter, försök att åtgärda det underliggande problemet och fråga om hjälp om du inte kan.",
|
||||
"Forever": "För alltid",
|
||||
"Full Rescan Interval (s)": "Intervall för fullständig omskanning (s)",
|
||||
"GUI": "Grafiskt användargränssnitt",
|
||||
"GUI / API HTTPS Certificate": "HTTPS-certifikat för gränssnitt / API",
|
||||
"GUI / API HTTPS Certificate": "HTTPS-certifikat för gränssnitt och API",
|
||||
"GUI Authentication Password": "Autentiseringslösenord för gränssnitt",
|
||||
"GUI Authentication User": "Autentiseringsanvändare för gränssnitt",
|
||||
"GUI Authentication: Set User and Password": "Autentisering för det grafiska gränssnittet: Ställ in användare och lösenord",
|
||||
"GUI Authentication: Set User and Password": "Autentisering för det grafiska gränssnittet: ange användarnamn och lösenord",
|
||||
"GUI Listen Address": "Lyssnaradress för gränssnitt",
|
||||
"GUI Override Directory": "Åsidosätt mapp för gränssnitt",
|
||||
"GUI Override Directory": "Mapp som åsidosätter gränssnittet",
|
||||
"GUI Theme": "Tema för gränssnitt",
|
||||
"General": "Allmänt",
|
||||
"Generate": "Generera",
|
||||
@@ -197,16 +197,16 @@
|
||||
"Global Discovery Servers": "Globala annonseringsservrar",
|
||||
"Global State": "Globalt tillstånd",
|
||||
"Help": "Hjälp",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tips: endast neka-regler upptäcktes medan standard är neka. Överväg att lägga till \"tillåt alla\" som sista regel.",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tips: Endast nekande regler hittades samtidigt som standardåtgärden är att neka. Överväg att lägga till ”tillåt alla” som sista regel.",
|
||||
"Home page": "Webbplats",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar indikerar dock att du kanske inte vill att ha det aktiverat. Vi har inaktiverat automatisk kraschrapportering för dig.",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar tyder dock på att du kanske inte vill ha det aktiverat. Vi har inaktiverat automatisk kraschrapportering för dig.",
|
||||
"Identification": "Identifiering",
|
||||
"If untrusted, enter encryption password": "Om opålitlig, ange krypteringslösenord",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Om du vill förhindra att andra användare på denna dator får åtkomst till Syncthing och dina filer via det, överväg att konfigurera autentisering.",
|
||||
"If untrusted, enter encryption password": "Om enheten inte är betrodd anger du krypteringslösenordet",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Om du vill förhindra att andra användare på den här datorn får åtkomst till Syncthing och därigenom dina filer bör du konfigurera autentisering.",
|
||||
"Ignore": "Ignorera",
|
||||
"Ignore Patterns": "Ignoreringsmönster",
|
||||
"Ignore Permissions": "Ignorera rättigheter",
|
||||
"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.": "Ignoreringsmönster kan bara läggas till efter att mappen har skapats. Om markerad kommer ett inmatningsfält för att ange ignoreringsmönster att visas efter att du har sparat.",
|
||||
"Ignore Permissions": "Ignorera behörigheter",
|
||||
"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.": "Ignoreringsmönster kan endast läggas till efter att mappen har skapats. Om alternativet markeras visas ett inmatningsfält för ignoreringsmönster efter att du har sparat.",
|
||||
"Ignored Devices": "Ignorerade enheter",
|
||||
"Ignored Folders": "Ignorerade mappar",
|
||||
"Ignored at": "Ignorerad vid",
|
||||
@@ -214,12 +214,12 @@
|
||||
"Incoming Rate Limit (KiB/s)": "Ingående hastighetsgräns (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Felaktig konfiguration kan skada innehållet i mappen och få Syncthing att sluta fungera.",
|
||||
"Incorrect user name or password.": "Felaktigt användarnamn eller lösenord.",
|
||||
"Info": "Info",
|
||||
"Info": "Information",
|
||||
"Internally used paths:": "Internt använda sökvägar:",
|
||||
"Introduced By": "Introducerad av",
|
||||
"Introducer": "Introduktör",
|
||||
"Introduction": "Introduktion",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversion av det givna tillståndet (d.v.s. exkluderar inte)",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Omvändning av det angivna villkoret, det vill säga exkludera inte",
|
||||
"Keep Versions": "Behåll versioner",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "Största först",
|
||||
@@ -236,9 +236,9 @@
|
||||
"Listener Failures": "Lyssnarfel",
|
||||
"Listener Status": "Lyssnarstatus",
|
||||
"Listeners": "Lyssnare",
|
||||
"Loading data...": "Läser in data...",
|
||||
"Loading...": "Läser in...",
|
||||
"Local Additions": "Lokalt tillägg",
|
||||
"Loading data...": "Läser in data…",
|
||||
"Loading...": "Läser in…",
|
||||
"Local Additions": "Lokala tillägg",
|
||||
"Local Discovery": "Lokal annonsering",
|
||||
"Local State": "Lokalt tillstånd",
|
||||
"Local State (Total)": "Lokalt tillstånd (totalt)",
|
||||
@@ -249,11 +249,11 @@
|
||||
"Log Out": "Logga ut",
|
||||
"Log in to see paths information.": "Logga in för att se sökvägsinformation.",
|
||||
"Log in to see version information.": "Logga in för att se versionsinformation.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Loggning pausad. Bläddra till botten för att fortsätta.",
|
||||
"Login failed, see Syncthing logs for details.": "Inloggningen misslyckades, se Syncthing-loggarna för detaljer.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Den automatiska loggrullningen har pausats. Rulla längst ned för att fortsätta.",
|
||||
"Login failed, see Syncthing logs for details.": "Inloggningen misslyckades. Mer information finns i Syncthings loggar.",
|
||||
"Logs": "Loggar",
|
||||
"Maintain an index of all blocks in the folder, enabling reuse of blocks from other files when syncing changes. Disable to reduce database size at the cost of not being able to reuse blocks across files.": "Upprätthåll ett index över alla block i mappen, vilket möjliggör återanvändning av block från andra filer vid synkronisering av ändringar. Inaktivera för att minska databasens storlek på bekostnad av att inte kunna återanvända block mellan filer.",
|
||||
"Major Upgrade": "Större uppgradering",
|
||||
"Major Upgrade": "Uppgradering av huvudversion",
|
||||
"Mass actions": "Massåtgärder",
|
||||
"Maximum Age": "Högsta ålder",
|
||||
"Maximum single entry size": "Största tillåtna poststorlek",
|
||||
@@ -262,19 +262,19 @@
|
||||
"Minimum Free Disk Space": "Minsta lediga diskutrymme",
|
||||
"Mod. Device": "Enhet som utförde ändring",
|
||||
"Mod. Time": "Tid för ändring",
|
||||
"More than a month ago": "Mer än en månad sedan",
|
||||
"More than a month ago": "För mer än en månad sedan",
|
||||
"More than a week ago": "För mer än en vecka sedan",
|
||||
"More than a year ago": "Mer än ett år sedan",
|
||||
"Move to top of queue": "Flytta till överst i kön",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Flernivå jokertecken (matchar flera mappnivåer)",
|
||||
"Move to top of queue": "Flytta längst upp i kön",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Jokertecken på flera nivåer (matchar flera mappnivåer)",
|
||||
"Never": "Aldrig",
|
||||
"New Device": "Ny enhet",
|
||||
"New Folder": "Ny mapp",
|
||||
"Newest First": "Nyaste först",
|
||||
"No": "Nej",
|
||||
"No File Versioning": "Ingen filversionshantering",
|
||||
"No files will be deleted as a result of this operation.": "Inga filer kommer att tas bort till följd av denna operation.",
|
||||
"No rules set": "Inga regler fastställda",
|
||||
"No files will be deleted as a result of this operation.": "Inga filer tas bort till följd av den här åtgärden.",
|
||||
"No rules set": "Inga regler angivna",
|
||||
"No upgrades": "Inga uppgraderingar",
|
||||
"Not shared": "Inte delad",
|
||||
"Notice": "Observera",
|
||||
@@ -282,61 +282,61 @@
|
||||
"OK": "OK",
|
||||
"Off": "Av",
|
||||
"Oldest First": "Äldsta först",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Valfri beskrivande etikett för mappen. Kan vara olika på varje enhet.",
|
||||
"Optional group for the device. Can be different on each device.": "Valfri grupp för enheten. Kan vara olika på varje enhet.",
|
||||
"Optional group for the folder. Can be different on each device.": "Valfri grupp för mappen. Kan vara olika på varje enhet.",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Valfri beskrivande etikett för mappen. Den kan skilja sig mellan enheterna.",
|
||||
"Optional group for the device. Can be different on each device.": "Valfri grupp för enheten. Den kan skilja sig mellan enheterna.",
|
||||
"Optional group for the folder. Can be different on each device.": "Valfri grupp för mappen. Den kan skilja sig mellan enheterna.",
|
||||
"Options": "Alternativ",
|
||||
"Out of Sync": "Osynkroniserad",
|
||||
"Out of Sync Items": "Osynkroniserade objekt",
|
||||
"Outgoing Rate Limit (KiB/s)": "Utgående hastighetsgräns (KiB/s)",
|
||||
"Override": "Åsidosätt",
|
||||
"Override Changes": "Åsidosätt förändringar",
|
||||
"Override Changes": "Åsidosätt ändringar",
|
||||
"Ownership": "Ägarskap",
|
||||
"Password": "Lösenord",
|
||||
"Path": "Sökväg",
|
||||
"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": "Sökväg till mappen på din dator. Kommer att skapas om det inte finns. Tecknet tilde (~) kan användas som en genväg för",
|
||||
"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": "Sökväg till mappen på din dator. Den skapas om den inte finns. Tecknet tilde (~) kan användas som en genväg för",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Sökväg där versioner ska lagras (lämna tomt för standardmappen .stversions i den delade mappen).",
|
||||
"Paths": "Sökvägar",
|
||||
"Pause": "Pausa",
|
||||
"Pause All": "Pausa alla",
|
||||
"Paused": "Pausad",
|
||||
"Paused (Unused)": "Pausad (oanvänd)",
|
||||
"Pending changes": "Väntar på ändringar",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning vid givet intervall och inaktiverad bevakning av ändringar",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning vid givet intervall och aktiverad bevakning av ändringar",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning vid givet intervall och misslyckades med att ställa in bevakning av ändringar, försöker igen var 1:e minut:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Lägg till det permanent i ignoreringslistan och undertryck ytterligare aviseringar.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Läs igenom versionsnyheterna innan du utför en större uppgradering.",
|
||||
"Pending changes": "Väntande ändringar",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning körs med angivet intervall och bevakning av ändringar är inaktiverad",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning körs med angivet intervall och bevakning av ändringar är aktiverad",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning körs med angivet intervall, men bevakning av ändringar kunde inte konfigureras. Ett nytt försök görs varje minut:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Lägg till den permanent i ignoreringslistan och dölj framtida aviseringar.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Läs versionsinformationen innan du uppgraderar till en ny huvudversion.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ställ in en autentiseringsanvändare och ett lösenord för det grafiska användargränssnittet i inställningsdialogrutan.",
|
||||
"Please wait": "Vänta",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix som indikerar att filen kan tas bort om den förhindrar mappborttagning",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix som indikerar att mönstret ska matchas utan skiftlägeskänslighet",
|
||||
"Preparing to Sync": "Förberedelser för synkronisering",
|
||||
"Preparing to Sync": "Förbereder synkronisering",
|
||||
"Preview": "Förhandsgranska",
|
||||
"Preview Usage Report": "Förhandsgranska användningsrapport",
|
||||
"QR code": "QR-kod",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"Quick guide to supported patterns": "Snabb handledning till mönster som stöds",
|
||||
"Quick guide to supported patterns": "Snabbguide till mönster som stöds",
|
||||
"Random": "Slumpmässig",
|
||||
"Receive Encrypted": "Ta emot krypterade",
|
||||
"Receive Encrypted": "Ta emot krypterat",
|
||||
"Receive Only": "Ta endast emot",
|
||||
"Received data is already encrypted": "Mottagna data är redan krypterade",
|
||||
"Recent Changes": "Senaste ändringar",
|
||||
"Reduced by ignore patterns": "Minskas med ignoreringsmönster",
|
||||
"Reduced by ignore patterns": "Minskad genom ignoreringsmönster",
|
||||
"Relay LAN": "LAN-relä",
|
||||
"Relay WAN": "WAN-relä",
|
||||
"Release Notes": "Versionsanteckningar",
|
||||
"Release Notes": "Versionsinformation",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Utgåvskandidater innehåller de senaste funktionerna och korrigeringarna. De liknar de traditionella Syncthing-utgåvorna som kommer ut varannan vecka.",
|
||||
"Remote Devices": "Fjärrenheter",
|
||||
"Remote GUI": "Fjärranslutet grafiskt användargränssnitt",
|
||||
"Remote GUI": "Fjärrgränssnitt",
|
||||
"Remove": "Ta bort",
|
||||
"Remove Device": "Ta bort enhet",
|
||||
"Remove Folder": "Ta bort mapp",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Krävs identifierare för mappen. Måste vara densamma på alla kluster enheter.",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Obligatorisk identifierare för mappen. Den måste vara identisk på alla enheter i klustret.",
|
||||
"Rescan": "Skanna igen",
|
||||
"Rescan All": "Skanna alla igen",
|
||||
"Rescans": "Återkommande skanningar",
|
||||
"Rescans": "Omskanningar",
|
||||
"Restart": "Starta om",
|
||||
"Restart Needed": "Omstart behövs",
|
||||
"Restarting": "Startar om",
|
||||
@@ -344,25 +344,25 @@
|
||||
"Restore Versions": "Återställ versioner",
|
||||
"Resume": "Återuppta",
|
||||
"Resume All": "Återuppta alla",
|
||||
"Reused": "Återanvänds",
|
||||
"Revert": "Återgå",
|
||||
"Reused": "Återanvänt",
|
||||
"Revert": "Återställ",
|
||||
"Revert Local Changes": "Återställ lokala ändringar",
|
||||
"Save": "Spara",
|
||||
"Saving changes": "Sparar ändringar",
|
||||
"Scan Time Remaining": "Återstående skanningstid",
|
||||
"Scanning": "Skannar",
|
||||
"See external versioning help for supported templated command line parameters.": "Se hjälp för extern version för stödda mallade kommandoradsparametrar.",
|
||||
"See external versioning help for supported templated command line parameters.": "Se hjälpen för extern versionshantering för information om kommandoradsparametrar som stöder mallar.",
|
||||
"Select All": "Markera alla",
|
||||
"Select a version": "Välj en version",
|
||||
"Select additional devices to share this folder with.": "Välj ytterligare enheter för att dela denna mapp med.",
|
||||
"Select additional folders to share with this device.": "Välj ytterligare mappar att dela med denna enhet.",
|
||||
"Select latest version": "Välj senaste versionen",
|
||||
"Select oldest version": "Välj äldsta versionen",
|
||||
"Send & Receive": "Skicka & ta emot",
|
||||
"Send & Receive": "Skicka och ta emot",
|
||||
"Send Extended Attributes": "Skicka utökade attribut",
|
||||
"Send Only": "Skicka endast",
|
||||
"Send Ownership": "Skicka ägarskap",
|
||||
"Set Ignores on Added Folder": "Ställ in ignoreringar för tillagd mapp",
|
||||
"Set Ignores on Added Folder": "Ange ignoreringsmönster för tillagd mapp",
|
||||
"Settings": "Inställningar",
|
||||
"Share": "Dela",
|
||||
"Share Folder": "Dela mapp",
|
||||
@@ -373,12 +373,12 @@
|
||||
"Shared With": "Delas med",
|
||||
"Sharing": "Delning",
|
||||
"Show ID": "Visa ID",
|
||||
"Show QR": "Visa QR",
|
||||
"Show QR": "Visa QR-kod",
|
||||
"Show detailed discovery status": "Visa detaljerad annonseringsstatus",
|
||||
"Show detailed listener status": "Visa detaljerad lyssnarstatus",
|
||||
"Show diff with previous version": "Visa skillnad med tidigare version",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas istället för enhets-ID i klusterstatus. Kommer att annonseras på andra enheter som ett valfritt standardnamn.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas istället för enhets-ID i klusterstatusen. Kommer att uppdateras till det namn som enheten annonserar om det lämnas tomt.",
|
||||
"Show diff with previous version": "Visa skillnader mot föregående version",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas i stället för enhets-ID i klusterstatusen. Namnet annonseras till andra enheter som ett valfritt standardnamn.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas i stället för enhets-ID i klusterstatusen. Om fältet lämnas tomt uppdateras det till det namn som enheten annonserar.",
|
||||
"Shut Down": "Stäng av",
|
||||
"Shutdown Complete": "Avstängning slutförd",
|
||||
"Simple": "Enkel",
|
||||
@@ -386,114 +386,114 @@
|
||||
"Single level wildcard (matches within a directory only)": "Jokertecken på en nivå (matchar endast i en mapp)",
|
||||
"Size": "Storlek",
|
||||
"Smallest First": "Minsta först",
|
||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Vissa annonseringsmetoder kunde inte fastställas för att hitta andra enheter eller tillkännage denna enhet:",
|
||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Vissa annonseringsmetoder kunde inte upprättas för att hitta andra enheter eller annonsera denna enhet:",
|
||||
"Some items could not be restored:": "Vissa objekt kunde inte återställas:",
|
||||
"Some listening addresses could not be enabled to accept connections:": "Vissa lyssningsadresser kunde inte aktiveras för att acceptera anslutningar:",
|
||||
"Source Code": "Källkod",
|
||||
"Stable releases and release candidates": "Stabila utgåvor och utgåvskandidater",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabila utgåvor är försenade med cirka två veckor. Under denna tid går de igenom testning som utgåvskandidater.",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabila utgåvor publiceras cirka två veckor senare. Under tiden testas de som utgåvskandidater.",
|
||||
"Stable releases only": "Endast stabila utgåvor",
|
||||
"Staggered": "Förskjuten",
|
||||
"Staggered File Versioning": "Filversionshantering i intervall",
|
||||
"Start Browser": "Starta webbläsaren",
|
||||
"Starting": "Börjar",
|
||||
"Starting": "Startar",
|
||||
"Statistics": "Statistik",
|
||||
"Stay logged in": "Förbli inloggad",
|
||||
"Stay logged in": "Håll mig inloggad",
|
||||
"Stopped": "Stoppad",
|
||||
"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.": "Lagrar och synkroniserar endast krypterade data. Mappar på alla anslutna enheter måste ställas in med samma lösenord eller också vara av typen \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "Ämne:",
|
||||
"Support": "Support",
|
||||
"Support Bundle": "Support Bundle",
|
||||
"Support Bundle": "Supportpaket",
|
||||
"Sync Extended Attributes": "Synkronisera utökade attribut",
|
||||
"Sync Ownership": "Synkronisera ägarskap",
|
||||
"Sync Protocol Listen Addresses": "Lyssnaradresser för synkroniseringsprotokoll",
|
||||
"Sync Status": "Synkroniseringsstatus",
|
||||
"Syncing": "Synkroniserar",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "Syncthing enhets-ID för \"{{devicename}}\"",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "Syncthings enhets-ID för \"{{devicename}}\"",
|
||||
"Syncthing has been shut down.": "Syncthing har stängts av.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing innehåller följande mjukvarupaket eller delar av dem:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing har fri och öppen källkod licensierad som 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 är ett program för kontinuerlig filsynkronisering. Den synkroniserar filer mellan två eller flera datorer i realtid, säkert skyddade från nyfikna ögon. Dina data är dina data och du har rätt att välja var de lagras, om de delas med någon tredje part och hur de överförs över internet.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing innehåller följande programvara eller delar av den:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing är fri programvara med öppen källkod, licensierad enligt 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 är ett program för kontinuerlig filsynkronisering. Det synkroniserar filer mellan två eller flera datorer i realtid och skyddar dem från obehörig insyn. Dina data tillhör dig, och du ska kunna välja var de lagras, om de delas med en tredje part och hur de överförs via internet.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing lyssnar på följande nätverksadresser för anslutningsförsök från andra enheter:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lyssnar inte efter anslutningsförsök från andra enheter på någon adress. Endast utgående anslutningar från denna enhet kanske fungerar.",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lyssnar inte efter anslutningsförsök från andra enheter på någon adress. Endast utgående anslutningar från den här enheten kan fungera.",
|
||||
"Syncthing is restarting.": "Syncthing startar om.",
|
||||
"Syncthing is saving changes.": "Syncthing sparar ändringar.",
|
||||
"Syncthing is upgrading.": "Syncthing uppgraderas.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing stöder nu automatiskt kraschrapportering till utvecklarna. Denna funktion är aktiverad som standard.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar vara avstängd, eller så finns det problem med din internetanslutning. Försöker igen…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem med behandlingen av din förfrågan. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar inte vara igång, eller så finns det problem med din internetanslutning. Försöker igen…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha problem med att behandla din begäran. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
|
||||
"TCP LAN": "TCP LAN",
|
||||
"TCP WAN": "TCP WAN",
|
||||
"Take me back": "Ta mig tillbaka",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Den grafiska gränssnittsadressen åsidosätts av startalternativ. Ändringar här träder inte i kraft så länge åsidosättningen är på plats.",
|
||||
"The Syncthing Authors": "Syncthing-upphovsmän",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Gränssnittets adress åsidosätts av startalternativ. Ändringar här får ingen effekt så länge åsidosättningen gäller.",
|
||||
"The Syncthing Authors": "Syncthings upphovspersoner",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Administratörsgränssnittet för Syncthing är konfigurerat för att möjliggöra fjärråtkomst utan lösenord.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Den aggregerade statistiken är offentligt tillgänglig på webbadressen nedan.",
|
||||
"The cleanup interval cannot be blank.": "Rensningsintervallet kan inte vara tomt.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Den sammanställda statistiken är offentligt tillgänglig på URL:en nedan.",
|
||||
"The cleanup interval cannot be blank.": "Rensningsintervallet får inte lämnas tomt.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurationen har sparats men inte aktiverats. Syncthing måste startas om för att aktivera den nya konfigurationen.",
|
||||
"The device ID cannot be blank.": "Enhets-ID kan inte vara tomt.",
|
||||
"The device ID cannot be blank.": "Enhets-ID får inte lämnas tomt.",
|
||||
"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).": "Enhets-ID som du anger här finns i dialogrutan \"Åtgärder > Visa ID\" på den andra enheten. Mellanslag och bindestreck är valfria (ignoreras).",
|
||||
"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.": "Den krypterade användningsrapporten skickas dagligen. Det används för att spåra vanliga plattformar, mappstorlekar och appversioner. Om den rapporterade datauppsättningen ändras kommer du att uppmanas med denna dialogruta igen.",
|
||||
"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.": "Det inmatade enhets-ID:t verkar inte vara korrekt. Det ska vara en 52 eller 56 teckensträng bestående av siffror och bokstäver, eventuellt med mellanrum och bindestreck.",
|
||||
"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.": "Den krypterade användningsrapporten skickas dagligen. Den används för att kartlägga vanliga plattformar, mappstorlekar och programversioner. Om den rapporterade datauppsättningen ändras visas den här dialogrutan igen.",
|
||||
"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.": "Det inmatade enhets-ID:t verkar inte vara korrekt. Det ska vara en sträng med 52 eller 56 tecken, bestående av bokstäver och siffror, där mellanslag och bindestreck är valfria.",
|
||||
"The folder ID cannot be blank.": "Mapp-ID får inte vara tomt.",
|
||||
"The folder ID must be unique.": "Mapp-ID måste vara unikt.",
|
||||
"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.": "Mappinnehållet på andra enheter skrivs över så att det blir identiskt med denna enhet. Filer som inte finns här kommer att tas bort på andra enheter.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Mappinnehållet på denna enhet skrivs över för att bli identiskt med andra enheter. Filer som nyligen har lagts till här kommer att tas bort.",
|
||||
"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.": "Mappinnehållet på andra enheter skrivs över så att det blir identiskt med innehållet på den här enheten. Filer som inte finns här tas bort från de andra enheterna.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Mappinnehållet på den här enheten skrivs över så att det blir identiskt med innehållet på de andra enheterna. Filer som nyligen har lagts till här tas bort.",
|
||||
"The folder path cannot be blank.": "Mappsökvägen kan inte vara tom.",
|
||||
"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.": "De följande intervallen används: varje 30 sekunder under den första timmen; varje timme under den första dagen; varje dag för de första 30 dagarna; varje vecka tills den maximala åldersgränsen uppnås.",
|
||||
"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.": "Följande intervall används: under den första timmen sparas en version var 30:e sekund, under den första dagen en version varje timme, under de första 30 dagarna en version varje dag och därefter en version varje vecka tills den maximala åldern nås.",
|
||||
"The following items could not be synchronized.": "Följande objekt kunde inte synkroniseras.",
|
||||
"The following items were changed locally.": "Följande objekt ändrades lokalt.",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Följande metoder används för att upptäcka andra enheter i nätverket och meddela att denna enhet ska hittas av andra:",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Följande metoder används för att upptäcka andra enheter i nätverket och annonsera den här enheten så att andra kan hitta den:",
|
||||
"The following text will automatically be inserted into a new message.": "Följande text kommer automatiskt att infogas i ett nytt meddelande.",
|
||||
"The following unexpected items were found.": "Följande oväntade objekt hittades.",
|
||||
"The interval must be a positive number of seconds.": "Intervallet måste vara ett positivt antal sekunder.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervallet, i sekunder, för att rensa i versionsmappen. Noll för att inaktivera periodisk rensning.",
|
||||
"The maximum age must be a number and cannot be blank.": "Åldersgränsen måste vara ett tal och kan inte lämnas tomt.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Den längsta tiden att behålla en version (i dagar, ställ in på 0 för att behålla versioner för alltid).",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervallet i sekunder mellan rensningar av versionsmappen. Ange noll för att inaktivera periodisk rensning.",
|
||||
"The maximum age must be a number and cannot be blank.": "Den högsta åldern måste vara ett tal och får inte lämnas tom.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Den längsta tiden som en version ska behållas, i dagar. Ange 0 för att behålla versioner för alltid.",
|
||||
"The number of connections must be a non-negative number.": "Antalet anslutningar måste vara ett icke-negativt tal.",
|
||||
"The number of days must be a number and cannot be blank.": "Antalet dagar måste vara en siffra och får inte vara tomt.",
|
||||
"The number of days must be a number and cannot be blank.": "Antalet dagar måste vara ett tal och får inte lämnas tomt.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Antalet dagar som filer ligger kvar i papperskorgen. Noll betyder för alltid.",
|
||||
"The number of old versions to keep, per file.": "Antalet gamla versioner som ska behållas, per fil.",
|
||||
"The number of versions must be a number and cannot be blank.": "Antalet versioner måste vara ett nummer och kan inte lämnas tomt.",
|
||||
"The number of old versions to keep, per file.": "Antalet gamla versioner som ska behållas per fil.",
|
||||
"The number of versions must be a number and cannot be blank.": "Antalet versioner måste vara ett tal och får inte lämnas tomt.",
|
||||
"The path cannot be blank.": "Sökvägen kan inte vara tom.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Hastighetsgränsen tillämpas på den ackumulerade trafiken för alla anslutningar till denna enhet.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Hastighetsgränsen tillämpas på den sammanlagda trafiken för alla anslutningar till den här enheten.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
|
||||
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte accepterat delning av denna mapp.",
|
||||
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte godkänt att den här mappen delas.",
|
||||
"The remote device has paused this folder.": "Fjärrenheten har pausat denna mapp.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Omskanningsintervallet måste vara ett positivt antal sekunder.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Omskanningsintervallet måste vara ett icke-negativt antal sekunder.",
|
||||
"There are no devices to share this folder with.": "Det finns inga enheter att dela denna mapp med.",
|
||||
"There are no file versions to restore.": "Det finns inga filversioner att återställa.",
|
||||
"There are no folders to share with this device.": "Det finns inga mappar att dela med denna enhet.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "De omprövas automatiskt och kommer att synkroniseras när felet är löst.",
|
||||
"This Device": "Denna enhet",
|
||||
"This Month": "Denna månad",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Detta kan lätt ge hackare tillgång till att läsa och ändra några filer på datorn.",
|
||||
"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.": "Denna enhet kan inte automatiskt upptäcka andra enheter eller meddela sin egen adress som andra kan hitta. Endast enheter med statiskt konfigurerade adresser kan ansluta.",
|
||||
"This is a major version upgrade.": "Det här är en stor uppgradering.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Denna inställning styr hur mycket ledigt utrymme som krävs på hemdisken (dvs. indexdatabasen).",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Försöken upprepas automatiskt och objekten synkroniseras när felet är löst.",
|
||||
"This Device": "Den här enheten",
|
||||
"This Month": "Den här månaden",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Det kan enkelt ge hackare möjlighet att läsa och ändra alla filer på datorn.",
|
||||
"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.": "Denna enhet kan inte automatiskt upptäcka andra enheter eller annonsera sin egen adress så att andra kan hitta den. Endast enheter med statiskt konfigurerade adresser kan ansluta.",
|
||||
"This is a major version upgrade.": "Det här är en uppgradering till en ny huvudversion.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Den här inställningen styr hur mycket ledigt utrymme som krävs på disken där hemmappen, det vill säga indexdatabasen, finns.",
|
||||
"Time": "Tid",
|
||||
"Time the item was last modified": "Tidpunkten objektet var senast ändrad",
|
||||
"Time the item was last modified": "Tidpunkt då objektet senast ändrades",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "För att ansluta till Syncthing-enheten med namnet \"{{devicename}}\", lägg till en ny fjärrenhet med detta ID:",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "För att tillåta en regel, markera kryssrutan. För att neka en regel, lämna den omarkerad.",
|
||||
"Today": "Idag",
|
||||
"Trash Can": "Papperskorgen",
|
||||
"Trash Can File Versioning": "Papperskorgs filversionshantering",
|
||||
"Trash Can File Versioning": "Filversionshantering med papperskorg",
|
||||
"Type": "Typ",
|
||||
"UNIX Permissions": "UNIX-behörigheter",
|
||||
"Unavailable": "Otillgänglig",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Otillgängligt/inaktiverat av administratör eller underhållare",
|
||||
"Undecided (will prompt)": "Obeslutad (kommer att fråga)",
|
||||
"Undecided (will prompt)": "Inte avgjort (fråga användaren)",
|
||||
"Unexpected Items": "Oväntade objekt",
|
||||
"Unexpected items have been found in this folder.": "Oväntade objekt har hittats i denna mapp.",
|
||||
"Unignore": "Sluta ignorera",
|
||||
"Unknown": "Okänd",
|
||||
"Unshared": "Inte delad",
|
||||
"Unshared Devices": "Odelade enheter",
|
||||
"Unshared Folders": "Odelade mappar",
|
||||
"Untrusted": "Opålitlig",
|
||||
"Unshared Devices": "Enheter utan delade mappar",
|
||||
"Unshared Folders": "Mappar som inte delas",
|
||||
"Untrusted": "Inte betrodd",
|
||||
"Up to Date": "Uppdaterad",
|
||||
"Updated {%file%}": "Uppdaterade {{file}}",
|
||||
"Upgrade": "Uppgradering",
|
||||
"Upgrade": "Uppgradera",
|
||||
"Upgrade To {%version%}": "Uppgradera till {{version}}",
|
||||
"Upgrading": "Uppgraderar",
|
||||
"Upload Rate": "Sändningshastighet",
|
||||
@@ -502,8 +502,8 @@
|
||||
"Use HTTPS for GUI": "Använd HTTPS för gränssnitt",
|
||||
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
|
||||
"User": "Användare",
|
||||
"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.",
|
||||
"User Home": "Användarens hemmapp",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn och lösenord har inte angetts för autentisering av det grafiska gränssnittet. Överväg att konfigurera dem.",
|
||||
"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änder en direkt TCP-anslutning över LAN",
|
||||
@@ -511,34 +511,34 @@
|
||||
"Version": "Version",
|
||||
"Versions": "Versioner",
|
||||
"Versions Path": "Sökväg för versioner",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versioner tas automatiskt bort om de är äldre än den maximala åldern eller överskrider antalet filer som tillåts i ett intervall.",
|
||||
"Waiting to Clean": "Väntar på att rensa",
|
||||
"Waiting to Scan": "Väntar på att skanna",
|
||||
"Waiting to Sync": "Väntar på att synkronisera",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versioner tas bort automatiskt om de är äldre än den högsta åldern eller om fler versioner lagras inom ett intervall än vad som tillåts.",
|
||||
"Waiting to Clean": "Väntar på rensning",
|
||||
"Waiting to Scan": "Väntar på skanning",
|
||||
"Waiting to Sync": "Väntar på synkronisering",
|
||||
"Warning": "Varning",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varning, denna sökväg är en överordnad mapp för en befintlig mapp \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning, denna sökväg är en överordnad mapp för en befintlig mapp \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varning, denna sökväg är en undermapp för en befintlig mapp \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning, denna sökväg är en undermapp för en befintlig mapp \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Varning: Om du använder en extern bevakare som {{syncthingInotify}}, bör du se till att den är inaktiverad.",
|
||||
"Watch for Changes": "Bevakning av ändringar",
|
||||
"Watch for Changes": "Bevaka ändringar",
|
||||
"Watching for Changes": "Bevakar ändringar",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Bevakning av ändringar upptäcker de flesta ändringar utan periodisk skanning.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet, kom ihåg att denna enhet måste läggas till på den andra enheten också.",
|
||||
"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.": "När du lägger till ny mapp, tänk på att mapp-ID knyter ihop mappar mellan olika enheter. De är skiftlägeskänsliga och måste matcha precis mellan alla enheter.",
|
||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "När den är inställd på mer än en på båda enheterna kommer Syncthing att försöka upprätta flera samtidiga anslutningar. Om värdena skiljer sig kommer det högsta att användas. Ställ in på noll för att låta Syncthing bestämma.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet måste den här enheten även läggas till på den andra sidan.",
|
||||
"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.": "När du lägger till en ny mapp bör du tänka på att mapp-ID:t används för att koppla ihop mappar mellan enheter. Mapp-ID:n är skiftlägeskänsliga och måste stämma exakt överens på alla enheter.",
|
||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "När värdet är större än ett på båda enheterna försöker Syncthing upprätta flera samtidiga anslutningar. Om värdena skiljer sig används det högsta. Ange noll för att låta Syncthing bestämma.",
|
||||
"Yes": "Ja",
|
||||
"Yesterday": "Igår",
|
||||
"You can also copy and paste the text into a new message manually.": "Du kan också kopiera och klistra in texten i ett nytt meddelande manuellt.",
|
||||
"You can also select one of these nearby devices:": "Du kan också välja en av dessa närliggande enheter:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Du kan ändra ditt val när som helst i inställningsdialogrutan.",
|
||||
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två publiceringsskanalerna på länken nedan.",
|
||||
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två utgivningskanalerna via länken nedan.",
|
||||
"You have no ignored devices.": "Du har inga ignorerade enheter.",
|
||||
"You have no ignored folders.": "Du har inga ignorerade mappar.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Du har osparade ändringar. Vill du verkligen kassera dem?",
|
||||
"You must keep at least one version.": "Du måste behålla åtminstone en version.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Du ska aldrig lägga till eller ändra något lokalt i en \"{{receiveEncrypted}}\"-mapp.",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Din SMS-app bör öppnas så att du kan välja mottagare och skicka den från ditt eget nummer.",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Din sms-app bör öppnas så att du kan välja mottagare och skicka meddelandet från ditt eget nummer.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Din e-postapp bör öppnas så att du kan välja mottagare och skicka den från din egen adress.",
|
||||
"days": "dagar",
|
||||
"deleted": "borttagen",
|
||||
@@ -555,13 +555,13 @@
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Svart",
|
||||
"dark": "Mörkt",
|
||||
"dark": "Mörk",
|
||||
"default": "Standard",
|
||||
"light": "Ljust"
|
||||
"light": "Ljus"
|
||||
}
|
||||
},
|
||||
"unknown device": "okänd enhet",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mapp \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mapp \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mappen \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mappen \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan återinföra denna enhet."
|
||||
}
|
||||
|
||||
+10
-9
@@ -222,7 +222,7 @@
|
||||
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreDevice(deviceID, pendingDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
|
||||
<span class="fas fa-times"></span> <span translate>Ignore</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingDevice(deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingDevice(deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification will reappear if the device connects again.' | translate}}">
|
||||
<span class="far fa-clock"></span> <span translate>Dismiss</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -269,7 +269,7 @@
|
||||
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreFolder(deviceID, folderID, offeringDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
|
||||
<span class="fas fa-times"></span> <span translate>Ignore</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingFolder(folderID, deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingFolder(folderID, deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.' | translate}}">
|
||||
<span class="far fa-clock"></span> <span translate>Dismiss</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -387,7 +387,7 @@
|
||||
</h4>
|
||||
<div class="panel-group" id="folders-{{ $index }}">
|
||||
<div class="panel panel-default" ng-repeat="folder in groupedFolders">
|
||||
<button class="btn panel-heading" data-toggle="collapse" data-parent="#folders-{{$index}}" data-target="#folder-{{$parent.$index}}-{{$index}}" aria-expanded="false">
|
||||
<button class="btn panel-heading" data-toggle="collapse" data-parent="#folders-{{$parent.$index}}" data-target="#folder-{{$parent.$index}}-{{$index}}" aria-expanded="false">
|
||||
<div class="panel-progress" ng-show="folderStatus(folder) == 'syncing'" ng-attr-style="width: {{syncPercentage(folder.id) | percent}}"></div>
|
||||
<div class="panel-progress" ng-show="folderStatus(folder) == 'scanning' && scanProgress[folder.id] != undefined" ng-attr-style="width: {{scanPercentage(folder.id) | percent}}"></div>
|
||||
<h4 class="panel-title">
|
||||
@@ -410,8 +410,8 @@
|
||||
</div>
|
||||
</h4>
|
||||
</button>
|
||||
<div id="folder-{{$parent.$index}}-{{$index}}" class="panel-collapse collapse">
|
||||
<div class="panel-body less-padding">
|
||||
<div id="folder-{{$parent.$index}}-{{$index}}" class="panel-collapse collapse" lazy-collapse>
|
||||
<div class="panel-body less-padding" ng-if="lazyReady">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr class="visible-xs">
|
||||
@@ -686,8 +686,8 @@
|
||||
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
|
||||
</h4>
|
||||
</button>
|
||||
<div id="device-this" class="panel-collapse collapse in">
|
||||
<div class="panel-body less-padding">
|
||||
<div id="device-this" class="panel-collapse collapse in" lazy-collapse>
|
||||
<div class="panel-body less-padding" ng-if="lazyReady">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -805,8 +805,8 @@
|
||||
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
|
||||
</h4>
|
||||
</button>
|
||||
<div id="device-{{$parent.$index}}-{{$index}}" class="panel-collapse collapse">
|
||||
<div class="panel-body less-padding">
|
||||
<div id="device-{{$parent.$index}}-{{$index}}" class="panel-collapse collapse" lazy-collapse>
|
||||
<div class="panel-body less-padding" ng-if="lazyReady">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr class="visible-xs">
|
||||
@@ -1083,6 +1083,7 @@
|
||||
<script type="text/javascript" src="syncthing/core/popoverDirective.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/syncthingController.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/tooltipDirective.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/lazyCollapseDirective.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/uncamelFilter.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/uniqueFolderDirective.js"></script>
|
||||
<script type="text/javascript" src="syncthing/core/validDeviceidDirective.js"></script>
|
||||
|
||||
@@ -30,7 +30,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, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Norcombe, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, cui, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Epifeny, epifeny, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Henrik Bråthen, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, JRNitre, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Luiz Angelo Daros de Luca, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, mattn, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Maxwell G, Michael Jephcote, Michael Rienstra, Michael Wang 汪東陽, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Prathik P Kulkarni, pullmerge, Quentin Hibon, Rahmi Pruitt, RealCharlesChia, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Shablone, Shivam Kumar, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Tao, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Umer-Azaz, Unrud, Val Markovic, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, vvaswani, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Gunnerson, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Norcombe, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, cui, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Epifeny, epifeny, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Frederic, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Henrik Bråthen, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, JRNitre, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Luiz Angelo Daros de Luca, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, mattn, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Maxwell G, Michael Jephcote, Michael Rienstra, Michael Wang 汪東陽, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Prathik P Kulkarni, pullmerge, Quentin Hibon, Rahmi Pruitt, RealCharlesChia, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Rohit Tanwar, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Shablone, Shivam Kumar, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Tao, Taylor Khan, tbodt, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Umer-Azaz, Unrud, Val Markovic, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, vvaswani, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,7 +62,6 @@ Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf,
|
||||
<li><a href="https://github.com/davecgh/go-spew">davecgh/go-spew</a>, Copyright © 2012-2016 Dave Collins.</li>
|
||||
<li><a href="https://github.com/go-asn1-ber/asn1-ber">go-asn1-ber/asn1-ber</a>, Copyright © 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
|
||||
<li><a href="https://github.com/go-ldap/ldap">go-ldap/ldap</a>, Copyright © 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
|
||||
<li><a href="https://go.yaml.in/yaml/v2">go.yaml.in/yaml/v2</a>, Copyright © 2026, the go.yaml.in/yaml/v2 authors.</li>
|
||||
<li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright © 2016 Sergey Kamardin.</li>
|
||||
<li><a href="https://github.com/gofrs/flock">gofrs/flock</a>, Copyright © 2018-2025, The Gofrs.</li>
|
||||
<li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright © 2011 The Snappy-Go Authors.</li>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('lazyCollapse', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function (scope, element) {
|
||||
// Render panel content only while expanded. Collapsed panels
|
||||
// contribute zero watchers. Bootstrap's show/hidden events
|
||||
// fire before/after the animation, so the DOM is present
|
||||
// during the transition.
|
||||
scope.lazyReady = element.hasClass('in');
|
||||
|
||||
$(element).on('show.bs.collapse', function () {
|
||||
scope.$apply(function () {
|
||||
scope.lazyReady = true;
|
||||
});
|
||||
});
|
||||
|
||||
$(element).on('hidden.bs.collapse', function () {
|
||||
scope.$apply(function () {
|
||||
scope.lazyReady = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -74,11 +74,7 @@ func (s *staticsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *staticsServer) serveAsset(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-cache, must-revalidate")
|
||||
|
||||
file := r.URL.Path
|
||||
|
||||
if file[0] == '/' {
|
||||
file = file[1:]
|
||||
}
|
||||
file := strings.TrimPrefix(r.URL.Path, "/")
|
||||
|
||||
if file == "" {
|
||||
file = "index.html"
|
||||
|
||||
Vendored
-1
@@ -116,7 +116,6 @@
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
|
||||
@@ -72,7 +72,6 @@ func TestDefaultValues(t *testing.T) {
|
||||
NATTimeoutS: 10,
|
||||
AutoUpgradeIntervalH: 12,
|
||||
KeepTemporariesH: 24,
|
||||
CacheIgnoredFiles: false,
|
||||
ProgressUpdateIntervalS: 5,
|
||||
LimitBandwidthInLan: false,
|
||||
MinHomeDiskFree: Size{1, "%"},
|
||||
@@ -278,7 +277,6 @@ func TestOverriddenValues(t *testing.T) {
|
||||
NATTimeoutS: 15,
|
||||
AutoUpgradeIntervalH: 24,
|
||||
KeepTemporariesH: 48,
|
||||
CacheIgnoredFiles: true,
|
||||
ProgressUpdateIntervalS: 10,
|
||||
LimitBandwidthInLan: true,
|
||||
MinHomeDiskFree: Size{5.2, "%"},
|
||||
|
||||
@@ -320,10 +320,6 @@ func migrateToConfigV15(cfg *Configuration) {
|
||||
}
|
||||
|
||||
func migrateToConfigV14(cfg *Configuration) {
|
||||
// Not using the ignore cache is the new default. Disable it on existing
|
||||
// configurations.
|
||||
cfg.Options.CacheIgnoredFiles = false
|
||||
|
||||
// Migrate UPnP -> NAT options
|
||||
cfg.Options.NATEnabled = cfg.Options.DeprecatedUPnPEnabled
|
||||
cfg.Options.DeprecatedUPnPEnabled = false
|
||||
|
||||
@@ -45,7 +45,6 @@ type OptionsConfiguration struct {
|
||||
AutoUpgradeIntervalH int `json:"autoUpgradeIntervalH" xml:"autoUpgradeIntervalH" default:"12"`
|
||||
UpgradeToPreReleases bool `json:"upgradeToPreReleases" xml:"upgradeToPreReleases"`
|
||||
KeepTemporariesH int `json:"keepTemporariesH" xml:"keepTemporariesH" default:"24"`
|
||||
CacheIgnoredFiles bool `json:"cacheIgnoredFiles" xml:"cacheIgnoredFiles" default:"false"`
|
||||
ProgressUpdateIntervalS int `json:"progressUpdateIntervalS" xml:"progressUpdateIntervalS" default:"5"`
|
||||
LimitBandwidthInLan bool `json:"limitBandwidthInLan" xml:"limitBandwidthInLan" default:"false"`
|
||||
MinHomeDiskFree Size `json:"minHomeDiskFree" xml:"minHomeDiskFree" default:"1 %"`
|
||||
|
||||
Vendored
-1
@@ -42,7 +42,6 @@
|
||||
<restartOnWakeup>true</restartOnWakeup>
|
||||
<autoUpgradeIntervalH>0</autoUpgradeIntervalH>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>true</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<symlinksEnabled>true</symlinksEnabled>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
|
||||
-1
@@ -22,7 +22,6 @@
|
||||
<restartOnWakeup>false</restartOnWakeup>
|
||||
<autoUpgradeIntervalH>24</autoUpgradeIntervalH>
|
||||
<keepTemporariesH>48</keepTemporariesH>
|
||||
<cacheIgnoredFiles>true</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>10</progressUpdateIntervalS>
|
||||
<symlinksEnabled>false</symlinksEnabled>
|
||||
<limitBandwidthInLan>true</limitBandwidthInLan>
|
||||
|
||||
@@ -407,6 +407,7 @@ func (c *idCheckingHTTPClient) Get(ctx context.Context, url string) (*http.Respo
|
||||
return nil, err
|
||||
}
|
||||
if err := c.check(resp); err != nil {
|
||||
resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -419,6 +420,7 @@ func (c *idCheckingHTTPClient) Post(ctx context.Context, url, ctype string, data
|
||||
return nil, err
|
||||
}
|
||||
if err := c.check(resp); err != nil {
|
||||
resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
+11
-1
@@ -240,16 +240,24 @@ func (f *BasicFilesystem) DirNames(name string) ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Open opens the file for reading, while not following symlinks
|
||||
func (f *BasicFilesystem) Open(name string) (File, error) {
|
||||
return f.OpenFile(name, os.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// OpenFile is the generalised file open call, using the flags to determine
|
||||
// the open semantics. We always add O_NOFOLLOW, disallowing opening files
|
||||
// by following symlinks, unless OptFollow is set.
|
||||
func (f *BasicFilesystem) OpenFile(name string, flags int, mode FileMode) (File, error) {
|
||||
rootedName, err := f.rooted(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flags |= alwaysOpenFlags // enforce extra bits in flags
|
||||
if flags&OptFollow != 0 {
|
||||
flags &^= OptFollow // unset the synthetic OptFollow bit
|
||||
} else {
|
||||
flags |= optNoFollow
|
||||
}
|
||||
fd, err := os.OpenFile(rootedName, flags, os.FileMode(mode))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -257,6 +265,8 @@ func (f *BasicFilesystem) OpenFile(name string, flags int, mode FileMode) (File,
|
||||
return basicFile{fd, name}, err
|
||||
}
|
||||
|
||||
// Create opens a file for writing, creating it as required. The Create will
|
||||
// fail if the destination is a symlink.
|
||||
func (f *BasicFilesystem) Create(name string) (File, error) {
|
||||
return f.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const alwaysOpenFlags = syscall.O_NOFOLLOW // never open symlinks as the final path component
|
||||
const optNoFollow = syscall.O_NOFOLLOW
|
||||
|
||||
func (f *BasicFilesystem) CreateSymlink(target, name string) error {
|
||||
name, err := f.rooted(name)
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build !(solaris && !cgo) && !(darwin && !cgo) && !(darwin && kqueue) && !(android && amd64)
|
||||
//go:build !(solaris && !cgo) && !(darwin && !cgo) && !(darwin && kqueue)
|
||||
// +build !solaris cgo
|
||||
// +build !darwin cgo
|
||||
// +build !darwin !kqueue
|
||||
// +build !android !amd64
|
||||
|
||||
package fs
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build (solaris && !cgo) || (darwin && !cgo) || (android && amd64) || (darwin && kqueue)
|
||||
// +build solaris,!cgo darwin,!cgo android,amd64 darwin,kqueue
|
||||
//go:build (solaris && !cgo) || (darwin && !cgo) || (darwin && kqueue)
|
||||
// +build solaris,!cgo darwin,!cgo darwin,kqueue
|
||||
|
||||
package fs
|
||||
|
||||
|
||||
+27
-54
@@ -5,23 +5,21 @@
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const alwaysOpenFlags = 0 // no extra flags
|
||||
const optNoFollow = 0 // not defined for Windows
|
||||
|
||||
var errNotSupported = errors.New("symlinks not supported")
|
||||
|
||||
@@ -72,30 +70,17 @@ func (f *BasicFilesystem) Hide(name string) error {
|
||||
}
|
||||
|
||||
func (f *BasicFilesystem) Roots() ([]string, error) {
|
||||
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getLogicalDriveStringsHandle, err := kernel32.FindProc("GetLogicalDriveStringsA")
|
||||
mask, err := windows.GetLogicalDrives()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buffer := [1024]byte{}
|
||||
bufferSize := uint32(len(buffer))
|
||||
|
||||
hr, _, _ := getLogicalDriveStringsHandle.Call(uintptr(unsafe.Pointer(&bufferSize)), uintptr(unsafe.Pointer(&buffer)))
|
||||
if hr == 0 {
|
||||
return nil, errors.New("syscall failed")
|
||||
}
|
||||
|
||||
var drives []string
|
||||
parts := bytes.Split(buffer[:], []byte{0})
|
||||
for _, part := range parts {
|
||||
if len(part) == 0 {
|
||||
break
|
||||
drives := make([]string, 0, bits.OnesCount32(mask))
|
||||
for letter := byte('A'); mask != 0; letter++ {
|
||||
if mask&1 == 1 {
|
||||
drives = append(drives, string([]byte{letter, ':', '\\'}))
|
||||
}
|
||||
drives = append(drives, string(part))
|
||||
mask >>= 1
|
||||
}
|
||||
|
||||
return drives, nil
|
||||
@@ -119,14 +104,14 @@ func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
|
||||
// SetSecurityInfo.
|
||||
|
||||
var si windows.SECURITY_INFORMATION
|
||||
var ownerSID, groupSID *syscall.SID
|
||||
var ownerSID, groupSID *windows.SID
|
||||
if uid != "" {
|
||||
ownerSID, err = syscall.StringToSid(uid)
|
||||
ownerSID, err = windows.StringToSid(uid)
|
||||
if err == nil {
|
||||
si |= windows.OWNER_SECURITY_INFORMATION
|
||||
}
|
||||
} else if gid != "" {
|
||||
groupSID, err = syscall.StringToSid(uid)
|
||||
groupSID, err = windows.StringToSid(uid)
|
||||
if err == nil {
|
||||
si |= windows.GROUP_SECURITY_INFORMATION
|
||||
}
|
||||
@@ -134,7 +119,7 @@ func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
|
||||
return errors.New("neither uid nor gid specified")
|
||||
}
|
||||
|
||||
return windows.SetSecurityInfo(hdl, windows.SE_FILE_OBJECT, si, (*windows.SID)(ownerSID), (*windows.SID)(groupSID), nil, nil)
|
||||
return windows.SetSecurityInfo(hdl, windows.SE_FILE_OBJECT, si, ownerSID, groupSID, nil, nil)
|
||||
}
|
||||
|
||||
func (f *BasicFilesystem) Remove(name string) error {
|
||||
@@ -218,47 +203,35 @@ func getFinalPathName(in string) (string, error) {
|
||||
// Wrap the call to GetFinalPathNameByHandleW
|
||||
// The string returned by this function uses the \?\ syntax
|
||||
// Implies GetFullPathName + GetLongPathName
|
||||
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
GetFinalPathNameByHandleW, err := kernel32.FindProc("GetFinalPathNameByHandleW")
|
||||
// https://github.com/golang/go/blob/ff048033e4304898245d843e79ed1a0897006c6d/src/internal/syscall/windows/syscall_windows.go#L303
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inPath, err := syscall.UTF16PtrFromString(in)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Get a file handler
|
||||
h, err := syscall.CreateFile(inPath,
|
||||
syscall.GENERIC_READ,
|
||||
syscall.FILE_SHARE_READ,
|
||||
// Get a file handle
|
||||
h, err := windows.CreateFile(inPath,
|
||||
windows.GENERIC_READ,
|
||||
windows.FILE_SHARE_READ,
|
||||
nil,
|
||||
syscall.OPEN_EXISTING,
|
||||
uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS),
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_FLAG_BACKUP_SEMANTICS,
|
||||
0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.CloseHandle(h)
|
||||
// Call GetFinalPathNameByHandleW
|
||||
var VOLUME_NAME_DOS uint32 = 0x0 // not yet defined in syscall
|
||||
var bufSize uint32 = syscall.MAX_PATH // 260
|
||||
defer windows.CloseHandle(h)
|
||||
|
||||
const VOLUME_NAME_DOS = 0x0 // not yet defined in x/sys/windows
|
||||
var bufSize uint32 = windows.MAX_PATH
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
buf := make([]uint16, bufSize)
|
||||
var ret uintptr
|
||||
ret, _, err = GetFinalPathNameByHandleW.Call(
|
||||
uintptr(h), // HANDLE hFile
|
||||
uintptr(unsafe.Pointer(&buf[0])), // LPWSTR lpszFilePath
|
||||
uintptr(bufSize), // DWORD cchFilePath
|
||||
uintptr(VOLUME_NAME_DOS), // DWORD dwFlags
|
||||
)
|
||||
var ret uint32
|
||||
ret, err = windows.GetFinalPathNameByHandle(
|
||||
h, &buf[0], bufSize, VOLUME_NAME_DOS)
|
||||
// The returned value is the actual length of the norm path
|
||||
// After Win 10 build 1607, MAX_PATH limitations have been removed
|
||||
// so it is necessary to check newBufSize
|
||||
newBufSize := uint32(ret) + 1
|
||||
newBufSize := ret + 1
|
||||
if ret == 0 || newBufSize > bufSize*100 {
|
||||
break
|
||||
}
|
||||
|
||||
+1
-1
@@ -499,7 +499,7 @@ func newCaseNode(name string, filesystem Filesystem) *caseNode {
|
||||
lower := UnicodeLowercaseNormalized(n)
|
||||
if lower != lastLower {
|
||||
node.lowerToReal[lower] = n
|
||||
lastLower = n
|
||||
lastLower = lower
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -173,8 +174,18 @@ const (
|
||||
OptSync = os.O_SYNC
|
||||
OptTruncate = os.O_TRUNC
|
||||
OptWriteOnly = os.O_WRONLY
|
||||
OptFollow = 1 << (bits.UintSize - 2)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Ensure OptFollow doesn't clash with any other flag.
|
||||
flags := OptAppend | OptCreate | OptExclusive | OptReadOnly | OptReadWrite | OptSync | OptTruncate | OptWriteOnly
|
||||
flags &= OptFollow
|
||||
if flags != 0 {
|
||||
panic("bug: flag clash")
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// as an error by any function.
|
||||
|
||||
@@ -234,3 +234,29 @@ func TestRepro9677MissingMtimeFS(t *testing.T) {
|
||||
newFS := NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}, NewMtimeOption(mtimeDB, ""))
|
||||
checkMtime(newFS)
|
||||
}
|
||||
|
||||
func TestBasicFilesystemRoots(t *testing.T) {
|
||||
var bfs BasicFilesystem
|
||||
roots, err := bfs.Roots()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The expected results depends on the system and we can't say too much,
|
||||
// but do some sanity checking.
|
||||
t.Logf("%d roots: %+v", len(roots), roots)
|
||||
if len(roots) > 25 {
|
||||
t.Errorf("suspiciously large number of filesystem roots: %d", len(roots))
|
||||
}
|
||||
rm := make(map[string]struct{})
|
||||
for _, root := range roots {
|
||||
if root == "" {
|
||||
t.Error("empty root")
|
||||
continue
|
||||
}
|
||||
if _, ok := rm[root]; ok {
|
||||
t.Errorf("duplicate root %q", root)
|
||||
continue
|
||||
}
|
||||
rm[root] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (C) 2014 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
|
||||
)
|
||||
|
||||
type nower interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
var clock = nower(defaultClock{})
|
||||
|
||||
type cache struct {
|
||||
entries map[string]cacheEntry
|
||||
}
|
||||
|
||||
type cacheEntry struct {
|
||||
result ignoreresult.R
|
||||
access int64 // Unix nanosecond count. Sufficient until the year 2262.
|
||||
}
|
||||
|
||||
func newCache() *cache {
|
||||
return &cache{
|
||||
entries: make(map[string]cacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cache) clean(d time.Duration) {
|
||||
for k, v := range c.entries {
|
||||
if clock.Now().Sub(time.Unix(0, v.access)) > d {
|
||||
delete(c.entries, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cache) get(key string) (ignoreresult.R, bool) {
|
||||
entry, ok := c.entries[key]
|
||||
if ok {
|
||||
entry.access = clock.Now().UnixNano()
|
||||
c.entries[key] = entry
|
||||
}
|
||||
return entry.result, ok
|
||||
}
|
||||
|
||||
func (c *cache) set(key string, result ignoreresult.R) {
|
||||
c.entries[key] = cacheEntry{result, time.Now().UnixNano()}
|
||||
}
|
||||
|
||||
func (c *cache) len() int {
|
||||
l := len(c.entries)
|
||||
return l
|
||||
}
|
||||
|
||||
type defaultClock struct{}
|
||||
|
||||
func (defaultClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// Copyright (C) 2014 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
fc := new(fakeClock)
|
||||
oldClock := clock
|
||||
clock = fc
|
||||
defer func() {
|
||||
clock = oldClock
|
||||
}()
|
||||
|
||||
c := newCache()
|
||||
|
||||
res, ok := c.get("nonexistent")
|
||||
if res.IsIgnored() || res.IsDeletable() || ok {
|
||||
t.Errorf("res %v, ok %v for nonexistent item", res, ok)
|
||||
}
|
||||
|
||||
// Set and check some items
|
||||
|
||||
c.set("true", ignoreresult.IgnoredDeletable)
|
||||
c.set("false", 0)
|
||||
|
||||
res, ok = c.get("true")
|
||||
if !res.IsIgnored() || !res.IsDeletable() || !ok {
|
||||
t.Errorf("res %v, ok %v for true item", res, ok)
|
||||
}
|
||||
|
||||
res, ok = c.get("false")
|
||||
if res.IsIgnored() || res.IsDeletable() || !ok {
|
||||
t.Errorf("res %v, ok %v for false item", res, ok)
|
||||
}
|
||||
|
||||
// Don't clean anything
|
||||
|
||||
c.clean(time.Second)
|
||||
|
||||
// Same values should exist
|
||||
|
||||
res, ok = c.get("true")
|
||||
if !res.IsIgnored() || !res.IsDeletable() || !ok {
|
||||
t.Errorf("res %v, ok %v for true item", res, ok)
|
||||
}
|
||||
|
||||
res, ok = c.get("false")
|
||||
if res.IsIgnored() || res.IsDeletable() || !ok {
|
||||
t.Errorf("res %v, ok %v for false item", res, ok)
|
||||
}
|
||||
|
||||
// Sleep and access, to get some data for clean
|
||||
|
||||
*fc += 500 // milliseconds
|
||||
|
||||
c.get("true")
|
||||
|
||||
*fc += 100 // milliseconds
|
||||
|
||||
// "false" was accessed ~600 ms ago, "true" was accessed ~100 ms ago.
|
||||
// This should clean out "false" but not "true"
|
||||
|
||||
c.clean(300 * time.Millisecond)
|
||||
|
||||
// Same values should exist
|
||||
|
||||
_, ok = c.get("true")
|
||||
if !ok {
|
||||
t.Error("item should still exist")
|
||||
}
|
||||
|
||||
_, ok = c.get("false")
|
||||
if ok {
|
||||
t.Errorf("item should have been cleaned")
|
||||
}
|
||||
}
|
||||
|
||||
type fakeClock int64 // milliseconds
|
||||
|
||||
func (f *fakeClock) Now() time.Time {
|
||||
t := time.Unix(int64(*f)/1000, (int64(*f)%1000)*int64(time.Millisecond))
|
||||
*f++
|
||||
return t
|
||||
}
|
||||
+9
-56
@@ -92,7 +92,7 @@ func (p Pattern) allowsSkippingIgnoredDirs() bool {
|
||||
if p.result.IsIgnored() {
|
||||
return true
|
||||
}
|
||||
if p.pattern[0] != '/' {
|
||||
if p.pattern == "" || p.pattern[0] != '/' {
|
||||
return false
|
||||
}
|
||||
// A "/**" at the end is allowed and doesn't have any bearing on the
|
||||
@@ -124,10 +124,7 @@ type Matcher struct {
|
||||
fs fs.Filesystem
|
||||
lines []string // exact lines read from .stignore
|
||||
patterns []Pattern // patterns including those from included files
|
||||
withCache bool
|
||||
matches *cache
|
||||
curHash string
|
||||
stop chan struct{}
|
||||
changeDetector ChangeDetector
|
||||
mut sync.Mutex
|
||||
}
|
||||
@@ -135,13 +132,6 @@ type Matcher struct {
|
||||
// An Option can be passed to New()
|
||||
type Option func(*Matcher)
|
||||
|
||||
// WithCache enables or disables lookup caching. The default is disabled.
|
||||
func WithCache(v bool) Option {
|
||||
return func(m *Matcher) {
|
||||
m.withCache = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithChangeDetector sets a custom ChangeDetector. The default is to simply
|
||||
// use the on disk modtime for comparison.
|
||||
func WithChangeDetector(cd ChangeDetector) Option {
|
||||
@@ -152,8 +142,7 @@ func WithChangeDetector(cd ChangeDetector) Option {
|
||||
|
||||
func New(fs fs.Filesystem, opts ...Option) *Matcher {
|
||||
m := &Matcher{
|
||||
fs: fs,
|
||||
stop: make(chan struct{}),
|
||||
fs: fs,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
@@ -161,9 +150,6 @@ func New(fs fs.Filesystem, opts ...Option) *Matcher {
|
||||
if m.changeDetector == nil {
|
||||
m.changeDetector = newModtimeChecker()
|
||||
}
|
||||
if m.withCache {
|
||||
go m.clean(2 * time.Hour)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -219,9 +205,6 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
|
||||
|
||||
m.curHash = newHash
|
||||
m.patterns = patterns
|
||||
if m.withCache {
|
||||
m.matches = newCache()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -232,7 +215,7 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
|
||||
// NFC everywhere else). This is always the case in real usage in syncthing, as
|
||||
// we ensure native unicode normalisation on all entry points (scanning and from
|
||||
// protocol) - so no need to normalize when calling this, except e.g. in tests.
|
||||
func (m *Matcher) Match(file string) (result ignoreresult.R) {
|
||||
func (m *Matcher) Match(file string) ignoreresult.R {
|
||||
switch {
|
||||
case fs.IsTemporary(file):
|
||||
return ignoreresult.IgnoreAndSkip
|
||||
@@ -254,19 +237,6 @@ func (m *Matcher) Match(file string) (result ignoreresult.R) {
|
||||
// Change backslashes to slashes (on Windows only)
|
||||
file = filepath.ToSlash(file)
|
||||
|
||||
if m.matches != nil {
|
||||
// Check the cache for a known result.
|
||||
res, ok := m.matches.get(file)
|
||||
if ok {
|
||||
return res
|
||||
}
|
||||
|
||||
// Update the cache with the result at return time
|
||||
defer func() {
|
||||
m.matches.set(file, result)
|
||||
}()
|
||||
}
|
||||
|
||||
// Check all the patterns for a match. Track whether the patterns so far
|
||||
// allow skipping matched directories or not. As soon as we hit an
|
||||
// exclude pattern (with some exceptions), we can't skip directories
|
||||
@@ -327,27 +297,6 @@ func (m *Matcher) Hash() string {
|
||||
return m.curHash
|
||||
}
|
||||
|
||||
func (m *Matcher) Stop() {
|
||||
close(m.stop)
|
||||
}
|
||||
|
||||
func (m *Matcher) clean(d time.Duration) {
|
||||
t := time.NewTimer(d / 2)
|
||||
for {
|
||||
select {
|
||||
case <-m.stop:
|
||||
return
|
||||
case <-t.C:
|
||||
m.mut.Lock()
|
||||
if m.matches != nil {
|
||||
m.matches.clean(d)
|
||||
}
|
||||
t.Reset(d / 2)
|
||||
m.mut.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hashPatterns(patterns []Pattern) string {
|
||||
h := sha256.New()
|
||||
for _, pat := range patterns {
|
||||
@@ -357,8 +306,8 @@ func hashPatterns(patterns []Pattern) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func loadIgnoreFile(fs fs.Filesystem, file string) (fs.File, fs.FileInfo, error) {
|
||||
fd, err := fs.Open(file)
|
||||
func loadIgnoreFile(ffs fs.Filesystem, file string) (fs.File, fs.FileInfo, error) {
|
||||
fd, err := ffs.OpenFile(file, fs.OptReadOnly|fs.OptFollow, 0o666)
|
||||
if err != nil {
|
||||
return fd, nil, err
|
||||
}
|
||||
@@ -463,6 +412,10 @@ func parseLine(line string) ([]Pattern, error) {
|
||||
patterns[0] = pattern
|
||||
|
||||
line = line[3:]
|
||||
if line == "" {
|
||||
// Pattern was exactly "**/", already covered by patterns[0].
|
||||
return patterns[:1], nil
|
||||
}
|
||||
pattern.pattern = line
|
||||
pattern.match, err = glob.Compile(line, '/')
|
||||
if err != nil {
|
||||
|
||||
+77
-205
@@ -52,7 +52,7 @@ func newTestFS() fs.Filesystem {
|
||||
func TestIgnore(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
err := pats.Load(".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -104,7 +104,7 @@ func TestExcludes(t *testing.T) {
|
||||
i*2
|
||||
!ign2
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -151,7 +151,7 @@ func TestFlagOrder(t *testing.T) {
|
||||
(?i)(?d)(?d)!ign9
|
||||
(?d)(?d)!ign10
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -188,7 +188,7 @@ func TestDeletables(t *testing.T) {
|
||||
ign7
|
||||
(?i)ign8
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -229,7 +229,7 @@ func TestBadPatterns(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, pat := range badPatterns {
|
||||
err := New(testFs, WithCache(true)).Parse(bytes.NewBufferString(pat), ".stignore")
|
||||
err := New(testFs).Parse(bytes.NewBufferString(pat), ".stignore")
|
||||
if err == nil {
|
||||
t.Errorf("No error for pattern %q", pat)
|
||||
}
|
||||
@@ -247,7 +247,7 @@ func TestBadPatterns(t *testing.T) {
|
||||
func TestCaseSensitivity(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
ign := New(testFs, WithCache(true))
|
||||
ign := New(testFs)
|
||||
err := ign.Parse(bytes.NewBufferString("test"), ".stignore")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -275,126 +275,6 @@ func TestCaseSensitivity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaching(t *testing.T) {
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
|
||||
|
||||
fd1, err := osutil.TempFile(fs, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fd2, err := osutil.TempFile(fs, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer fd1.Close()
|
||||
defer fd2.Close()
|
||||
defer fs.Remove(fd1.Name())
|
||||
defer fs.Remove(fd2.Name())
|
||||
|
||||
_, err = fd1.Write([]byte("/x/\n#include " + filepath.Base(fd2.Name()) + "\n"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fd2.Write([]byte("/y/\n"))
|
||||
|
||||
pats := New(fs, WithCache(true))
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pats.matches.len() != 0 {
|
||||
t.Fatal("Expected empty cache")
|
||||
}
|
||||
|
||||
// Cache some outcomes
|
||||
|
||||
for _, letter := range []string{"a", "b", "x", "y"} {
|
||||
pats.Match(letter)
|
||||
}
|
||||
|
||||
if pats.matches.len() != 4 {
|
||||
t.Fatal("Expected 4 cached results")
|
||||
}
|
||||
|
||||
// Reload file, expect old outcomes to be preserved
|
||||
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pats.matches.len() != 4 {
|
||||
t.Fatal("Expected 4 cached results")
|
||||
}
|
||||
|
||||
// Modify the include file, expect empty cache. Ensure the timestamp on
|
||||
// the file changes.
|
||||
|
||||
fd2.Write([]byte("/z/\n"))
|
||||
fd2.Sync()
|
||||
fakeTime := time.Now().Add(5 * time.Second)
|
||||
fs.Chtimes(fd2.Name(), fakeTime, fakeTime)
|
||||
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pats.matches.len() != 0 {
|
||||
t.Fatal("Expected 0 cached results")
|
||||
}
|
||||
|
||||
// Cache some outcomes again
|
||||
|
||||
for _, letter := range []string{"b", "x", "y"} {
|
||||
pats.Match(letter)
|
||||
}
|
||||
|
||||
// Verify that outcomes preserved on next load
|
||||
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pats.matches.len() != 3 {
|
||||
t.Fatal("Expected 3 cached results")
|
||||
}
|
||||
|
||||
// Modify the root file, expect cache to be invalidated
|
||||
|
||||
fd1.Write([]byte("/a/\n"))
|
||||
fd1.Sync()
|
||||
fakeTime = time.Now().Add(5 * time.Second)
|
||||
fs.Chtimes(fd1.Name(), fakeTime, fakeTime)
|
||||
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pats.matches.len() != 0 {
|
||||
t.Fatal("Expected cache invalidation")
|
||||
}
|
||||
|
||||
// Cache some outcomes again
|
||||
|
||||
for _, letter := range []string{"b", "x", "y"} {
|
||||
pats.Match(letter)
|
||||
}
|
||||
|
||||
// Verify that outcomes provided on next load
|
||||
|
||||
err = pats.Load(fd1.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pats.matches.len() != 3 {
|
||||
t.Fatal("Expected 3 cached results")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommentsAndBlankLines(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
@@ -409,7 +289,7 @@ func TestCommentsAndBlankLines(t *testing.T) {
|
||||
|
||||
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -419,6 +299,17 @@ func TestCommentsAndBlankLines(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegatedGlobstarOnly(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
pats := New(testFs)
|
||||
if err := pats.Parse(bytes.NewBufferString("!**/\n"), ".stignore"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Must not panic on an empty pattern component.
|
||||
pats.Match("foo")
|
||||
}
|
||||
|
||||
var result ignoreresult.R
|
||||
|
||||
func BenchmarkMatch(b *testing.B) {
|
||||
@@ -451,59 +342,6 @@ flamingo
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchCached(b *testing.B) {
|
||||
stignore := `
|
||||
.frog
|
||||
.frog*
|
||||
.frogfox
|
||||
.whale
|
||||
.whale/*
|
||||
.dolphin
|
||||
.dolphin/*
|
||||
~ferret~.*
|
||||
.ferret.*
|
||||
flamingo.*
|
||||
flamingo
|
||||
*.crow
|
||||
*.crow
|
||||
`
|
||||
// Caches per file, hence write the patterns to a file.
|
||||
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
|
||||
|
||||
fd, err := osutil.TempFile(fs, "", "")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = fd.Write([]byte(stignore))
|
||||
defer fd.Close()
|
||||
defer fs.Remove(fd.Name())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// Load the patterns
|
||||
pats := New(fs, WithCache(true))
|
||||
err = pats.Load(fd.Name())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
// Cache the outcome for "filename"
|
||||
pats.Match("filename")
|
||||
|
||||
// This load should now load the cached outcomes as the set of patterns
|
||||
// has not changed.
|
||||
err = pats.Load(fd.Name())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result = pats.Match("filename")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheReload(t *testing.T) {
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
|
||||
|
||||
@@ -522,7 +360,7 @@ func TestCacheReload(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pats := New(fs, WithCache(true))
|
||||
pats := New(fs)
|
||||
err = pats.Load(fd.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -579,7 +417,7 @@ func TestCacheReload(t *testing.T) {
|
||||
func TestHash(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
p1 := New(testFs, WithCache(true))
|
||||
p1 := New(testFs)
|
||||
err := p1.Load(".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -595,7 +433,7 @@ func TestHash(t *testing.T) {
|
||||
/ffile
|
||||
lost+found
|
||||
`
|
||||
p2 := New(testFs, WithCache(true))
|
||||
p2 := New(testFs)
|
||||
err = p2.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -610,7 +448,7 @@ func TestHash(t *testing.T) {
|
||||
/ffile
|
||||
lost+found
|
||||
`
|
||||
p3 := New(testFs, WithCache(true))
|
||||
p3 := New(testFs)
|
||||
err = p3.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -636,7 +474,7 @@ func TestHash(t *testing.T) {
|
||||
func TestHashOfEmpty(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
p1 := New(testFs, WithCache(true))
|
||||
p1 := New(testFs)
|
||||
|
||||
err := p1.Load(".stignore")
|
||||
if err != nil {
|
||||
@@ -678,7 +516,7 @@ func TestWindowsPatterns(t *testing.T) {
|
||||
a/b
|
||||
c\d
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -706,7 +544,7 @@ func TestAutomaticCaseInsensitivity(t *testing.T) {
|
||||
A/B
|
||||
c/d
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -728,7 +566,7 @@ func TestCommas(t *testing.T) {
|
||||
foo,bar.txt
|
||||
{baz,quux}.txt
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -762,7 +600,7 @@ func TestIssue3164(t *testing.T) {
|
||||
(?d)(?i)/foo
|
||||
(?d)(?i)**/bar
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -801,7 +639,7 @@ func TestIssue3174(t *testing.T) {
|
||||
stignore := `
|
||||
*ä*
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -822,7 +660,7 @@ func TestIssue3639(t *testing.T) {
|
||||
stignore := `
|
||||
foo/
|
||||
`
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -858,7 +696,7 @@ func TestIssue3674(t *testing.T) {
|
||||
{"as/dc", true},
|
||||
}
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -893,7 +731,7 @@ func TestGobwasGlobIssue18(t *testing.T) {
|
||||
{"bbaa", false},
|
||||
}
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -925,7 +763,7 @@ func TestRoot(t *testing.T) {
|
||||
{"b", true},
|
||||
}
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -951,7 +789,7 @@ func TestLines(t *testing.T) {
|
||||
!/a
|
||||
`
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -992,7 +830,7 @@ func TestDuplicateLines(t *testing.T) {
|
||||
/*
|
||||
`
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -1025,7 +863,7 @@ func TestIssue4680(t *testing.T) {
|
||||
{"#snapshot/foo", true},
|
||||
}
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -1045,7 +883,7 @@ func TestIssue4689(t *testing.T) {
|
||||
|
||||
stignore := `// orig`
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
@@ -1076,7 +914,7 @@ func TestIssue4901(t *testing.T) {
|
||||
puppy
|
||||
`
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
fd, err := pats.fs.Create(".stignore")
|
||||
if err != nil {
|
||||
@@ -1119,7 +957,7 @@ func TestIssue4901(t *testing.T) {
|
||||
func TestIssue5009(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
stignore := `
|
||||
ign1
|
||||
@@ -1152,7 +990,7 @@ func TestIssue5009(t *testing.T) {
|
||||
func TestSpecialChars(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
stignore := `(?i)/#recycle
|
||||
(?i)/#nosync
|
||||
@@ -1179,7 +1017,7 @@ func TestSpecialChars(t *testing.T) {
|
||||
func TestIntlWildcards(t *testing.T) {
|
||||
testFs := newTestFS()
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
stignore := `1000春
|
||||
200?春
|
||||
@@ -1208,7 +1046,7 @@ func TestPartialIncludeLine(t *testing.T) {
|
||||
|
||||
// Loading a partial #include line (no file mentioned) should error but not crash.
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
cases := []string{
|
||||
"#include",
|
||||
@@ -1267,7 +1105,7 @@ func TestSkipIgnoredDirs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
pats := New(testFs, WithCache(true))
|
||||
pats := New(testFs)
|
||||
|
||||
stignore := `
|
||||
/foo/ign*
|
||||
@@ -1711,7 +1549,7 @@ func testEscape(t *testing.T, tests []escapeTest, noErrors bool) {
|
||||
for name, content := range testEscapeFiles {
|
||||
fs.WriteFile(testFS, name, []byte(content), 0o666)
|
||||
}
|
||||
pats := New(testFS, WithCache(true))
|
||||
pats := New(testFS)
|
||||
|
||||
err := pats.Parse(bytes.NewBufferString(test.pattern), ".stignore")
|
||||
if noErrors {
|
||||
@@ -1731,3 +1569,37 @@ func testEscape(t *testing.T, tests []escapeTest, noErrors bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIgnoreThroughSymlink verifies that an ignore file can be loaded when
|
||||
// it is itself a symlink pointing at the real file. This exercises the
|
||||
// SkipDefaultOpenFlags path, as our normal Open enforces O_NOFOLLOW. We use
|
||||
// a real filesystem here because the fake one does not implement the
|
||||
// O_NOFOLLOW semantics that make this distinction meaningful.
|
||||
func TestIgnoreThroughSymlink(t *testing.T) {
|
||||
if build.IsWindows {
|
||||
t.Skip("symlinks not supported on Windows")
|
||||
}
|
||||
|
||||
testFS := fs.NewFilesystem(fs.FilesystemTypeBasic, t.TempDir())
|
||||
|
||||
// The real ignore file lives under a different name, and .stignore is a
|
||||
// symlink pointing at it.
|
||||
if err := fs.WriteFile(testFS, "real-ignores", []byte("bfile\n"), 0o666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := testFS.CreateSymlink("real-ignores", ".stignore"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pats := New(testFS)
|
||||
if err := pats.Load(".stignore"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !pats.Match("bfile").IsIgnored() {
|
||||
t.Error("bfile should be ignored")
|
||||
}
|
||||
if pats.Match("afile").IsIgnored() {
|
||||
t.Error("afile should not be ignored")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,19 @@ package model
|
||||
|
||||
import (
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// How many files to send in each Index/IndexUpdate message.
|
||||
// How many files & blocks to send in each Index/IndexUpdate message.
|
||||
const (
|
||||
MaxBatchSizeBytes = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed)
|
||||
MaxBatchSizeFiles = 1000 // Either way, don't include more files than this
|
||||
MaxBatchSizeFiles = 1000
|
||||
MaxBatchSizeBlocks = 5000
|
||||
)
|
||||
|
||||
// FileInfoBatch is a utility to do file operations on the database in suitably
|
||||
// sized batches.
|
||||
type FileInfoBatch struct {
|
||||
infos []protocol.FileInfo
|
||||
size int
|
||||
nblocks int
|
||||
flushFn func([]protocol.FileInfo) error
|
||||
error error
|
||||
}
|
||||
@@ -47,11 +46,11 @@ func (b *FileInfoBatch) Append(f protocol.FileInfo) {
|
||||
b.infos = make([]protocol.FileInfo, 0, MaxBatchSizeFiles)
|
||||
}
|
||||
b.infos = append(b.infos, f)
|
||||
b.size += proto.Size(f.ToWire(true))
|
||||
b.nblocks += len(f.Blocks)
|
||||
}
|
||||
|
||||
func (b *FileInfoBatch) Full() bool {
|
||||
return len(b.infos) >= MaxBatchSizeFiles || b.size >= MaxBatchSizeBytes
|
||||
return len(b.infos) >= MaxBatchSizeFiles || b.nblocks >= MaxBatchSizeBlocks
|
||||
}
|
||||
|
||||
func (b *FileInfoBatch) FlushIfFull() error {
|
||||
@@ -82,9 +81,5 @@ func (b *FileInfoBatch) Flush() error {
|
||||
func (b *FileInfoBatch) Reset() {
|
||||
b.infos = nil
|
||||
b.error = nil
|
||||
b.size = 0
|
||||
}
|
||||
|
||||
func (b *FileInfoBatch) Size() int {
|
||||
return b.size
|
||||
b.nblocks = 0
|
||||
}
|
||||
|
||||
+68
-45
@@ -195,19 +195,23 @@ func (f *folder) Serve(ctx context.Context) error {
|
||||
case <-f.pullScheduled:
|
||||
if f.PullerDelayS > 0 {
|
||||
// Wait for incoming updates to settle before doing the
|
||||
// actual pull. Only set the state to SyncWaiting if we have
|
||||
// reason to believe there is something to sync, to avoid
|
||||
// unnecessary flashing in the GUI.
|
||||
if needCount, err := f.db.CountNeed(f.folderID, protocol.LocalDeviceID); err == nil && needCount.TotalItems() > 0 {
|
||||
f.setState(FolderSyncWaiting)
|
||||
// actual pull.
|
||||
//
|
||||
// Only set the state to SyncWaiting if we are Idle (and
|
||||
// not, e.g., errored) and have reason to believe there is
|
||||
// something to sync, to avoid unnecessary flashing in the
|
||||
// GUI.
|
||||
if cur, _, _ := f.getState(); cur == FolderIdle {
|
||||
if needCount, err := f.db.CountNeed(f.folderID, protocol.LocalDeviceID); err == nil && needCount.TotalItems() > 0 {
|
||||
f.setState(FolderSyncWaiting)
|
||||
}
|
||||
}
|
||||
pullTimer.Reset(time.Duration(float64(time.Second) * f.PullerDelayS))
|
||||
} else {
|
||||
_, err = f.pull(ctx)
|
||||
continue
|
||||
}
|
||||
_, err = f.pull(ctx)
|
||||
|
||||
case <-pullTimer.C:
|
||||
f.setState(FolderIdle)
|
||||
_, err = f.pull(ctx)
|
||||
|
||||
case <-f.pullFailTimer.C:
|
||||
@@ -238,10 +242,12 @@ func (f *folder) Serve(ctx context.Context) error {
|
||||
case next := <-f.scanDelay:
|
||||
f.sl.DebugContext(ctx, "Delaying scan")
|
||||
f.scanTimer.Reset(next)
|
||||
continue
|
||||
|
||||
case <-f.scanScheduled:
|
||||
f.sl.DebugContext(ctx, "Scan was scheduled")
|
||||
f.scanTimer.Reset(0)
|
||||
continue
|
||||
|
||||
case fsEvents := <-f.watchChan:
|
||||
f.sl.DebugContext(ctx, "Scan due to watcher")
|
||||
@@ -252,16 +258,20 @@ func (f *folder) Serve(ctx context.Context) error {
|
||||
err = f.restartWatch(ctx)
|
||||
|
||||
case <-f.versionCleanupTimer.C:
|
||||
if _, _, healthErr := f.getState(); healthErr != nil {
|
||||
continue
|
||||
}
|
||||
f.sl.DebugContext(ctx, "Doing version cleanup")
|
||||
f.versionCleanupTimerFired(ctx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if svcutil.IsFatal(err) {
|
||||
return err
|
||||
}
|
||||
f.setError(ctx, err)
|
||||
if svcutil.IsFatal(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the state to FolderError when err is non-nil, otherwise reset
|
||||
// it back to FolderIdle.
|
||||
f.setError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +413,15 @@ func (f *folder) pull(ctx context.Context) (success bool, err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
// Abort early (before acquiring a limiter token) if there's a folder
|
||||
// error. This must happen before the "nothing to do" check below so
|
||||
// that up-to-date folders are also periodically health-checked.
|
||||
err = f.getHealthErrorWithoutIgnores()
|
||||
if err != nil {
|
||||
f.sl.DebugContext(ctx, "Skipping pull due to folder error", slogutil.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
// If there is nothing to do, don't even enter sync-waiting state.
|
||||
needCount, err := f.db.CountNeed(f.folderID, protocol.LocalDeviceID)
|
||||
if err != nil {
|
||||
@@ -416,13 +435,6 @@ func (f *folder) pull(ctx context.Context) (success bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Abort early (before acquiring a token) if there's a folder error
|
||||
err = f.getHealthErrorWithoutIgnores()
|
||||
if err != nil {
|
||||
f.sl.DebugContext(ctx, "Skipping pull due to folder error", slogutil.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Send only folder doesn't do any io, it only checks for out-of-sync
|
||||
// items that differ in metadata and updates those.
|
||||
if f.Type != config.FolderTypeSendOnly {
|
||||
@@ -487,7 +499,6 @@ func (f *folder) scanSubdirs(ctx context.Context, subDirs []string) error {
|
||||
}()
|
||||
|
||||
f.setState(FolderScanWaiting)
|
||||
defer f.setState(FolderIdle)
|
||||
|
||||
if err := f.ioLimiter.TakeWithContext(ctx, 1); err != nil {
|
||||
return err
|
||||
@@ -571,17 +582,17 @@ func (f *folder) scanSubdirs(ctx context.Context, subDirs []string) error {
|
||||
const maxToRemove = 1000
|
||||
|
||||
type scanBatch struct {
|
||||
f *folder
|
||||
updateBatch *FileInfoBatch
|
||||
toRemove []string
|
||||
deleted map[string]struct{}
|
||||
f *folder
|
||||
updateBatch *FileInfoBatch
|
||||
toRemove []string
|
||||
skipInFindRename map[string]struct{}
|
||||
}
|
||||
|
||||
func (f *folder) newScanBatch() *scanBatch {
|
||||
b := &scanBatch{
|
||||
f: f,
|
||||
toRemove: make([]string, 0, maxToRemove),
|
||||
deleted: make(map[string]struct{}),
|
||||
f: f,
|
||||
toRemove: make([]string, 0, maxToRemove),
|
||||
skipInFindRename: make(map[string]struct{}),
|
||||
}
|
||||
b.updateBatch = NewFileInfoBatch(func(fs []protocol.FileInfo) error {
|
||||
if err := b.f.getHealthErrorWithoutIgnores(); err != nil {
|
||||
@@ -589,7 +600,7 @@ func (f *folder) newScanBatch() *scanBatch {
|
||||
return err
|
||||
}
|
||||
b.f.updateLocalsFromScanning(fs)
|
||||
clear(b.deleted)
|
||||
clear(b.skipInFindRename)
|
||||
return nil
|
||||
})
|
||||
return b
|
||||
@@ -625,12 +636,12 @@ func (b *scanBatch) FlushIfFull() error {
|
||||
return b.updateBatch.FlushIfFull()
|
||||
}
|
||||
|
||||
func (b *scanBatch) markDeleted(name string) {
|
||||
b.deleted[name] = struct{}{}
|
||||
func (b *scanBatch) markSkipInFindRename(name string) {
|
||||
b.skipInFindRename[name] = struct{}{}
|
||||
}
|
||||
|
||||
func (b *scanBatch) hasDeleted(name string) bool {
|
||||
_, ok := b.deleted[name]
|
||||
func (b *scanBatch) shouldSkipInFindRenames(name string) bool {
|
||||
_, ok := b.skipInFindRename[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -734,12 +745,18 @@ func (f *folder) scanSubdirsChangedAndNew(ctx context.Context, subDirs []string,
|
||||
switch f.Type {
|
||||
case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted:
|
||||
default:
|
||||
if nf, ok := f.findRename(ctx, res.File, batch); ok {
|
||||
if ok, err := batch.Update(nf); err != nil {
|
||||
return 0, err
|
||||
} else if ok {
|
||||
changes++
|
||||
batch.markDeleted(nf.Name)
|
||||
// Rename detection is comparatively expensive, so only attempt
|
||||
// it for files that appeared as new on disk during this scan. A
|
||||
// rename that overwrites an existing file (the destination path
|
||||
// already had an entry, so it scans as an update rather than a
|
||||
// new file) is not optimised as a rename.
|
||||
if res.File.New && res.File.Size > 0 {
|
||||
if nf, ok := f.findRename(ctx, res.File, batch); ok {
|
||||
if ok, err := batch.Update(nf); err != nil {
|
||||
return 0, err
|
||||
} else if ok {
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -932,7 +949,7 @@ loop:
|
||||
continue
|
||||
}
|
||||
|
||||
if batch.hasDeleted(fi.Name) {
|
||||
if batch.shouldSkipInFindRenames(fi.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -952,6 +969,7 @@ loop:
|
||||
}
|
||||
|
||||
if !osutil.IsDeleted(f.mtimefs, fi.Name) {
|
||||
batch.markSkipInFindRename(fi.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -963,6 +981,7 @@ loop:
|
||||
nf.SetDeleted(f.shortID)
|
||||
nf.LocalFlags = f.localFlags
|
||||
found = true
|
||||
batch.markSkipInFindRename(fi.Name)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -993,7 +1012,6 @@ func (f *folder) scanTimerFired(ctx context.Context) error {
|
||||
|
||||
func (f *folder) versionCleanupTimerFired(ctx context.Context) {
|
||||
f.setState(FolderCleanWaiting)
|
||||
defer f.setState(FolderIdle)
|
||||
|
||||
if err := f.ioLimiter.TakeWithContext(ctx, 1); err != nil {
|
||||
return
|
||||
@@ -1184,8 +1202,13 @@ func (f *folder) setError(ctx context.Context, err error) {
|
||||
default:
|
||||
}
|
||||
|
||||
_, _, oldErr := f.getState()
|
||||
if (err != nil && oldErr != nil && oldErr.Error() == err.Error()) || (err == nil && oldErr == nil) {
|
||||
state, _, oldErr := f.getState()
|
||||
switch {
|
||||
case err != nil && oldErr != nil && oldErr.Error() == err.Error():
|
||||
// The error is the same as the old error, no change is required
|
||||
return
|
||||
case err == nil && oldErr == nil && state == FolderIdle:
|
||||
// No error before or now, and we are already Idle
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1195,7 +1218,7 @@ func (f *folder) setError(ctx context.Context, err error) {
|
||||
} else {
|
||||
f.sl.InfoContext(ctx, "Folder error changed", slogutil.Error(err), slog.Any("previously", oldErr))
|
||||
}
|
||||
} else {
|
||||
} else if oldErr != nil {
|
||||
f.sl.InfoContext(ctx, "Folder error cleared")
|
||||
f.SchedulePull()
|
||||
}
|
||||
@@ -1203,7 +1226,7 @@ func (f *folder) setError(ctx context.Context, err error) {
|
||||
if f.FSWatcherEnabled {
|
||||
if err != nil {
|
||||
f.stopWatch()
|
||||
} else {
|
||||
} else if oldErr != nil {
|
||||
f.scheduleWatchRestart()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ func (f *receiveEncryptedFolder) revert(ctx context.Context) error {
|
||||
f.sl.InfoContext(ctx, "Reverting unexpected items")
|
||||
|
||||
f.setState(FolderScanning)
|
||||
defer f.setState(FolderIdle)
|
||||
|
||||
batch := NewFileInfoBatch(func(fs []protocol.FileInfo) error {
|
||||
f.updateLocalsFromScanning(fs)
|
||||
|
||||
@@ -74,7 +74,6 @@ func (f *receiveOnlyFolder) revert(ctx context.Context) error {
|
||||
f.sl.InfoContext(ctx, "Reverting folder")
|
||||
|
||||
f.setState(FolderScanning)
|
||||
defer f.setState(FolderIdle)
|
||||
|
||||
scanChan := make(chan string)
|
||||
go f.pullScannerRoutine(ctx, scanChan)
|
||||
|
||||
@@ -123,10 +123,12 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
oldData := []byte("hello\n")
|
||||
knownFiles := setupKnownFiles(t, ffs, oldData)
|
||||
|
||||
// Send and index update for the known stuff
|
||||
// Send an index update for the known stuff. Update the local index
|
||||
// prior to getting index data from the remote device, so we avoid a
|
||||
// pull starting and causing race flakiness for the rest of the test.
|
||||
|
||||
must(t, m.Index(conn, &protocol.Index{Folder: "ro", Files: knownFiles}))
|
||||
f.updateLocalsFromScanning(knownFiles)
|
||||
must(t, m.Index(conn, &protocol.Index{Folder: "ro", Files: knownFiles}))
|
||||
|
||||
// Scan the folder.
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ func (f *sendOnlyFolder) override(ctx context.Context) error {
|
||||
f.sl.InfoContext(ctx, "Overriding global state ")
|
||||
|
||||
f.setState(FolderScanning)
|
||||
defer f.setState(FolderIdle)
|
||||
|
||||
batch := NewFileInfoBatch(func(files []protocol.FileInfo) error {
|
||||
f.updateLocalsFromScanning(files)
|
||||
|
||||
@@ -168,7 +168,6 @@ func (f *sendReceiveFolder) pull(ctx context.Context) (bool, error) {
|
||||
go f.pullScannerRoutine(ctx, scanChan)
|
||||
defer func() {
|
||||
close(scanChan)
|
||||
f.setState(FolderIdle)
|
||||
}()
|
||||
|
||||
metricFolderPulls.WithLabelValues(f.ID).Inc()
|
||||
|
||||
@@ -276,7 +276,7 @@ func (s *indexHandler) sendIndexTo(ctx context.Context) error {
|
||||
// can't happen, once an error is returned the index sender exits
|
||||
panic(fmt.Sprintf("bug: once failed it should stay failed (%v)", batchError))
|
||||
}
|
||||
l.Debugf("%v: Sending %d files (<%d bytes)", s, len(fs), batch.Size())
|
||||
l.Debugf("%v: Sending %d files", s, len(fs))
|
||||
|
||||
lastSequence := fs[len(fs)-1].Sequence
|
||||
var err error
|
||||
|
||||
+10
-10
@@ -300,7 +300,7 @@ func (m *model) initFolders(cfg config.Configuration) error {
|
||||
folderCfg.CreateRoot()
|
||||
continue
|
||||
}
|
||||
err := m.newFolder(folderCfg, cfg.Options.CacheIgnoredFiles)
|
||||
err := m.newFolder(folderCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -335,8 +335,8 @@ func (m *model) fatal(err error) {
|
||||
}
|
||||
|
||||
// Need to hold lock on m.mut when calling this.
|
||||
func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration, cacheIgnoredFiles bool) {
|
||||
ignores := ignore.New(cfg.Filesystem(), ignore.WithCache(cacheIgnoredFiles))
|
||||
func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration) {
|
||||
ignores := ignore.New(cfg.Filesystem())
|
||||
if cfg.Type != config.FolderTypeReceiveEncrypted {
|
||||
if err := ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) {
|
||||
slog.Error("Failed to load ignores", slogutil.Error(err))
|
||||
@@ -509,7 +509,7 @@ func (m *model) cleanupFolderLocked(cfg config.FolderConfiguration) {
|
||||
delete(m.folderEncryptionFailures, cfg.ID)
|
||||
}
|
||||
|
||||
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) error {
|
||||
func (m *model) restartFolder(from, to config.FolderConfiguration) error {
|
||||
if to.ID == "" {
|
||||
panic("bug: cannot restart empty folder ID")
|
||||
}
|
||||
@@ -539,7 +539,7 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
|
||||
|
||||
m.cleanupFolderLocked(from)
|
||||
if !to.Paused {
|
||||
m.addAndStartFolderLocked(to, cacheIgnoredFiles)
|
||||
m.addAndStartFolderLocked(to)
|
||||
}
|
||||
|
||||
runner, _ := m.folderRunners.Get(to.ID)
|
||||
@@ -560,11 +560,11 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) error {
|
||||
func (m *model) newFolder(cfg config.FolderConfiguration) error {
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
m.addAndStartFolderLocked(cfg, cacheIgnoredFiles)
|
||||
m.addAndStartFolderLocked(cfg)
|
||||
|
||||
// Cluster configs might be received and processed before reaching this
|
||||
// point, i.e. before the folder is started. If that's the case, start
|
||||
@@ -2972,7 +2972,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
slog.Info("Paused folder", cfg.LogAttr())
|
||||
} else {
|
||||
slog.Info("Adding folder", cfg.LogAttr())
|
||||
if err := m.newFolder(cfg, to.Options.CacheIgnoredFiles); err != nil {
|
||||
if err := m.newFolder(cfg); err != nil {
|
||||
m.fatal(err)
|
||||
return true
|
||||
}
|
||||
@@ -2998,8 +2998,8 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
|
||||
// This folder exists on both sides. Settings might have changed.
|
||||
// Check if anything differs that requires a restart.
|
||||
if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) || from.Options.CacheIgnoredFiles != to.Options.CacheIgnoredFiles {
|
||||
if err := m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles); err != nil {
|
||||
if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) {
|
||||
if err := m.restartFolder(fromCfg, toCfg); err != nil {
|
||||
m.fatal(err)
|
||||
return true
|
||||
}
|
||||
|
||||
+13
-10
@@ -1603,7 +1603,7 @@ func TestIgnores(t *testing.T) {
|
||||
ID: "fresh", Path: "XXX",
|
||||
FilesystemType: config.FilesystemTypeFake,
|
||||
}
|
||||
ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles))
|
||||
ignores := ignore.New(fcfg.Filesystem())
|
||||
m.mut.Lock()
|
||||
m.folderCfgs[fcfg.ID] = fcfg
|
||||
m.folderIgnores[fcfg.ID] = ignores
|
||||
@@ -1618,7 +1618,7 @@ func TestIgnores(t *testing.T) {
|
||||
pausedDefaultFolderConfig := defaultFolderConfig
|
||||
pausedDefaultFolderConfig.Paused = true
|
||||
|
||||
m.restartFolder(defaultFolderConfig, pausedDefaultFolderConfig, false)
|
||||
m.restartFolder(defaultFolderConfig, pausedDefaultFolderConfig)
|
||||
// Here folder initialization is not an issue as a paused folder isn't
|
||||
// added to the model and thus there is no initial scan happening.
|
||||
|
||||
@@ -2216,7 +2216,7 @@ func TestIndexesForUnknownDevicesDropped(t *testing.T) {
|
||||
t.Error("expected two devices")
|
||||
}
|
||||
|
||||
m.newFolder(defaultFolderConfig, false)
|
||||
m.newFolder(defaultFolderConfig)
|
||||
defer cleanupModel(m)
|
||||
|
||||
if devs, err := m.sdb.ListDevicesForFolder("default"); err != nil || len(devs) != 1 {
|
||||
@@ -3270,17 +3270,20 @@ func TestRenameSequenceOrder(t *testing.T) {
|
||||
t.Errorf("Unexpected count: %d != %d", count, numFiles)
|
||||
}
|
||||
|
||||
// Modify all the files, other than the ones we expect to rename
|
||||
// Modify all the files other than the rename sources, whose content we
|
||||
// keep intact so the renamed copies still match by block hash.
|
||||
for i := 0; i < numFiles; i++ {
|
||||
if i == 3 || i == 17 || i == 16 || i == 4 {
|
||||
if i == 3 || i == 16 {
|
||||
continue
|
||||
}
|
||||
v := fmt.Sprintf("%d", i)
|
||||
writeFile(t, ffs, v, []byte(v+"-new"))
|
||||
}
|
||||
// Rename
|
||||
must(t, ffs.Rename("3", "17"))
|
||||
must(t, ffs.Rename("16", "4"))
|
||||
// Rename to previously unseen names. Renaming onto an existing name is
|
||||
// treated as an in-place update rather than a rename, since rename
|
||||
// detection only runs for files that are new on disk.
|
||||
must(t, ffs.Rename("3", "20"))
|
||||
must(t, ffs.Rename("16", "21"))
|
||||
|
||||
// Scan
|
||||
m.ScanFolders()
|
||||
@@ -3291,10 +3294,10 @@ func TestRenameSequenceOrder(t *testing.T) {
|
||||
it, errFn := m.LocalFilesSequenced("default", protocol.LocalDeviceID, 0)
|
||||
for i := range it {
|
||||
t.Log(i)
|
||||
if i.FileName() == "17" {
|
||||
if i.FileName() == "20" {
|
||||
firstExpectedSequence = i.SequenceNo() + 1
|
||||
}
|
||||
if i.FileName() == "4" {
|
||||
if i.FileName() == "21" {
|
||||
secondExpectedSequence = i.SequenceNo() + 1
|
||||
}
|
||||
if i.FileName() == "3" {
|
||||
|
||||
@@ -250,7 +250,7 @@ func (*alwaysChanged) Changed() bool {
|
||||
func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderConfiguration) {
|
||||
t.Helper()
|
||||
m.removeFolder(fcfg)
|
||||
ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged()))
|
||||
ignores := ignore.New(fcfg.Filesystem(), ignore.WithChangeDetector(newAlwaysChanged()))
|
||||
m.mut.Lock()
|
||||
m.addAndStartFolderLockedWithIgnores(fcfg, ignores)
|
||||
m.mut.Unlock()
|
||||
|
||||
@@ -146,6 +146,8 @@ type FileInfo struct {
|
||||
Deleted bool
|
||||
NoPermissions bool
|
||||
|
||||
New bool // set by the scanner when the file newly appeared on disk; no db / wire representation
|
||||
|
||||
truncated bool // was created from a truncated file info without blocks
|
||||
}
|
||||
|
||||
@@ -233,7 +235,8 @@ func (f *FileInfo) LogAttr() slog.Attr {
|
||||
case FileInfoTypeFile:
|
||||
kind = "file"
|
||||
if !f.Deleted {
|
||||
attrs = append(attrs,
|
||||
attrs = append(
|
||||
attrs,
|
||||
slog.Any("modified", f.ModTime()),
|
||||
slog.String("permissions", fmt.Sprintf("0%03o", f.Permissions)),
|
||||
slog.Int64("size", f.Size),
|
||||
|
||||
+22
-3
@@ -267,9 +267,22 @@ func (w *walker) scan(ctx context.Context, toHashChan chan<- protocol.FileInfo,
|
||||
// isWarnableError returns true if err is a kind of error we should warn
|
||||
// about receiving from the folder walk.
|
||||
func isWarnableError(err error) bool {
|
||||
return err != nil &&
|
||||
!errors.Is(err, fs.SkipDir) && // intentional skip
|
||||
!errors.Is(err, context.Canceled) // folder restarting
|
||||
switch {
|
||||
case err == nil:
|
||||
return false
|
||||
case errors.Is(err, fs.SkipDir):
|
||||
// intentional skip
|
||||
return false
|
||||
case errors.Is(err, context.Canceled):
|
||||
// folder restarting
|
||||
return false
|
||||
case fs.IsNotExist(err):
|
||||
// sudden does-not-exist is fine for deleted files, deleted folder
|
||||
// root triggers health check error instead
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (w *walker) walkAndHashFiles(ctx context.Context, toHashChan chan<- protocol.FileInfo, finishedChan chan<- ScanResult) fs.WalkFunc {
|
||||
@@ -458,6 +471,8 @@ func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileIn
|
||||
f.RawBlockSize = int32(blockSize)
|
||||
l.Debugln(w, "checking:", f)
|
||||
|
||||
f.New = !hasCurFile
|
||||
|
||||
if hasCurFile {
|
||||
if curFile.IsEquivalentOptional(f, protocol.FileInfoComparison{
|
||||
ModTimeWindow: w.ModTimeWindow,
|
||||
@@ -505,6 +520,8 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo,
|
||||
f.NoPermissions = w.IgnorePerms
|
||||
l.Debugln(w, "checking:", f)
|
||||
|
||||
f.New = !hasCurFile
|
||||
|
||||
if hasCurFile {
|
||||
if curFile.IsEquivalentOptional(f, protocol.FileInfoComparison{
|
||||
ModTimeWindow: w.ModTimeWindow,
|
||||
@@ -557,6 +574,8 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, info fs.FileIn
|
||||
f = w.updateFileInfo(f, curFile)
|
||||
l.Debugln(w, "checking:", f)
|
||||
|
||||
f.New = !hasCurFile
|
||||
|
||||
if hasCurFile {
|
||||
if curFile.IsEquivalentOptional(f, protocol.FileInfoComparison{
|
||||
ModTimeWindow: w.ModTimeWindow,
|
||||
|
||||
@@ -749,7 +749,7 @@ func TestRecurseInclude(t *testing.T) {
|
||||
*
|
||||
`
|
||||
testFs := newTestFs()
|
||||
ignores := ignore.New(testFs, ignore.WithCache(true))
|
||||
ignores := ignore.New(testFs)
|
||||
if err := ignores.Parse(bytes.NewBufferString(stignore), ".stignore"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -859,7 +859,7 @@ func TestSkipIgnoredDirs(t *testing.T) {
|
||||
|
||||
w := &walker{}
|
||||
|
||||
pats := ignore.New(fss, ignore.WithCache(true))
|
||||
pats := ignore.New(fss)
|
||||
|
||||
stignore := `
|
||||
/foo/ign*
|
||||
@@ -892,7 +892,7 @@ func TestIncludedSubdir(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pats := ignore.New(fss, ignore.WithCache(true))
|
||||
pats := ignore.New(fss)
|
||||
|
||||
stignore := `
|
||||
!/foo/bar
|
||||
|
||||
+2
-3
@@ -554,7 +554,7 @@ func replaceRawPath(u *url.URL, rp string) {
|
||||
q = fs[1]
|
||||
}
|
||||
|
||||
if p[0] == '/' {
|
||||
if p != "" && p[0] == '/' {
|
||||
u.Path = p
|
||||
} else {
|
||||
u.Path += p
|
||||
@@ -607,6 +607,7 @@ func soapRequestWithIP(ctx context.Context, url, service, function, message stri
|
||||
l.Debugln("SOAP do:", err)
|
||||
return resp, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
resp, err = io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -616,8 +617,6 @@ func soapRequestWithIP(ctx context.Context, url, service, function, message stri
|
||||
|
||||
l.Debugf("SOAP Response: %s\n\n%s\n\n", r.Status, resp)
|
||||
|
||||
r.Body.Close()
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
return resp, errors.New(function + ": " + r.Status)
|
||||
}
|
||||
|
||||
@@ -79,3 +79,16 @@ func TestControlURLParsing(t *testing.T) {
|
||||
t.Error("URL normalization of", subject, "failed; expected", expected, "got", u.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestControlURLParsingQueryOnly(t *testing.T) {
|
||||
rootURL := "http://192.168.243.1:80/igd.xml"
|
||||
|
||||
u, _ := url.Parse(rootURL)
|
||||
subject := "?control=WANCommonIFC1"
|
||||
expected := "http://192.168.243.1:80/igd.xml?control=WANCommonIFC1"
|
||||
replaceRawPath(u, subject)
|
||||
|
||||
if u.String() != expected {
|
||||
t.Error("URL normalization of", subject, "failed; expected", expected, "got", u.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@ type Report struct {
|
||||
NATType string `json:"natType,omitempty" metric:"nat_detection,gaugeVec:type" since:"3"`
|
||||
|
||||
AlwaysLocalNets bool `json:"alwaysLocalNets,omitempty" metric:"feature_count{feature=AlwaysLocalNets},gauge" since:"3"`
|
||||
CacheIgnoredFiles bool `json:"cacheIgnoredFiles,omitempty" metric:"feature_count{feature=CacheIgnoredFiles},gauge" since:"3"`
|
||||
OverwriteRemoteDeviceNames bool `json:"overwriteRemoteDeviceNames,omitempty" metric:"feature_count{feature=OverwriteRemoteDeviceNames},gauge" since:"3"`
|
||||
ProgressEmitterEnabled bool `json:"progressEmitterEnabled,omitempty" metric:"feature_count{feature=ProgressEmitterEnabled},gauge" since:"3"`
|
||||
CustomDefaultFolderPath bool `json:"customDefaultFolderPath,omitempty" metric:"feature_count{feature=CustomDefaultFolderPath},gauge" since:"3"`
|
||||
|
||||
@@ -217,7 +217,6 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
|
||||
report.Uptime = s.UptimeS()
|
||||
report.NATType = s.connectionsService.NATType()
|
||||
report.AlwaysLocalNets = len(opts.AlwaysLocalNets) > 0
|
||||
report.CacheIgnoredFiles = opts.CacheIgnoredFiles
|
||||
report.OverwriteRemoteDeviceNames = opts.OverwriteRemoteDevNames
|
||||
report.ProgressEmitterEnabled = opts.ProgressUpdateIntervalS > -1
|
||||
report.CustomDefaultFolderPath = defaultFolder.Path != "~"
|
||||
|
||||
@@ -111,6 +111,9 @@ func (v external) prepareCommand(filePath string) (*exec.Cmd, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("command is invalid: %w", err)
|
||||
}
|
||||
if len(words) == 0 {
|
||||
return nil, errors.New("command is empty")
|
||||
}
|
||||
|
||||
context := map[string]string{
|
||||
"%FOLDER_FILESYSTEM%": string(v.filesystem.Type()),
|
||||
|
||||
@@ -109,6 +109,20 @@ func TestExternalCommandSplit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternalWhitespaceCommand(t *testing.T) {
|
||||
// A whitespace-only command splits into zero words; prepareCommand must
|
||||
// return an error rather than panicking on words[0].
|
||||
e := external{
|
||||
filesystem: fs.NewFilesystem(fs.FilesystemTypeFake, "TestExternalWhitespaceCommand"),
|
||||
}
|
||||
for _, cmd := range []string{" ", "\t", "\n", " \t \n"} {
|
||||
e.command = cmd
|
||||
if _, err := e.prepareCommand("file"); err == nil {
|
||||
t.Errorf("expected error for command %q, got nil", cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prepForRemoval(t *testing.T, file string) {
|
||||
if err := os.RemoveAll("testdata"); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "STDISCOSRV" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "STRELAYSRV" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.SH INTRODUCTION AND DEFINITIONS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.SH OVERVIEW
|
||||
@@ -711,7 +711,7 @@ Added in version 2.1.0.
|
||||
|
||||
.sp
|
||||
Whether to use block indexing for the folder; defaults to \fBtrue\fP for
|
||||
send\-only and receive\-only folders. Block indexing makes it possible for
|
||||
send\-receive and receive\-only folders. Block indexing makes it possible for
|
||||
Syncthing to reuse data blocks between files (even in different
|
||||
folders), but requires maintaining an index of every individual block’s
|
||||
hash. This index can be costly for large folders, so indexing can be
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.SH DESCRIPTION
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.INDENT 0.0
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.SH ANNOUNCEMENTS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.SH MODE OF OPERATION
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.SH ROUTER SETUP
|
||||
|
||||
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-RELAY" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-RELAY" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.SH WHAT IS A RELAY?
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-REST-API" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-SECURITY" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.sp
|
||||
@@ -122,13 +122,13 @@ This versioning strategy delegates the decision on what to do to an
|
||||
command line script). Just prior to a file being replaced, the command will be
|
||||
executed. The file needs to be removed from the folder in the process, or
|
||||
otherwise Syncthing will report an error. The command can use the following
|
||||
templated arguments:
|
||||
templated arguments or environment variables:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B %FOLDER_PATH%
|
||||
.B \fB%FOLDER_PATH%\fP (\fB$FOLDER_PATH\fP)
|
||||
Path to the folder
|
||||
.TP
|
||||
.B %FILE_PATH%
|
||||
.B \fB%FILE_PATH%\fP (\fB$FILE_PATH\fP)
|
||||
Path to the file within the folder
|
||||
.UNINDENT
|
||||
.sp
|
||||
@@ -138,6 +138,28 @@ the default \fBSync\fP folder in Windows, and the full path to the file is
|
||||
\fBC:\eUsers\eUser\eSync\eFamily photos\eIMG_2021\-03\-01.jpg\fP, then the
|
||||
\fB%FOLDER_PATH%\fP will be \fBC:\eUsers\eUser\eSync\fP, and the
|
||||
\fB%FILE_PATH%\fP will be \fBFamily photos\eIMG_2021\-03\-01.jpg\fP\&.
|
||||
.sp
|
||||
\fBWARNING:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
You should consider file paths as untrusted user input and treat them
|
||||
accordingly. Do not assume that they are free from malicious characters
|
||||
or commands.
|
||||
.sp
|
||||
Syncthing will properly ensure unquoted template variables are passed as
|
||||
individual parameter values, as in \fBsomecommand %FOLDER_PATH%
|
||||
%FILE_PATH%\fP\&. An unquoted combination such as
|
||||
\fB%FOLDER_PATH%/%FILE_PATH%\fP is also valid.
|
||||
.sp
|
||||
Any form of shell wrapping in the command, such as sh \-c “echo
|
||||
%FOLDER_PATH%/%FILE_PATH%”, is invalid. Syncthing may reject such
|
||||
commands as unsafe.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Added in version 2.1.2: The environment variables \fB$FOLDER_PATH\fP and \fB$FILE_PATH\fP which can
|
||||
be used in the script instead of the command line template placeholders.
|
||||
|
||||
.SS Example for Unixes
|
||||
.sp
|
||||
Let’s say I want to keep the latest version of each file as they are replaced
|
||||
|
||||
+4
-5
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "Jul 23, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.SH SYNOPSIS
|
||||
@@ -439,14 +439,13 @@ Display database file metadata.
|
||||
Syncthing can use a SOCKS, HTTP, or HTTPS proxy to talk to the outside
|
||||
world. The proxy is used for outgoing connections only \- it is not possible
|
||||
to accept incoming connections through the proxy. The proxy is configured
|
||||
through the environment variable \fBall_proxy\fP\&. Somewhat unusually, this
|
||||
variable must be named in lower case \- it is not “ALL_PROXY”. For
|
||||
example:
|
||||
through the environment variable \fBALL_PROXY\fP or its lowercase variant
|
||||
\fBall_proxy\fP\&. If both are set, \fBALL_PROXY\fP takes precedence. For example:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.EX
|
||||
$ export all_proxy=socks://192.0.2.42:8081
|
||||
$ export ALL_PROXY=socks://192.0.2.42:8081
|
||||
.EE
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
|
||||
@@ -99,7 +99,6 @@
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>true</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
|
||||
@@ -117,7 +117,6 @@
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
|
||||
Reference in New Issue
Block a user