Compare commits

...
47 Commits
Author SHA1 Message Date
bt90andGitHub 467522d04d lib/connections: Allow IPv6 ULA in discovery announcements (fixes #7456) (#9048)
The allowed IPv4 ranges are the same as before. But we now also accept IPv6 addresses in the ULA range FC00::/7. These addresses don't require an interface identifier and are roughly equivalent to the IPv4 private ranges.

Typical usecases:

VPN interface IPs: Wireguard, OpenVPN, Tailscale, ...
fixed IPv6 LAN addressing while the provider assigns a dynamic prefix. e.g used by pihole
https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/net/ip.go;l=146
2023-08-23 12:28:48 +02:00
bt90andGitHub 3147285c60 lib/beacon: Check FlagRunning (#9051) 2023-08-22 11:27:43 +02:00
Jakob BorgandGitHub acd767b30b all: Remove lib/util package (#9049)
Grab-bag packages are nasty, this cleans it up a little by splitting it
into topical packages sempahore, netutil, stringutil, structutil.
2023-08-21 19:44:33 +02:00
40b3b9ad15 lib/model: Clean up index handler life cycle (fixes #9021) (#9038)
Co-authored-by: Simon Frei <freisim93@gmail.com>
2023-08-21 18:39:13 +02:00
bt90andGitHub c2c6133aa5 lib/osutil, lib/upnp: Check FlagRunning (fixes #8767) (#9047) 2023-08-21 14:49:28 +00:00
Jakob BorgandGitHub ccec8a4cdb build: Update dependencies (#9046) 2023-08-21 15:56:02 +02:00
Jakob BorgandGitHub cbf0e31f69 all: Use Go 1.21, new QUIC API (#9040) 2023-08-21 15:25:52 +02:00
Syncthing Release Automation c40dae315b gui, man, authors: Update docs, translations, and contributors 2023-08-21 03:45:38 +00:00
Jakob Borg ac0ce1c38f script: Remove find-metrics which belongs in docs 2023-08-17 12:27:56 +02:00
Jakob Borg 72c683aaca gui: Fix inadvertently always-false comparison (ref #7726) 2023-08-16 11:51:45 +02:00
Syncthing Release Automation 8042bd1a54 gui, man, authors: Update docs, translations, and contributors 2023-08-14 03:45:48 +00:00
Jakob Borg 462389934b cmd/stupgrades: Serve friendlier URLs for upgrade assets (fixes #9033) 2023-08-09 21:01:15 +02:00
Jakob Borg b347c14bd1 build: Use correct range specification for Go version
The old `^1.20.7` means `1.x.y, >= 1.20.7` which allows 1.21.0, which
was not intended. The new `~1.20.7` means `1.20.x, >= 1.20.7`, which is
safer.
2023-08-09 16:05:11 +02:00
Jakob Borg 8dfec6983b build: WASM is not a thing we need to try to compile for 2023-08-09 11:02:43 +02:00
Jakob Borg 9ebf2dae7b build: Ability to manually trigger Actions builds 2023-08-09 10:50:07 +02:00
André ColombandGitHub a8cacdca94 lib/versioner: Minor fixes in comments and error message (#9031)
* lib/versioner: Factor out DefaultPath constant.

Replace several instances where .stversions is named literally to all
use the same definition in the versioner package.  Exceptions are the
packages where a cyclic dependency on versioner is impossible, or some
tests which combine the versions base path with other components.

* lib/versioner: Fix comment about trash can in simple versioner.

* lib/versioner: Fix wrong versioning type string in error message.

The error message shows the folder type instead of the versioning
type, although the correct field is used in the comparison.
2023-08-09 07:10:06 +00:00
Jakob BorgandGitHub 8b87cd5229 lib/model: Reinstate setting folder idle state (#9029) 2023-08-08 07:24:02 +02:00
Syncthing Release Automation e09146ee03 gui, man, authors: Update docs, translations, and contributors 2023-08-07 03:45:35 +00:00
Jakob BorgandGitHub b9c08d3814 all: Add Prometheus-style metrics to expose some internal performance counters (fixes #5175) (#9003) 2023-08-04 19:57:30 +02:00
Jakob Borg 58042b3129 build: Increase Go version to 1.20.7 2023-08-03 08:11:16 +02:00
Keith HarrisonandGitHub eed12f3ec5 lib/config: Allow sharing already encrypted folder with untrusted devices (fixes #8965) (#9012)
Safety check added in v1.23.6 introduced bug. Bug unshares folders with untrusted devices if folder does not have an encryption password set, regardless of whether the folder is shared with the untrusted device as encrypted or not. Prevents sharing with untrusted devices in some cases where sharing would be encrypted.

Patch preserves safety check but permits sharing folders with untrusted devices if they are shared as encrypted.

Signed-off-by: kewiha <keithh@protonmail.com>
2023-08-02 07:14:53 +00:00
tomasz1986andGitHub 5323928159 gui: Use case-insensive and backslash-agnostic versions filter (fixes #7973) (#8995)
Currently, the versions filter is case-sensitive regardless of the
underlying OS. With this change, the filter becomes case-insensitive
everywhere, which is more user-friendly and makes it easier to search
for files whose exact case the user may not remember.

In addition, forward and backslashes are no longer distinguished,
whether used as path separators or as part of a file / directory
name (which is unlikely but possible on some platforms).

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-08-01 14:20:01 +02:00
Syncthing Release Automation 97625ccc26 gui, man, authors: Update docs, translations, and contributors 2023-07-31 03:45:37 +00:00
Jakob Borg 4fe746d9aa build: Run govulncheck (fixes #8983) 2023-07-30 14:38:36 +02:00
Jakob Borg 4f8cdd41ee build: Run build & tests on main branch nightly 2023-07-30 14:24:17 +02:00
Jakob Borg 406e3646e5 build: Send test logs to Grafana Loki for statistics 2023-07-30 13:40:26 +02:00
Jakob BorgandGitHub 9d21b91124 all: Refactor the protocol/model interface a bit (ref #8981) (#9007) 2023-07-29 10:24:44 +02:00
Chih-Hsuan YenandGitHub b806026990 lib/connections: Fix building with -tags noquic (#9009) 2023-07-28 10:08:50 +00:00
tomasz1986andGitHub 341b79814e gui: Fix tooltips on buttons inside button groups (ref #7984) (#9008)
As per Bootstrap recommendation, buttons with tooltips inside button
groups require to have container: 'body' set. This prevents tooltips
from causing the buttons to jump on hover and also allows the tooltips
to be wider instead of wrapping on every space.

Ref: https://getbootstrap.com/docs/3.3/components/#btn-groups

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2023-07-27 14:38:48 +02:00
Jakob BorgandGitHub 319916124b cmd/strelaysrv: Handle accept error with debug set (fixes #9001) (#9004) 2023-07-26 23:55:48 +01:00
Emil LundbergandGitHub b08b99e284 lib/api: Fix data race in TestCSRFRequired (#9006) 2023-07-26 21:33:45 +00:00
Jakob BorgandGitHub f565df628c gui: Show full error for failed items (#9005)
Also closes #8992.
2023-07-26 23:20:17 +02:00
Jakob BorgandGitHub 855c6dc67b lib/api: Allow Bearer authentication style with API key (#9002)
Currently, historically, we look for the `X-API-Key` header to
authenticate with an API key. There's nothing wrong with this, but in
some scenarios it's easier to produce an `Authorization` header with a
`Bearer $token` content, which is nowadays more common. This change adds
support for both, so that we will accept an API key either in our custom
header or as a bearer token.
2023-07-26 13:13:06 +02:00
tomasz1986andGitHub dc5e10fa2c gui: Remove Twitter link from footer (#9000) 2023-07-25 00:53:57 +02:00
Syncthing Release Automation b857e57a35 gui, man, authors: Update docs, translations, and contributors 2023-07-24 03:45:42 +00:00
f42f041f53 lib/ur: Don't report uptime if start time is in the past (fixes #7698) (#8996)
Currently, because of devices with unset RTC clock, the 100% percentile
for Uptime on [1] is calculated since the Unix epoch which is useless as
far as usage statistics are concerned. Thus, if the Syncthing start time
is set to a past date, assume that the clock is wrong and do not even
try to report the uptime.

[1] https://data.syncthing.net

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
Co-authored-by: Jakob Borg <jakob@kastelo.net>
2023-07-22 21:25:03 +00:00
Christian KujauandGitHub 6b6b2c6194 lib/model: use WARN for "Unexpected folder" messages (#8998) 2023-07-22 21:17:32 +00:00
tomasz1986andGitHub d70eb569f2 lib/osutil: Skip setLowPriority in Windows if already lower (fixes #6597) (#8993) 2023-07-21 04:38:15 +00:00
deepsource-autofix[bot]GitHubdeepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
21c074cc2c all: replace empty slice literal with var (#8990)
refactor: replace empty slice literal with `var`

An empty slice can be represented by `nil` or an empty slice literal. They are
functionally equivalent — their `len` and `cap` are both zero — but the `nil`
slice is the preferred style. For more information about empty slices,
see [Declaring Empty Slices](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices).

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2023-07-18 14:44:37 +00:00
deepsource-autofix[bot]GitHubdeepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
f23c41221b all: fix unused method receiver (#8988)
refactor: fix unused method receiver

Methods with unused receivers can be a symptom of unfinished refactoring or a bug. To keep 
the same method signature, omit the receiver name or '_' as it is unused.

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2023-07-18 14:34:50 +00:00
deepsource-autofix[bot]GitHubdeepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
24e230d455 all: unused parameter should be replaced by underscore (#8989)
refactor: unused parameter should be replaced by underscore

Unused parameters in functions or methods should be replaced with `_`
(underscore) or removed.

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2023-07-18 14:33:13 +00:00
Syncthing Release Automation 31daa20367 gui, man, authors: Update docs, translations, and contributors 2023-07-17 03:46:46 +00:00
Jakob Borg e4d0f9dd6c cmd/syncthing: Mention STVERSIONEXTRA in --help output (ref #8980) 2023-07-16 17:48:24 +02:00
Jakob BorgandGitHub df2ac7aaeb gui, lib/api: Add possibility to feed through extra version information (#8980)
This adds an environment variable STVERSIONEXTRA that, when set, gets
added to the version information in the API and GUI.

The purpose of all this is to be able to communicate something about the
bundling or packaging, through the log & GUI and the end user, to the
potential person supporting it -- i.e., us. :) A wrapper can set this
variable to indicate that Syncthing is being run via `SyncTrayzor`,
`Syncthing-macOS`, etc., and thus indicate to the end user that the GUI
they are looking at is perhaps not the only source of truth and
management for this instance.
2023-07-16 17:43:10 +02:00
Jakob Borg b96b23957b cmd/ursrv: Update map tile URL 2023-07-16 17:36:05 +02:00
bt90andGitHub 265ce139c5 cmd/strelaypoolsrv: Update map tile URL (#8985) 2023-07-16 17:20:40 +02:00
Jakob Borg 48c95eb41d cmd/stcrashreceiver: Correct parsing of current version string 2023-07-12 09:27:34 +02:00
181 changed files with 2783 additions and 1321 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
- infrastructure
env:
GO_VERSION: "^1.20.5"
GO_VERSION: "^1.21.0"
CGO_ENABLED: "0"
BUILD_USER: docker
BUILD_HOST: github.syncthing.net
+41 -3
View File
@@ -3,12 +3,16 @@ name: Build Syncthing
on:
pull_request:
push:
schedule:
# Run nightly build at 05:00 UTC
- cron: '00 05 * * *'
workflow_dispatch:
env:
# The go version to use for builds. We set check-latest to true when
# installing, so we get the latest patch version that matches the
# expression.
GO_VERSION: "^1.20.3"
GO_VERSION: "~1.21.0"
# Optimize compatibility on the slow archictures.
GO386: softfloat
@@ -44,7 +48,7 @@ jobs:
runner: ["windows-latest", "ubuntu-latest", "macos-latest"]
# The oldest version in this list should match what we have in our go.mod.
# Variables don't seem to be supported here, or we could have done something nice.
go: ["1.19", "1.20"]
go: ["1.20", "1.21"]
runs-on: ${{ matrix.runner }}
steps:
- name: Set git to use LF
@@ -69,9 +73,19 @@ jobs:
run: |
go run build.go
- name: Install go-test-json-to-loki
run: |
go install calmh.dev/go-test-json-to-loki@latest
- name: Test
run: |
go run build.go test
go run build.go test | go-test-json-to-loki
env:
GOFLAGS: "-json"
LOKI_URL: ${{ secrets.LOKI_URL }}
LOKI_USER: ${{ secrets.LOKI_USER }}
LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }}
LOKI_LABELS: "go=${{ matrix.go }},runner=${{ matrix.runner }},repo=${{ github.repository }},ref=${{ github.ref }}"
#
# Meta checks for formatting, copyright, etc
@@ -112,6 +126,7 @@ jobs:
- package-cross
- package-source
- package-debian
- govulncheck
steps:
- uses: actions/checkout@v3
@@ -375,6 +390,7 @@ jobs:
| grep -v nacl/ \
| grep -v plan9/ \
| grep -v windows/ \
| grep -v /wasm \
)
for plat in $platforms; do
@@ -749,3 +765,25 @@ jobs:
platforms: linux/amd64,linux/arm64,linux/arm/7
push: ${{ env.DOCKER_PUSH == 'true' }}
tags: ${{ env.DOCKER_TAGS }}
#
# Check for known vulnerabilities in Go dependencies
#
govulncheck:
runs-on: ubuntu-latest
name: Run govulncheck
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
cache: false
check-latest: true
- name: run govulncheck
run: |
go run build.go assets
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
+4 -1
View File
@@ -71,11 +71,12 @@ Carsten Hagemann (carstenhag) <moter8@gmail.com> <carsten@chagemann.de>
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
Cedric Staniewski (xduugu) <cedric@gmx.ca>
chenrui <rui@meetup.com>
Chih-Hsuan Yen <yan12125@gmail.com>
Chih-Hsuan Yen <yan12125@gmail.com> <1937689+yan12125@users.noreply.github.com>
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
Chris Howie (cdhowie) <me@chrishowie.com>
Chris Joel (cdata) <chris@scriptolo.gy>
Chris Tonkinson <chris@masterbran.ch>
Christian Kujau <ckujau@users.noreply.github.com>
Christian Prescott <me@christianprescott.com>
chucic <chucic@seznam.cz>
Colin Kennedy (moshen) <moshen.colin@gmail.com>
@@ -105,6 +106,7 @@ Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
Elliot Huffman <thelich2@gmail.com>
Emil Hessman (ceh) <emil@hessman.se>
Emil Lundberg <emil@emlun.se>
Eng Zer Jun <engzerjun@gmail.com>
entity0xfe <109791748+entity0xfe@users.noreply.github.com> <entity0xfe@my.domain>
Eric Lesiuta <elesiuta@gmail.com>
@@ -175,6 +177,7 @@ K.B.Dharun Krishna <kbdharunkrishna@gmail.com>
Kalle Laine <pahakalle@protonmail.com>
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
Kebin Liu <lkebin@gmail.com>
Keith Harrison <keithh@protonmail.com>
Keith Turner <kturner@apache.org>
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
+20 -3
View File
@@ -215,7 +215,13 @@ func crashReportFingerprint(message string) []string {
}
// syncthing v1.1.4-rc.1+30-g6aaae618-dirty-crashrep "Erbium Earthworm" (go1.12.5 darwin-amd64) jb@kvin.kastelo.net 2019-05-23 16:08:14 UTC [foo, bar]
var longVersionRE = regexp.MustCompile(`syncthing\s+(v[^\s]+)\s+"([^"]+)"\s\(([^\s]+)\s+([^-]+)-([^)]+)\)\s+([^\s]+)[^\[]*(?:\[(.+)\])?$`)
// or, somewhere along the way the "+" in the version tag disappeared:
// syncthing v1.23.7-dev.26.gdf7b56ae.dirty-stversionextra "Fermium Flea" (go1.20.5 darwin-arm64) jb@ok.kastelo.net 2023-07-12 06:55:26 UTC [Some Wrapper, purego, stnoupgrade]
var (
longVersionRE = regexp.MustCompile(`syncthing\s+(v[^\s]+)\s+"([^"]+)"\s\(([^\s]+)\s+([^-]+)-([^)]+)\)\s+([^\s]+)[^\[]*(?:\[(.+)\])?$`)
gitExtraRE = regexp.MustCompile(`\.\d+\.g[0-9a-f]+`) // ".1.g6aaae618"
gitExtraSepRE = regexp.MustCompile(`[.-]`) // dot or dash
)
type version struct {
version string // "v1.1.4-rc.1+30-g6aaae618-dirty-crashrep"
@@ -257,10 +263,21 @@ func parseVersion(line string) (version, error) {
builder: m[6],
}
parts := strings.Split(v.version, "+")
// Split the version tag into tag and commit. This is old style
// v1.2.3-something.4+11-g12345678 or newer with just dots
// v1.2.3-something.4.11.g12345678 or v1.2.3-dev.11.g12345678.
parts := []string{v.version}
if strings.Contains(v.version, "+") {
parts = strings.Split(v.version, "+")
} else {
idxs := gitExtraRE.FindStringIndex(v.version)
if len(idxs) > 0 {
parts = []string{v.version[:idxs[0]], v.version[idxs[0]+1:]}
}
}
v.tag = parts[0]
if len(parts) > 1 {
fields := strings.Split(parts[1], "-")
fields := gitExtraSepRE.Split(parts[1], -1)
if len(fields) >= 2 && strings.HasPrefix(fields[1], "g") {
v.commit = fields[1][1:]
}
+14
View File
@@ -44,6 +44,20 @@ func TestParseVersion(t *testing.T) {
extra: []string{"foo", "bar"},
},
},
{
longVersion: `syncthing v1.23.7-dev.26.gdf7b56ae-stversionextra "Fermium Flea" (go1.20.5 darwin-arm64) jb@ok.kastelo.net 2023-07-12 06:55:26 UTC [Some Wrapper, purego, stnoupgrade]`,
parsed: version{
version: "v1.23.7-dev.26.gdf7b56ae-stversionextra",
tag: "v1.23.7-dev",
commit: "df7b56ae",
codename: "Fermium Flea",
runtime: "go1.20.5",
goos: "darwin",
goarch: "arm64",
builder: "jb@ok.kastelo.net",
extra: []string{"Some Wrapper", "purego", "stnoupgrade"},
},
},
}
for _, tc := range cases {
+1 -1
View File
@@ -237,7 +237,7 @@
uptimeSeconds: 0,
};
$scope.map = L.map('map').setView([40.90296, 1.90925], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{
attribution: 'Leaflet',
maxZoom: 17
+7 -1
View File
@@ -36,8 +36,14 @@ func listener(_, addr string, config *tls.Config, token string) {
for {
conn, isTLS, err := listener.AcceptNoWrapTLS()
if err != nil {
// Conn may be nil if accept failed, or non-nil if the initial
// read to figure out if it's TLS or not failed. In the latter
// case, close the connection before moving on.
if conn != nil {
conn.Close()
}
if debug {
log.Println("Listener failed to accept connection from", conn.RemoteAddr(), ". Possibly a TCP Ping.")
log.Println("Listener failed to accept:", err)
}
continue
}
+11 -1
View File
@@ -57,7 +57,7 @@ type githubReleases struct {
url string
}
func (p *githubReleases) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (p *githubReleases) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
log.Println("Fetching", p.url)
rels := upgrade.FetchLatestReleases(p.url, "")
if rels == nil {
@@ -68,6 +68,16 @@ func (p *githubReleases) ServeHTTP(w http.ResponseWriter, req *http.Request) {
sort.Sort(upgrade.SortByRelease(rels))
rels = filterForLatest(rels)
// Move the URL used for browser downloads to the URL field, and remove
// the browser URL field. This avoids going via the GitHub API for
// downloads, since Syncthing uses the URL field.
for _, rel := range rels {
for j, asset := range rel.Assets {
rel.Assets[j].URL = asset.BrowserURL
rel.Assets[j].BrowserURL = ""
}
}
buf := new(bytes.Buffer)
_ = json.NewEncoder(buf).Encode(rels)
+5
View File
@@ -99,6 +99,11 @@ above.
"minio" for the github.com/minio/sha256-simd implementation,
and blank (the default) for auto detection.
STVERSIONEXTRA Add extra information to the version string in logs and the
version line in the GUI. Can be set to the name of a wrapper
or tool controlling syncthing to communicate this to the end
user.
GOMAXPROCS Set the maximum number of CPU cores to use. Defaults to all
available CPU cores.
+1 -1
View File
@@ -197,7 +197,7 @@ found in the LICENSE file.
};
var baseLayer = L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: '...',
maxZoom: 18
}
+16 -18
View File
@@ -1,9 +1,8 @@
module github.com/syncthing/syncthing
go 1.19
go 1.20
require (
github.com/AudriusButkevicius/pfilter v0.0.11
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
github.com/alecthomas/kong v0.8.0
github.com/calmh/incontainer v0.0.0-20221224152218-b3e71b103d7a
@@ -22,7 +21,7 @@ require (
github.com/gogo/protobuf v1.3.2
github.com/golang/snappy v0.0.4 // indirect
github.com/greatroar/blobloom v0.7.2
github.com/hashicorp/golang-lru/v2 v2.0.4
github.com/hashicorp/golang-lru/v2 v2.0.5
github.com/jackpal/gateway v1.0.10
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
@@ -38,45 +37,44 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.0 // indirect
github.com/quic-go/quic-go v0.34.0
github.com/prometheus/procfs v0.11.1 // indirect
github.com/quic-go/quic-go v0.38.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/sasha-s/go-deadlock v0.3.1
github.com/shirou/gopsutil/v3 v3.23.6
github.com/shirou/gopsutil/v3 v3.23.7
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.2
github.com/urfave/cli v1.22.14
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.11.0
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.12.0
golang.org/x/sys v0.10.0
golang.org/x/text v0.11.0
golang.org/x/net v0.14.0
golang.org/x/sys v0.11.0
golang.org/x/text v0.12.0
golang.org/x/time v0.3.0
golang.org/x/tools v0.11.0
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846
google.golang.org/protobuf v1.31.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 // indirect
)
// https://github.com/gobwas/glob/pull/55
+31 -33
View File
@@ -1,5 +1,3 @@
github.com/AudriusButkevicius/pfilter v0.0.11 h1:6emuvqNeH1gGlqkML35pEizyPcaxdAN4JO9sdgwcx78=
github.com/AudriusButkevicius/pfilter v0.0.11/go.mod h1:4eF1UYuEhoycTlr9IOP1sb0lL9u4nfAIouRqt2xJbzM=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
@@ -50,8 +48,9 @@ github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8=
github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
@@ -81,12 +80,12 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/greatroar/blobloom v0.7.2 h1:F30MGLHOcb4zr0pwCPTcKdlTM70rEgkf+LzdUPc5ss8=
github.com/greatroar/blobloom v0.7.2/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/hashicorp/golang-lru/v2 v2.0.4 h1:7GHuZcgid37q8o5i3QI9KMT4nCWQQ3Kx3Ov6bb9MfK0=
github.com/hashicorp/golang-lru/v2 v2.0.4/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -136,11 +135,11 @@ github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 h1:W04oB3d0J01W5jgYRGKsV8LCM6g9EkCvPkZcmFuy0OE=
github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b h1:vab8deKC4QoIfm9fJM59iuNz1ELGsuLoYYpiF+pHiG8=
github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -157,14 +156,12 @@ github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUo
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU=
github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -172,8 +169,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08=
github.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -210,10 +207,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 h1:FqrVOBQxQ8r/UwwXibI0KMolVhvFiGobSfdE33deHJM=
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -235,8 +232,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -271,12 +268,13 @@ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -287,8 +285,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -299,8 +297,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/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=
-7
View File
@@ -574,13 +574,6 @@ html[lang|="ko"] i {
font-style: normal;
}
/* Prevent buttons from jumping up and down
when a tooltip is shown for one of them. */
.btn-group-vertical > .tooltip + .btn,
.btn-group-vertical > .tooltip + .btn-group {
margin-top: -1px;
}
.select-on-click {
-webkit-user-select: all;
user-select: all;
-1
View File
@@ -316,7 +316,6 @@
"This is a major version upgrade.": "ترقية أساسية ",
"Time": "الوقت",
"Time the item was last modified": "توقيت اخر تعديل للعنصر",
"Twitter": "Twitter",
"Type": "نوع",
"Unavailable": "غير متوفر",
"Unavailable/Disabled by administrator or maintainer": "غير متوفر/معطل من قبل المسؤول أو الصيانة",
-1
View File
@@ -105,7 +105,6 @@
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "The maximum time to keep a version (in days, set to 0 to keep versions forever).",
"The number of old versions to keep, per file.": "Колькі старых вэрсій трымаць, для кожнага файлу.",
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"Twitter": "Twitter",
"Unknown": "Невядома",
"Up to Date": "Найноўшае",
"Upgrade To {%version%}": "Upgrade To {{version}}",
+2 -3
View File
@@ -2,7 +2,7 @@
"A device with that ID is already added.": "Устройство с този идентификатор вече е добавено.",
"A negative number of days doesn't make sense.": "Отрицателният брой дни е безсмислен.",
"A new major version may not be compatible with previous versions.": "Ново значимо издание, което може да е несъвместимо с предните издания.",
"API Key": "Ключ за ППИ",
"API Key": "API Ключ",
"About": "Относно",
"Action": "Действие",
"Actions": "Действия",
@@ -94,7 +94,7 @@
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство \"{{name}}\" ({{device}}) с адрес {{address}} желае да се свърже. Да бъде ли добавено?",
"Device Certificate": "Сертификат на устройството",
"Device ID": "Идентификатор на устройство",
"Device Identification": "Идентификатор на устройство",
"Device Identification": "Идентификация на устройство",
"Device Name": "Име на устройството",
"Device is untrusted, enter encryption password": "Устройството е недоверено, въведете парола за шифроване",
"Device rate limits": "Ограничаване на скоростта",
@@ -448,7 +448,6 @@
"Today": "Днес",
"Trash Can": "Кошче за отпадъци",
"Trash Can File Versioning": "Версии от вида „кошче за отпадъци“",
"Twitter": "Twitter",
"Type": "Вид",
"UNIX Permissions": "Права на UNIX",
"Unavailable": "Няма налични",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Avui",
"Trash Can": "Paperera",
"Trash Can File Versioning": "Paperera de versionat de fitxers",
"Twitter": "Twitter",
"Type": "Tipus",
"UNIX Permissions": "Permisos UNIX",
"Unavailable": "No disponible",
@@ -448,7 +448,6 @@
"Today": "Avui",
"Trash Can": "Paperera",
"Trash Can File Versioning": "Versionat d'arxius de la paperera",
"Twitter": "Twitter",
"Type": "Tipus",
"UNIX Permissions": "Permisos UNIX",
"Unavailable": "No disponible",
-1
View File
@@ -415,7 +415,6 @@
"Today": "Dnes",
"Trash Can": "Koš",
"Trash Can File Versioning": "Ponechávat jednu předchozí verzi (jako Koš) ",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX oprávnění",
"Unavailable": "Nedostupné",
-1
View File
@@ -448,7 +448,6 @@
"Today": "I dag",
"Trash Can": "Affaldskurv",
"Trash Can File Versioning": "Versionering med papirkurv",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX rettigheder",
"Unavailable": "Ikke tilgængelig",
+1 -2
View File
@@ -422,7 +422,7 @@
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Das Intervall, in Sekunden, zwischen den Bereinigungen im Versionsverzeichnis. Null um das regelmäßige Bereinigen zu deaktivieren.",
"The maximum age must be a number and cannot be blank.": "Das Höchstalter muss angegeben werden und eine Zahl sein.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Die längste Zeit, die alte Versionen vorgehalten werden (in Tagen) (0 um alte Versionen für immer zu behalten).",
"The number of days must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
"The number of days must be a number and cannot be blank.": "Die Anzahl der Tage muss eine Ganzzahl sein und darf nicht leer sein.",
"The number of days to keep files in the trash can. Zero means forever.": "Dauer in Tagen für welche die Dateien aufgehoben werden sollen. 0 bedeutet für immer.",
"The number of old versions to keep, per file.": "Anzahl der alten Versionen, die von jeder Datei behalten werden sollen.",
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
@@ -448,7 +448,6 @@
"Today": "Heute",
"Trash Can": "Papierkorb",
"Trash Can File Versioning": "Papierkorb Dateiversionierung",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX-Berechtigungen",
"Unavailable": "Nicht verfügbar",
-1
View File
@@ -314,7 +314,6 @@
"Time": "Χρόνος",
"Time the item was last modified": "Ώρα τελευταίας τροποποίησης του στοιχείου",
"Trash Can File Versioning": "Τήρηση εκδόσεων κάδου ανακύκλωσης",
"Twitter": "Twitter",
"Type": "Τύπος",
"UNIX Permissions": "Άδειες αρχείων UNIX",
"Unavailable": "Μη διαθέσιμο",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Today",
"Trash Can": "Trash Can",
"Trash Can File Versioning": "Bin File Versioning",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Unavailable",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Today",
"Trash Can": "Rubbish Bin",
"Trash Can File Versioning": "Rubbish Bin File Versioning",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Unavailable",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Today",
"Trash Can": "Trash Can",
"Trash Can File Versioning": "Trash Can File Versioning",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Unavailable",
-1
View File
@@ -317,7 +317,6 @@
"Time": "Tempo",
"Time the item was last modified": "Tempo de lasta modifo de la ero",
"Trash Can File Versioning": "Rubuja Dosiera Versionado",
"Twitter": "Twitter",
"Type": "Tipo",
"UNIX Permissions": "Permesoj UNIX",
"Unavailable": "Ne disponebla",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Hoy",
"Trash Can": "Papelera",
"Trash Can File Versioning": "Versionado de archivos de la papelera",
"Twitter": "Twitter",
"Type": "Tipo",
"UNIX Permissions": "Permisos de UNIX",
"Unavailable": "No disponible",
-1
View File
@@ -113,7 +113,6 @@
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi!",
"The following items could not be synchronized.": "Järgnevaid üksusi ei õnnestunud sünkroniseerida.",
"The maximum age must be a number and cannot be blank.": "Maksimaalne vanus peab olema arv ning ei tohi olla tühi.",
"Twitter": "Twitter",
"Unknown": "Teadmata",
"Upload Rate": "Üleslaadimise Kiirus",
"Use HTTPS for GUI": "Kasuta HTTPS'i GUI jaoks",
-1
View File
@@ -384,7 +384,6 @@
"Time": "Ordua",
"Time the item was last modified": "Itema azkenekoz aldatu zen ordua",
"Trash Can File Versioning": "Zakarrontzia",
"Twitter": "Twitter",
"Type": "Mota",
"UNIX Permissions": "UNIX baimenak",
"Unavailable": "Ez dago erabilgarri",
-1
View File
@@ -310,7 +310,6 @@
"Time": "Aika",
"Time the item was last modified": "Aika jolloin kohdetta viimeksi muokattiin",
"Trash Can File Versioning": "Roskakorin tiedostoversiointi",
"Twitter": "Twitter",
"Type": "Tyyppi",
"Unavailable": "Ei saatavilla",
"Unavailable/Disabled by administrator or maintainer": "Ei saatavilla / ylläpitäjän estämä.",
-1
View File
@@ -210,7 +210,6 @@
"This setting controls the free space required on the home (i.e., index database) disk.": "Ce réglage contrôle l'espace disque requis dans le disque qui abrite votre répertoire utilisateur (pour la base de données d'indexation).",
"Time": "Heure",
"Trash Can File Versioning": "Style poubelle",
"Twitter": "Twitter",
"Unknown": "Inconnu",
"Unshared": "Non partagé",
"Up to Date": "À jour",
+5 -6
View File
@@ -186,7 +186,7 @@
"Global Discovery Servers": "Serveurs de découverte globale",
"Global State": "État global",
"Help": "Aide (en anglais)",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Remarque : vous n'avez indiqué que des filtres de refus alors que le comportement par défaut est déjà le refus. Envisagez d'ajouter * (Partager tout le reste) comme dernier filtre (coché) pour inverser le comportement par défaut.",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Remarque : vous n'avez indiqué que des filtres de refus alors que le comportement par défaut est déjà le refus. Envisagez d'ajouter * (Partager tout les autres attributs non spécifiés) comme dernier filtre (coché) pour inverser le comportement par défaut.",
"Home page": "Page d'accueil",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Cependant, vos réglages indiquent que vous pourriez souhaiter ne pas l'activer. Nous avons désactivé pour vous l'envoi automatique des rapports.",
"Identification": "Identifiant abrégé",
@@ -290,8 +290,8 @@
"Preview": "Aperçu",
"Preview Usage Report": "Aperçu du rapport de statistiques d'utilisation",
"QR code": "Code QR",
"QUIC LAN": "QUIC LAN",
"QUIC WAN": "QUIC WAN",
"QUIC LAN": "LAN QUIC",
"QUIC WAN": "WAN QUIC",
"QUIC connections are in most cases considered suboptimal": "Les connexions QUIC sont généralement peu performantes",
"Quick guide to supported patterns": "Guide rapide des masques compatibles ci-dessous",
"Random": "Aléatoire",
@@ -355,7 +355,7 @@
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé. (Modifiable ultérieurement).",
"Shutdown": "Arrêter",
"Shutdown Complete": "Arrêté",
"Shutdown Complete": "Arrêt complet",
"Simple": "Suivi simplifié",
"Simple File Versioning": "Suivi simplifié des versions",
"Single level wildcard (matches within a directory only)": "N'importe quel nombre (dont 0) de n'importe quels caractères (sauf le séparateur de répertoires)",
@@ -369,7 +369,7 @@
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Les versions stables sont reportées d'environ deux semaines. Pendant ce temps elles sont testées en tant que versions préliminaires.",
"Stable releases only": "Seulement les versions stables",
"Staggered": "Versions échelonnées",
"Staggered File Versioning": "Versions échelonnées",
"Staggered File Versioning": "Versions échelonnées des fichiers",
"Start Browser": "Lancer le navigateur web",
"Statistics": "Statistiques",
"Stopped": "Arrêté",
@@ -448,7 +448,6 @@
"Today": "Aujourd'hui",
"Trash Can": "Corbeille",
"Trash Can File Versioning": "Style poubelle",
"Twitter": "Piaf",
"Type": "Type",
"UNIX Permissions": "Permissions UNIX",
"Unavailable": "Indisponible",
-1
View File
@@ -368,7 +368,6 @@
"Time": "Tiid",
"Time the item was last modified": "Tiidstip dat it ûnderdiel foar it lest oanpast waard.",
"Trash Can File Versioning": "Jiskefet-triemferzjebehear",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX-Rjochten",
"Unavailable": "Net beskikber",
+1 -2
View File
@@ -22,6 +22,5 @@
"Alphabetic": "Alfabética",
"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 xestiona as versións. Ten que eliminar o ficheiro do cartafol compartido. Si a ruta ao aplicativo contén espazos, deberían ir acotados.",
"Anonymous Usage Reporting": "Informe anónimo de uso",
"LDAP": "LDAP",
"Twitter": "Twitter"
"LDAP": "LDAP"
}
-1
View File
@@ -98,7 +98,6 @@
"Source Code": "קוד מקור",
"Support": "תמיכה",
"Syncing": "כעת בסנכרון",
"Twitter": "Twitter",
"Upgrade": "שדרוג",
"Upgrade To {%version%}": "שדרוג לגרסה {{version}}",
"Upgrading": "כעת בשדרוג",
+1 -2
View File
@@ -18,6 +18,5 @@
"Allowed Networks": "Dozvoljene mreže",
"Alphabetic": "Abecednim redom",
"Apply": "Primijeni",
"LDAP": "LDAP",
"Twitter": "Twitter"
"LDAP": "LDAP"
}
-1
View File
@@ -439,7 +439,6 @@
"Today": "Ma",
"Trash Can": "Szemetes",
"Trash Can File Versioning": "Szemetes fájlverzió-követés",
"Twitter": "Twitter",
"Type": "Típus",
"UNIX Permissions": "UNIX jogosultságok",
"Unavailable": "Nem elérhető",
-1
View File
@@ -395,7 +395,6 @@
"Time the item was last modified": "Waktu file terakhir dimodifikasi",
"Today": "Hari Ini",
"Trash Can File Versioning": "Pemversian Berkas Tempat Sampah",
"Twitter": "Twitter",
"Type": "Tipe",
"UNIX Permissions": "Izin UNIX",
"Unavailable": "Tidak Tersedia",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Oggi",
"Trash Can": "Cestino",
"Trash Can File Versioning": "Controllo Versione con Cestino",
"Twitter": "Twitter",
"Type": "Tipo",
"UNIX Permissions": "Permessi UNIX",
"Unavailable": "Non disponibile",
-1
View File
@@ -379,7 +379,6 @@
"Time the item was last modified": "項目を最後に変更した日時",
"Today": "今日",
"Trash Can File Versioning": "ゴミ箱によるバージョン管理",
"Twitter": "Twitter",
"Type": "タイプ",
"UNIX Permissions": "UNIX パーミッション",
"Unavailable": "利用不可",
-1
View File
@@ -448,7 +448,6 @@
"Today": "오늘",
"Trash Can": "휴지통",
"Trash Can File Versioning": "휴지통을 통한 파일 버전 관리",
"Twitter": "트위터",
"Type": "유형",
"UNIX Permissions": "UNIX 권한",
"Unavailable": "변경 불가",
-1
View File
@@ -376,7 +376,6 @@
"Today": "Šiandien",
"Trash Can": "Šiukšlinė",
"Trash Can File Versioning": "Šiukšliadėžės versijų valdymas",
"Twitter": "„Twitter“",
"Type": "Tipas",
"UNIX Permissions": "UNIX leidimai",
"Unavailable": "Neprieinama",
-1
View File
@@ -294,7 +294,6 @@
"Time": "Klokkeslett",
"Time the item was last modified": "Tidspunktet elementet sist ble endret",
"Trash Can File Versioning": "Papirkurv versjonskontroll",
"Twitter": "Twitter",
"Unavailable": "Utilgjengelig",
"Unavailable/Disabled by administrator or maintainer": "Utilgjengelig/avskrudd av administrator eller vedlikeholder",
"Undecided (will prompt)": "Ikke bestemt (vil spørre)",
+1 -2
View File
@@ -24,6 +24,5 @@
"Copied from elsewhere": "अन्यत्रबाट प्रतिलिपित्",
"Copied from original": "मूलबाट प्रतिलिपित्",
"Danger!": "खतरा!",
"LDAP": "LDAP",
"Twitter": "Twitter"
"LDAP": "LDAP"
}
-1
View File
@@ -448,7 +448,6 @@
"Today": "Vandaag",
"Trash Can": "Prullenbak",
"Trash Can File Versioning": "Prullenbak-versiebeheer",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX-machtigingen",
"Unavailable": "Niet beschikbaar",
-1
View File
@@ -231,7 +231,6 @@
"This can easily give hackers access to read and change any files on your computer.": "Dette kan lett gje datasnokar tilgang til å lesa og endra vilkårlege filer på denne maskina.",
"This is a major version upgrade.": "Dette er ei hovudoppgradering",
"Trash Can File Versioning": "Papirkorg-filutgåvehandtering",
"Twitter": "Twitter",
"Unknown": "Ukjent",
"Unshared": "Ikkje delt",
"Up to Date": "Oppdatert",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Dzisiaj",
"Trash Can": "Kosz",
"Trash Can File Versioning": "Wersjonowanie plików w koszu",
"Twitter": "Twitter",
"Type": "Rodzaj",
"UNIX Permissions": "UNIX-owe uprawnienia",
"Unavailable": "Niedostępne",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Hoje",
"Trash Can": "Cesto de Lixo",
"Trash Can File Versioning": "Lixeira",
"Twitter": "Twitter",
"Type": "Tipo",
"UNIX Permissions": "Permissões UNIX",
"Unavailable": "Não disponível",
+23 -7
View File
@@ -4,13 +4,15 @@
"A new major version may not be compatible with previous versions.": "Uma nova versão principal pode não ser compatível com versões anteriores.",
"API Key": "Chave da API",
"About": "Acerca da aplicação",
"Action": "Acção",
"Actions": "Acções",
"Action": "Operação",
"Actions": "Operações",
"Active filter rules": "Regras de filtros em uso",
"Add": "Adicionar",
"Add Device": "Adicionar dispositivo",
"Add Folder": "Adicionar pasta",
"Add Remote Device": "Adicionar dispositivo remoto",
"Add devices from the introducer to our device list, for mutually shared folders.": "Adicione dispositivos do apresentador à nossa lista de dispositivos para ter pastas mutuamente partilhadas.",
"Add filter entry": "Adicionar filtro",
"Add ignore patterns": "Adicionar padrões de exclusão",
"Add new folder?": "Adicionar nova pasta?",
"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.": "Para além disso o intervalo entre verificações completas irá ser aumentado (vezes 60, ou seja, um novo valor predefinido de 1h). Também o pode configurar manualmente para cada pasta, posteriormente, depois de seleccionar Não.",
@@ -53,7 +55,7 @@
"Cleaning Versions": "Limpando versões",
"Cleanup Interval": "Intervalo entre limpezas",
"Click to see full identification string and QR code.": "Clique para ver a identificação completa e o código QR.",
"Close": "Fechar",
"Close": "Dispensar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentário, quando usado no início de uma linha",
"Compression": "Compressão",
@@ -76,6 +78,7 @@
"Danger!": "Perigo!",
"Database Location": "Localização da base de dados",
"Debugging Facilities": "Recursos de depuração",
"Default": "Predefinido",
"Default Configuration": "Configuração predefinida",
"Default Device": "Dispositivo predefinido",
"Default Folder": "Pasta predefinida",
@@ -111,7 +114,7 @@
"Discovery": "Descoberta",
"Discovery Failures": "Falhas da descoberta",
"Discovery Status": "Estado da descoberta",
"Dismiss": "Fechar",
"Dismiss": "Dispensar",
"Do not add it to the ignore list, so this notification may recur.": "Não adicionar à lista dos ignorados, para que esta notificação volte a aparecer.",
"Do not restore": "Não restaurar",
"Do not restore all": "Não restaurar nenhum",
@@ -141,6 +144,7 @@
"Enter up to three octal digits.": "Insira de um a três dígitos em octal.",
"Error": "Erro",
"Extended Attributes": "Atributos estendidos",
"Extended Attributes Filter": "Filtro de atributos extendidos",
"External": "Externa",
"External File Versioning": "Externa",
"Failed Items": "Itens que falharam",
@@ -182,6 +186,7 @@
"Global Discovery Servers": "Servidores de descoberta global",
"Global State": "Estado global",
"Help": "Ajuda",
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Dica: quando a predefinição é \"negar\", apenas as regras de negação são encontradas. Considere adicionar \"permitir tudo\" como última regra.",
"Home page": "Página do projecto",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Contudo, a sua configuração actual indica que pode não a querer activada. Nós desactivámos automaticamente o relatório de estouro para si.",
"Identification": "Identificação",
@@ -228,8 +233,10 @@
"Log tailing paused. Scroll to the bottom to continue.": "O acompanhamento do final do registo está em pausa. Desloque para o final para continuar.",
"Logs": "Registos",
"Major Upgrade": "Actualização importante",
"Mass actions": "Acções em massa",
"Mass actions": "Operações em massa",
"Maximum Age": "Idade máxima",
"Maximum single entry size": "Tamanho máximo das entradas únicas",
"Maximum total size": "Tamanho máximo total",
"Metadata Only": "Metadados apenas",
"Minimum Free Disk Space": "Espaço livre mínimo no disco",
"Mod. Device": "Dispositivo mod.",
@@ -246,6 +253,7 @@
"No": "Não",
"No File Versioning": "Nenhuma",
"No files will be deleted as a result of this operation.": "Nenhum ficheiro será eliminado como resultado desta operação.",
"No rules set": "Não foram definidas regras",
"No upgrades": "Sem actualizações",
"Not shared": "Não partilhada",
"Notice": "Avisos",
@@ -396,7 +404,7 @@
"The cleanup interval cannot be blank.": "O intervalo entre limpezas não pode estar vazio.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "A configuração foi gravada mas não activada. O Syncthing tem que reiniciar para activar a nova configuração.",
"The device ID cannot be blank.": "O ID do dispositivo não pode estar vazio.",
"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).": "O ID do dispositivo a colocar aqui pode ser obtido no menu \"Acções > Mostrar ID\" do outro dispositivo. Espaços e hífenes são opcionais (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).": "O ID do dispositivo a colocar aqui pode ser obtido no menu \"Operações > Mostrar ID\" do outro dispositivo. Espaços e hífenes são opcionais (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.": "O relatório de utilização cifrado é enviado diariamente. É utilizado para rastrear plataformas comuns, tamanhos de pastas e versões da aplicação. Se o tipo de dados do relatório for alterado, será notificado novamente através desta janela.",
"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.": "O ID do dispositivo fornecido não parece ser válido. Deveria ter 52 ou 56 caracteres constituídos por letras e números, com espaços e hífenes opcionais.",
"The folder ID cannot be blank.": "O ID da pasta não pode estar vazio.",
@@ -436,10 +444,10 @@
"Time": "Quando",
"Time the item was last modified": "Quando o item foi modificado pela última vez",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Para se ligar ao dispositivo Syncthing com o nome \"{{devicename}}\", adicione um novo dispositivo remoto do seu lado com este ID:",
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Para permitir uma regra, marque a caixa. Para negar uma regra, deixe-a desmarcada.",
"Today": "Hoje",
"Trash Can": "Lixo",
"Trash Can File Versioning": "Reciclagem",
"Twitter": "Twitter",
"Type": "Tipo",
"UNIX Permissions": "Permissões UNIX",
"Unavailable": "Indisponível",
@@ -465,6 +473,8 @@
"Use notifications from the filesystem to detect changed items.": "Usar notificações do sistema de ficheiros para detectar itens alterados.",
"User Home": "Pasta do utilizador",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "O nome de utilizador e a respectiva senha para a autenticação na interface gráfica não foram definidos. Considere efectuar essa configuração.",
"Using a QUIC connection over LAN": "Usando uma ligação QUIC sobre LAN",
"Using a QUIC connection over WAN": "Usando uma ligação QUIC sobre WAN",
"Using a direct TCP connection over LAN": "Usando uma ligação TCP directa sobre LAN",
"Using a direct TCP connection over WAN": "Usando uma ligação TCP directa sobre WAN",
"Version": "Versão",
@@ -499,10 +509,16 @@
"Your SMS app should open to let you choose the recipient and send it from your own number.": "A sua aplicação de SMS deverá abrir para deixar escolher o destinatário e enviar a partir do seu próprio número.",
"Your email app should open to let you choose the recipient and send it from your own address.": "A sua aplicação de email deverá abrir para deixar escolher o destinatário e enviar a partir do seu próprio endereço.",
"days": "dias",
"deleted": "eliminado",
"deny": "negar",
"directories": "pastas",
"file": "ficheiro",
"files": "ficheiros",
"folder": "pasta",
"full documentation": "documentação completa",
"items": "itens",
"modified": "modificado",
"permit": "permitir",
"seconds": "segundos",
"theme-name-black": "Preto",
"theme-name-dark": "Escuro",
-1
View File
@@ -360,7 +360,6 @@
"Time": "Time",
"Time the item was last modified": "Time the item was last modified",
"Trash Can File Versioning": "Trash Can File Versioning",
"Twitter": "Twitter",
"Type": "Type",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Unavailable",
-1
View File
@@ -432,7 +432,6 @@
"Today": "Сегодня",
"Trash Can": "Корзина",
"Trash Can File Versioning": "Использовать версионность для файлов в Корзине",
"Twitter": "Twitter",
"Type": "Тип",
"UNIX Permissions": "Разрешения UNIX",
"Unavailable": "Недоступно",
+76 -25
View File
@@ -12,6 +12,7 @@
"Add Folder": "බහාලුමක් යොදන්න",
"Add Remote Device": "දුරස්ථ උපාංගයක් යොදන්න",
"Add devices from the introducer to our device list, for mutually shared folders.": "අන්‍යෝන්‍ය වශයෙන් බෙදා ගත් ෆෝල්ඩර සඳහා, අපගේ උපාංග ලැයිස්තුවට හඳුන්වා දෙන්නා වෙතින් උපාංග එක් කරන්න.",
"Add filter entry": "පෙරීමේ නිවේශිතයක් යොදන්න",
"Add ignore patterns": "නොසලකා හැරීමේ රටා එක් කරන්න",
"Add new folder?": "නව බහාලුම යොදනවාද?",
"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.": "අතිරේකව සම්පූර්ණ නැවත පරිලෝකනය කිරීමේ පරතරය වැඩි වනු ඇත (වාර 60, එනම් 1h හි නව පෙරනිමිය). අංක තේරීමෙන් පසුව ඔබට සෑම ෆෝල්ඩරයක් සඳහාම එය අතින් වින්‍යාසගත කළ හැක.",
@@ -37,14 +38,16 @@
"Are you sure you want to restore {%count%} files?": "ඔබට ගොනු {{count}} ක් ප්‍රත්‍යර්පණය කිරීමට වුවමනාද?",
"Are you sure you want to revert all local changes?": "ඔබට සියලු දේශීය වෙනස්කම් ප්‍රතිවර්තනය කිරීමට අවශ්‍ය බව විශ්වාසද?",
"Are you sure you want to upgrade?": "ඔබට උත්ශ්‍රේණි කිරීමට වුවමනාද?",
"Authors": "කතුවරුන්",
"Auto Accept": "ස්වයං පිළිගැනීම",
"Automatic Crash Reporting": "ස්වයංක්‍රීය බිඳවැටීම් වාර්තා කිරීම",
"Automatic Crash Reporting": "ස්වයංක්‍රීය බිඳවැටීම් වාර්තාකරණය",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "ස්වයංක්‍රීය උත්ශ්‍රේණිගත කිරීම දැන් ස්ථායී නිකුතු සහ නිදහස් අපේක්ෂකයින් අතර තේරීම ඉදිරිපත් කරයි.",
"Automatic upgrades": "ස්වයංක්‍රීය උත්ශ්‍රේණි",
"Automatic upgrades are always enabled for candidate releases.": "අපේක්ෂක නිකුතු සඳහා ස්වයංක්‍රීය උත්ශ්‍රේණි කිරීම් සැමවිටම සක්‍රීය වේ.",
"Automatically create or share folders that this device advertises at the default path.": "මෙම උපාංගය පෙරනිමි මාර්ගයේ ප්‍රචාරණය කරන ෆෝල්ඩර ස්වයංක්‍රීයව සාදන්න හෝ බෙදාගන්න.",
"Available debug logging facilities:": "පවතින දෝශ නිරාකරණය කිරීමේ පහසුකම්:",
"Be careful!": "පරෙස්සම් වෙන්න!",
"Body:": "අන්තර්ගතය:",
"Bugs": "දෝෂ",
"Cancel": "අවලංගු",
"Changelog": "වෙනස්කම්",
@@ -56,6 +59,8 @@
"Command": "විධානය",
"Comment, when used at the start of a line": "පේළියක ආරම්භයේ භාවිතා කරන විට අදහස් දක්වන්න",
"Compression": "සම්පීඩනය",
"Configuration Directory": "වින්‍යාස නාමාවලිය",
"Configuration File": "වින්‍යාස ගොනුව",
"Configured": "වින්‍යාසගතයි",
"Connected (Unused)": "සම්බන්ධයි (භාවිතයේ නැත)",
"Connection Error": "සම්බන්ධතාවයේ දෝෂයකි",
@@ -64,10 +69,15 @@
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "වෙනස්කම් සඳහා අඛණ්ඩව නැරඹීම දැන් සමමුහුර්තකරණය තුළ පවතී. මෙය තැටියේ වෙනස්කම් හඳුනාගෙන වෙනස් කරන ලද මාර්ගවල පමණක් ස්කෑන් කිරීමක් නිකුත් කරයි. ප්‍රතිලාභ නම් වෙනස්කම් ඉක්මනින් ප්‍රචාරණය වීම සහ අඩු සම්පූර්ණ ස්කෑන් අවශ්‍ය වීමයි.",
"Copied from elsewhere": "වෙනත් තැනකින් පිටපත් කර ඇත",
"Copied from original": "මුල් පිටපතෙන් පිටපත් කර ඇත",
"Copied!": "පිටපත් විය!",
"Copy": "පිටපතක්",
"Copy failed! Try to select and copy manually.": "පිටපත් වීමට අසමත් විය! අතින් තෝරා පිටපත් කිරීමට බලන්න.",
"Currently Shared With Devices": "දැනට උපාංග සමඟ බෙදාගෙන ඇත",
"Custom Range": "අභිරුචි පරාසය",
"Danger!": "අනතුර!",
"Database Location": "දත්ත සමුදායේ ස්ථානය",
"Debugging Facilities": "නිදොස්කරණ පහසුකම්",
"Default": "පෙරනිමි",
"Default Configuration": "පෙරනිමි වින්‍යාසය",
"Default Device": "පෙරනිමි උපාංගය",
"Default Folder": "පෙරනිමි බහාලුම",
@@ -81,6 +91,7 @@
"Deselect folders to stop sharing with this device.": "මෙම උපාංගය සමඟ බෙදා ගැනීම නැවැත්වීමට ෆෝල්ඩර තේරීම ඉවත් කරන්න.",
"Device": "උපාංගය",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "උපාංගය \"{{name}}\" ({{device}} ට {{address}}) සම්බන්ධ වීමට අවශ්‍යයි. නව උපාංගයක් එක් කරන්නද?",
"Device Certificate": "උපාංගයේ සහතිකය",
"Device ID": "උපාංගයේ හැඳු.",
"Device Identification": "උපාංගයේ හැඳුනුම",
"Device Name": "උපාංගයේ නම",
@@ -96,26 +107,27 @@
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "ගොනු අවසර සංසන්දනය කිරීම සහ සමමුහුර්ත කිරීම අබල කරයි. නොපවතින හෝ අභිරුචි අවසර සහිත පද්ධති මත ප්‍රයෝජනවත් වේ (උදා: FAT, exFAT, Synology, Android).",
"Discard": "ඉවතලන්න",
"Disconnected": "විසන්ධියි",
"Disconnected (Inactive)": "විසන්ධියි (අක්‍රිය)",
"Disconnected (Unused)": "විසන්ධියි (භාවිතයේ නැත)",
"Discovered": "සොයා ගන්නා ලදී",
"Discovery": "සොයාගැනීම",
"Discovery Failures": "සොයාගැනීමේ අසාර්ථකත්වය",
"Discovery Status": "සොයාගැනීමේ තත්ත්වය",
"Discovery Status": "සොයාගැනීමේ තත්වය",
"Dismiss": "අහකට",
"Do not add it to the ignore list, so this notification may recur.": "එය නොසලකා හැරීමේ ලැයිස්තුවට එක් නොකරන්න, එබැවින් මෙම දැනුම්දීම නැවත සිදු විය හැක.",
"Do not restore": "ප්‍රත්‍යර්පණය නොකරන්න",
"Do not restore all": "සියල්ල ප්‍රත්‍යර්පණය නොකරන්න",
"Do you want to enable watching for changes for all your folders?": "ඔබට ඔබගේ සියලුම ෆෝල්ඩර සඳහා වෙනස්කම් නැරඹීම සබල කිරීමට අවශ්‍යද?",
"Documentation": "ප්‍රලේඛනය",
"Download Rate": "බාගත කිරීමේ අනුපාතය",
"Download Rate": "බාගැනීමේ අනුපාතය",
"Downloaded": "බාගත වී ඇත",
"Downloading": "බාගැනෙමින්",
"Edit": "සංස්කරණය",
"Edit Device": "උපාංගය සංස්කරණය",
"Edit Device Defaults": "උපාංග පෙරනිමි සංස්කරණය කරන්න",
"Edit Device Defaults": "පෙරනිමි සංස්කරණය කරන්න",
"Edit Folder": "බහාලුම සංස්කරණය",
"Edit Folder Defaults": "ෆෝල්ඩර පෙරනිමි සංස්කරණය කරන්න",
"Editing {%path%}.": "සංස්කරණය {{path}}.",
"Editing {%path%}.": "{{path}} සංශෝධනය.",
"Enable Crash Reporting": "බිඳවැටීම් වාර්තා කිරීම සබල කරන්න",
"Enable NAT traversal": "NAT සංක්‍රමණය සබල කරන්න",
"Enable Relaying": "Relaying සබල කරන්න",
@@ -127,11 +139,11 @@
"Enter up to three octal digits.": "අෂ්ටක ඉලක්කම් තුනක් දක්වා ඇතුළු කරන්න.",
"Error": "දෝෂයකි",
"External": "බාහිර",
"External File Versioning": "බාහිර ගොනු අනුවාදය",
"External File Versioning": "බාහිර ගොනු අනුවාදය",
"Failed Items": "අසාර්ථක අයිතම",
"Failed to load file versions.": "ගොනු අනුවාද පූරණය කිරීමට අසමත් විය.",
"Failed to load ignore patterns.": "නොසලකා හැරීමේ රටා පූරණය කිරීමට අසමත් විය.",
"Failed to setup, retrying": "පිහිටුවීමට අසමත් විය, නැවත උත්සාහ කරමින්",
"Failed to setup, retrying": "පිහිටුවීමට අසමත් විය, උත්සාහ කරමින්",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 සම්බන්ධතාවක් නොමැති නම් IPv6 සේවාදායක වෙත සම්බන්ධ වීමට අසමත් වීම අපේක්ෂා කෙරේ.",
"File Pull Order": "ගොනු ඇදීමේ නියෝගය",
"File Versioning": "ගොනු අනුවාදය",
@@ -151,12 +163,15 @@
"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.": "ෆෝල්ඩරය එකතු කිරීමෙන් පසු \"{{receiveEncrypted}}\" ෆෝල්ඩර වර්ගය වෙනස් කළ නොහැක. ඔබට ෆෝල්ඩරය ඉවත් කිරීම, තැටියේ ඇති දත්ත මකා දැමීම හෝ විකේතනය කිරීම සහ ෆෝල්ඩරය නැවත එක් කිරීම අවශ්ය වේ.",
"Folders": "බහාලුම්",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "පහත ෆෝල්ඩර සඳහා වෙනස්කම් නැරඹීමට පටන් ගැනීමේදී දෝෂයක් ඇති විය. එය සෑම මිනිත්තුවකම නැවත උත්සාහ කරනු ඇත, එබැවින් දෝෂ ඉක්මනින් පහව යනු ඇත. ඒවා දිගටම පවතින්නේ නම්, යටින් පවතින ගැටලුව විසඳීමට උත්සාහ කර ඔබට නොහැකි නම් උදව් ඉල්ලන්න.",
"Full Rescan Interval (s)": "සම්පූර්ණ නැවත ස්කෑන් පරතරය (ය)",
"Forever": "සදහටම",
"Full Rescan Interval (s)": "පූර්ණ සුපිරික්සීමේ පරතරය (ත.)",
"GUI": "GUI",
"GUI / API HTTPS Certificate": "GUI / යෙ.ක්‍ර.මු. HTTPS සහතිකය",
"GUI Authentication Password": "GUI සත්‍යාපන මුරපදය",
"GUI Authentication User": "GUI සත්‍යාපන පරිශ්‍රීලක",
"GUI Authentication: Set User and Password": "GUI සත්‍යාපනය: පරිශ්‍රීලක හා මුරපදය සකසන්න",
"GUI Listen Address": "GUI සවන්දීමේ ලිපිනය",
"GUI Override Directory": "GUI අභිබවන නාමාවලිය",
"GUI Theme": "GUI තේමාව",
"General": "සාමාන්‍ය",
"Generate": "උත්පාදනය",
@@ -174,7 +189,7 @@
"Ignore Permissions": "අවසර නොසලකන්න",
"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.": "නොසලකා හැරීමේ රටා එකතු කළ හැක්කේ ෆෝල්ඩරය නිර්මාණය කිරීමෙන් පසුව පමණි. පරීක්ෂා කළහොත්, නොසලකා හැරීමේ රටා ඇතුළු කිරීමට ආදාන ක්ෂේත්‍රයක් සුරැකීමෙන් පසුව ඉදිරිපත් කෙරේ.",
"Ignored Devices": "නොසැලකූ උපාංග",
"Ignored Folders": "නොසලක බහාලුම්",
"Ignored Folders": "නොසලක බහාලුම්",
"Ignored at": "දී නොසලකා හරින ලදී",
"Incoming Rate Limit (KiB/s)": "එන අනුපාත සීමාව (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "වැරදි වින්‍යාසය ඔබගේ ෆෝල්ඩර අන්තර්ගතයට හානි කළ හැකි අතර සමමුහුර්ත කිරීම අක්‍රිය කරයි.",
@@ -191,6 +206,7 @@
"Last seen": "අවසන දුටුවේ",
"Latest Change": "නවතම වෙනස",
"Learn more": "තව දැනගන්න",
"Learn more at {%url%}": "{{url}} හරහා තව දැනගන්න",
"Limit": "සීමාව",
"Listener Failures": "සවන්දෙන්නන්ගේ අසාර්ථකත්වය",
"Listener Status": "සවන්දෙන්නන්ගේ තත්ත්වය",
@@ -198,49 +214,58 @@
"Loading data...": "දත්ත පූරණය වෙමින්...",
"Loading...": "පූරණය වෙමින්...",
"Local Additions": "දේශීය එකතු කිරීම්",
"Local Discovery": "ස්ථානීය සොයාගැනීම",
"Local Discovery": "ස්ථානීය සොයාගැනීම",
"Local State": "ස්ථානීය තත්‍වය",
"Local State (Total)": "ස්ථානීය තත්‍වය (මුළු)",
"Locally Changed Items": "දේශීයව වෙනස් කරන ලද අයිතම",
"Log": "සටහන",
"Log File": "සටහන් ගොනුව",
"Log tailing paused. Scroll to the bottom to continue.": "ලොග් වලිගය විරාම කරන ලදී. ඉදිරියට යාමට පහළට අනුචලනය කරන්න.",
"Logs": "සටහන්",
"Major Upgrade": "ප්රධාන උත්ශ්රේණි කිරීම",
"Major Upgrade": "ප්රධාන උත්ශ්රේණි කිරීම",
"Mass actions": "මහා ක්‍රියා",
"Maximum Age": "උපරිම වයස",
"Maximum single entry size": "තනි නිවේශිතයක උපරිම ප්‍රමාණය",
"Maximum total size": "උපරිම මුළු ප්‍රමාණය",
"Metadata Only": "පාරදත්ත පමණි",
"Minimum Free Disk Space": "අවම තැටියේ ඉඩ",
"Mod. Device": "mod. උපාංගය",
"Mod. Time": "mod. කාලය",
"More than a month ago": "මාසයකට පෙර",
"More than a week ago": "සතියකට පෙර",
"More than a year ago": "වසරකට පෙර",
"Move to top of queue": "පෝලිමේ මුලට ගෙනයන්න",
"Multi level wildcard (matches multiple directory levels)": "බහු මට්ටමේ වයිල්ඩ්කාඩ් (බහු ඩිරෙක්ටරි මට්ටම් වලට ගැලපේ)",
"Never": "කවදාවත්",
"New Device": "නව උපාංගය",
"New Folder": "නව බහාලුම",
"Newest First": "අලුත්ම මුලින්ම",
"Newest First": "නව දෑ පළමුව",
"No": "නැහැ",
"No File Versioning": "ගොනු අනුවාදනයක් නැත",
"No files will be deleted as a result of this operation.": "මෙම මෙහෙයුමේ ප්‍රතිඵලයක් ලෙස කිසිදු ගොනුවක් මකා නොදමනු ඇත.",
"No rules set": "නීති සකසා නැත",
"No upgrades": "උත්ශ්‍රේණි නැත",
"Not shared": "බෙදාගෙන නැත",
"Notice": "දැන්වීම",
"OK": "හරි",
"Off": "අක්‍රියයි",
"Oldest First": "පරණිතම පළමු",
"Oldest First": "පරණ දෑ පළමු",
"Optional descriptive label for the folder. Can be different on each device.": "ෆෝල්ඩරය සඳහා විකල්ප විස්තර ලේබලය. එක් එක් උපාංගය මත වෙනස් විය හැක.",
"Options": "විකල්ප",
"Out of Sync": "සමමුහර්ත නොවේ",
"Out of Sync Items": "අයිතම සමමුහර්ත නොව",
"Out of Sync": "සමමුහර්ත නොවේ",
"Out of Sync Items": "සමමුහර්ත නොවන අථක",
"Outgoing Rate Limit (KiB/s)": "පිටතට යන ගාස්තු සීමාව (KiB/s)",
"Override": "අභිබවන්න",
"Override Changes": "වෙනස්කම් අභිබවන්න",
"Ownership": "අයිතිය",
"Path": "මාර්ගය",
"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": "දේශීය පරිගණකයේ ෆෝල්ඩරය වෙත මාර්ගය. එය නොමැති නම් නිර්මාණය වනු ඇත. tilde අක්ෂරය (~) සඳහා කෙටි මගක් ලෙස භාවිතා කළ හැක",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "අනුවාද ගබඩා කළ යුතු මාර්ගය (බෙදාගත් ෆෝල්ඩරයේ පෙරනිමි .stversions නාමාවලිය සඳහා හිස්ව තබන්න).",
"Pause": "විරාමය",
"Pause All": "සියල්ල විරාමය",
"Paused": "විරාමය",
"Paused (Unused)": "විරාමයි (භාවිතා නොකළ)",
"Paths": "මාර්ග",
"Pause": "විරාමයක්",
"Pause All": "සියල්ලට විරාමයක්",
"Paused": "විරාමයි",
"Paused (Unused)": "විරාමයකි (භාවිතා නොකළ)",
"Pending changes": "පොරොත්තු වෙනස්කම්",
"Periodic scanning at given interval and disabled watching for changes": "ලබා දී ඇති කාල සීමාව තුළ වරින් වර ස්කෑන් කිරීම සහ වෙනස්කම් සඳහා අබල කර බැලීම",
"Periodic scanning at given interval and enabled watching for changes": "ලබා දී ඇති කාල සීමාව තුළ වරින් වර ස්කෑන් කිරීම සහ වෙනස්කම් සඳහා නැරඹීම සබල කර ඇත",
@@ -251,9 +276,12 @@
"Please wait": "රැඳෙන්න",
"Prefix indicating that the file can be deleted if preventing directory removal": "නාමාවලිය ඉවත් කිරීම වළක්වන්නේ නම් ගොනුව මකා දැමිය හැකි බව පෙන්නුම් කරන උපසර්ගය",
"Prefix indicating that the pattern should be matched without case sensitivity": "සංවේදිතාවකින් තොරව රටාව ගැලපිය යුතු බව පෙන්නුම් කරන උපසර්ගය",
"Preparing to Sync": "සමමුහූර්තයට සූදානම් ස්ථානයේ",
"Preparing to Sync": "සමමුහූර්තයට සූදානම් වෙමින්",
"Preview": "පෙරදසුන",
"Preview Usage Report": "භාවිත වාර්තාවේ පෙරදසුන",
"QR code": "QR කේතය",
"QUIC LAN": "QUIC LAN",
"QUIC WAN": "QUIC WAN",
"Quick guide to supported patterns": "සහාය දක්වන රටා සඳහා ඉක්මන් මාර්ගෝපදේශය",
"Random": "අහඹු",
"Receive Encrypted": "සංකේතිිතව ලබන්න",
@@ -269,15 +297,15 @@
"Remove Device": "උපාංගය ඉවත් කරන්න",
"Remove Folder": "බහාලුම ඉවත් කරන්න",
"Required identifier for the folder. Must be the same on all cluster devices.": "ෆෝල්ඩරය සඳහා අවශ්‍ය හඳුනාගැනීම. සියලුම පොකුරු උපාංග මත සමාන විය යුතුය.",
"Rescan": "නැවත ස්කෑන් කරන්න",
"Rescan All": "සියල්ල නැවත ස්කෑන් කරන්න",
"Rescan": "යළි සුපිරික්සන්න",
"Rescan All": "සියල්ල යළි සුපිරික්සන්න",
"Rescans": "නැවත ස්කෑන් කරයි",
"Restart": "යළි අරඹන්න",
"Restart Needed": "නැවත ආරම්භ කිරීම අවශ්‍යයි",
"Restarting": "යළි ඇරඹෙමින්",
"Restore": "ප්‍රත්‍යර්පණය",
"Restore Versions": "අනුවාද ප්‍රත්‍යර්පණය",
"Resume": "අරඹන්න",
"Resume": "නැවතත්",
"Resume All": "සියල්ල නැවතත්",
"Reused": "යළි භාවිත",
"Revert": "ආපසු හරවන්න",
@@ -294,10 +322,13 @@
"Select oldest version": "පරණම අනුවාදය තෝරන්න",
"Send & Receive": "යැවීම සහ ලැබීම",
"Send Only": "යැවීම පමණි",
"Send Ownership": "අයිතිය යවන්න",
"Set Ignores on Added Folder": "එකතු කළ ෆෝල්ඩරයේ නොසලකා හැරීම් සකසන්න",
"Settings": "සැකසුම්",
"Share": "බෙදාගන්න",
"Share Folder": "බහාලුම බෙදාගන්න",
"Share by Email": "වි-තැපෑලෙන් බෙදාගන්න",
"Share by SMS": "කෙටි පණිවුඩ මගින් බෙදාගන්න",
"Share this folder?": "මෙම බහාලුම බෙදා ගන්නද?",
"Shared Folders": "බෙදාගත් බහාලුම්",
"Shared With": "සමඟ බෙදාගෙන ඇත",
@@ -329,10 +360,14 @@
"Statistics": "සංඛ්‍යාලේඛන",
"Stopped": "නැවැත්තුවා",
"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.": "සංකේතනය කළ දත්ත පමණක් ගබඩා කර සමමුහුර්ත කරයි. සියලුම සම්බන්ධිත උපාංගවල ඇති ෆෝල්ඩර එකම මුරපදයකින් හෝ \"{{receiveEncrypted}}\" වර්ගයට අයත් විය යුතුය.",
"Subject:": "මාතෘකාව:",
"Support": "සහාය",
"Support Bundle": "ආධාරක බණ්ඩලය",
"Sync Ownership": "සමමුහූර්ත අයිතිය",
"Sync Protocol Listen Addresses": "ප්‍රොටෝකෝලය සවන්දීමේ ලිපින සමමුහුර්ත කරන්න",
"Sync Status": "සමමුහූර්ත තත්‍වය",
"Syncing": "සමමුහූර්තය",
"Syncthing device ID for \"{%devicename%}\"": "\"{{devicename}}\" සඳහා සයින්තින් උපාංගයේ හැඳු.",
"Syncthing has been shut down.": "සමමුහුර්ත කිරීම වසා ඇත.",
"Syncthing includes the following software or portions thereof:": "සමමුහුර්තකරණයට පහත මෘදුකාංග හෝ එහි කොටස් ඇතුළත් වේ:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "සමමුහුර්ත කිරීම MPL v2.0 ලෙස බලපත්‍ර ලබා ඇති නිදහස් සහ විවෘත මූලාශ්‍ර මෘදුකාංගයකි.",
@@ -343,6 +378,8 @@
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "සමමුහුර්තකරණය දැන් සංවර්ධකයින්ට බිඳවැටීම් ස්වයංක්‍රීයව වාර්තා කිරීමට සහය දක්වයි. මෙම විශේෂාංගය පෙරනිමියෙන් සක්රිය කර ඇත.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "සමමුහුර්ත කිරීම අක්‍රිය වී ඇති බවක් පෙනේ, නැතහොත් ඔබගේ අන්තර්ජාල සම්බන්ධතාවයේ ගැටලුවක් තිබේ. නැවත උත්සාහ කරමින්…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "සමමුහුර්ත කිරීම ඔබගේ ඉල්ලීම සැකසීමේ ගැටලුවක් අත්විඳින බව පෙනේ. ගැටලුව දිගටම පවතින්නේ නම් කරුණාකර පිටුව නැවුම් කරන්න හෝ සමමුහුර්ත කිරීම නැවත ආරම්භ කරන්න.",
"TCP LAN": "TCP LAN",
"TCP WAN": "TCP WAN",
"Take me back": "මාව ආපසු ගන්න",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "GUI ලිපිනය ආරම්භක විකල්ප මගින් අභිබවා යයි. ප්‍රතික්‍ෂේප කිරීම ක්‍රියාත්මක වන විට මෙහි වෙනස්කම් බල නොපායි.",
"The Syncthing Authors": "සයින්තින් කතුවරුන්",
@@ -363,6 +400,7 @@
"The following items could not be synchronized.": "පහත අථක සමමුහූර්ත කිරීමට නොහැකිය.",
"The following items were changed locally.": "පහත අථක ස්ථානීයව වෙනස් කර ඇත.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "ජාලයේ වෙනත් උපාංග සොයා ගැනීමට සහ මෙම උපාංගය අන් අය විසින් සොයා ගන්නා ලෙස නිවේදනය කිරීමට පහත ක්‍රම භාවිතා කරයි:",
"The following text will automatically be inserted into a new message.": "පහත පෙළ නව පණිවිඩයකට ස්වයංක්‍රීයව ඇතුළු කෙරේ.",
"The following unexpected items were found.": "පහත අනපේක්‍ෂිත අථක හමු විය.",
"The interval must be a positive number of seconds.": "පරතරය ධනාත්මක තත්පර ගණනක් විය යුතුය.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "අනුවාද නාමාවලිය තුළ පිරිසිදු කිරීම ධාවනය කිරීම සඳහා තත්පර කිහිපයකින් පරතරය. ආවර්තිතා පිරිසිදු කිරීම අක්රිය කිරීමට ශුන්ය.",
@@ -389,10 +427,10 @@
"This setting controls the free space required on the home (i.e., index database) disk.": "මෙම සිටුවම නිවසේ (එනම්, දර්ශක දත්ත ගබඩාව) තැටියේ අවශ්‍ය නිදහස් ඉඩ පාලනය කරයි.",
"Time": "කාලය",
"Time the item was last modified": "අයිතමය අවසන් වරට වෙනස් කළ වේලාව",
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "\"{{devicename}}\" සයින්තින් උපාංගය සමඟ සම්බන්ධ වීමට, මෙම හැඳු. සහිත නව දුරස්ථ උපාංගයක් ඔබගේ පසට එක් කරන්න:",
"Today": "අද",
"Trash Can": "කසල බඳුන",
"Trash Can File Versioning": "කුණු කූඩය ගොනු අනුවාදය",
"Twitter": "ට්විටර්",
"Type": "වර්ගය",
"UNIX Permissions": "UNIX අවසර",
"Unavailable": "නොතිබේ",
@@ -417,6 +455,10 @@
"Use HTTPS for GUI": "GUI සඳහා HTTPS භාවිතා කරන්න",
"Use notifications from the filesystem to detect changed items.": "වෙනස් කළ අයිතම හඳුනා ගැනීමට ගොනු පද්ධතියෙන් දැනුම්දීම් භාවිතා කරන්න.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "GUI සත්‍යාපනය සඳහා පරිශීලක නාමය/මුරපදය සකසා නොමැත. කරුණාකර එය පිහිටුවීම සලකා බලන්න.",
"Using a QUIC connection over LAN": "LAN හරහා QUIC සම්බන්ධතාවයක් භාවිතය",
"Using a QUIC connection over WAN": "WAN හරහා QUIC සම්බන්ධතාවයක් භාවිතය",
"Using a direct TCP connection over LAN": "LAN හරහා සෘජු TCP සම්බන්ධතාවයක් භාවිතය",
"Using a direct TCP connection over WAN": "WAN හරහා සෘජු TCP සම්බන්ධතාවයක් භාවිතය",
"Version": "අනුවාදය",
"Versions": "අනුවාද",
"Versions Path": "අනුවාද මාර්ගය",
@@ -437,19 +479,28 @@
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "නව ෆෝල්ඩරයක් එකතු කරන විට, උපාංග අතර ෆෝල්ඩර එකට ගැටගැසීමට ෆෝල්ඩර හැඳුනුම්පත භාවිතා කරන බව මතක තබා ගන්න. ඒවා සිද්ධි සංවේදී වන අතර සියලුම උපාංග අතර හරියටම ගැළපිය යුතුය.",
"Yes": "ඔව්",
"Yesterday": "ඊයේ",
"You can also copy and paste the text into a new message manually.": "ඔබට අතින් නව පණිවිඩයකට පෙළ පිටපත් කර ඇලවීමට හැකිය.",
"You can also select one of these nearby devices:": "මෙම ආසන්න උපාංග වලින් එකක් ද තේරීමට හැකිය:",
"You can change your choice at any time in the Settings dialog.": "සැකසුම් තුළ ඕනෑම විටෙක ඔබගේ තේරීම වෙනස් කිරීමට හැකිය.",
"You can read more about the two release channels at the link below.": "පහත සබැඳියෙන් නිකුතු නාලිකා දෙක ගැන තවත් කියවීමට හැකිය.",
"You have no ignored devices.": "නොසලකා හරින ලද උපාංග නැත.",
"You have no ignored folders.": "නොසලකා හරින ලද බහාලුම් නැත.",
"You have no ignored folders.": "නොසලක බහාලුම් නැත.",
"You have unsaved changes. Do you really want to discard them?": "ඔබ සතුව නොසුරැකි වෙනස්කම් ඇත. ඒවා ඉවතලීමට වුවමනාද?",
"You must keep at least one version.": "අවම වශයෙන් එක් අනුවාදයක් තබා ගත යුතුය.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "ඔබ කිසිවිටෙක \"{{receiveEncrypted}}\" ෆෝල්ඩරයකට දේශීයව කිසිවක් එකතු කිරීම හෝ වෙනස් කිරීම නොකළ යුතුය.",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "ලබන්නා තේරීමට සහ එය ඔබගේම අංකයෙන් යැවීමට කෙටි පණිවිඩ යෙදුම විවෘත විය යුතුය.",
"Your email app should open to let you choose the recipient and send it from your own address.": "ලබන්නා තේරීමට සහ එය ඔබගේම ලිපිනයෙන් යැවීමට වි-තැපැල් යෙදුම විවෘත විය යුතුය.",
"days": "දවස්",
"deleted": "මකා ඇත",
"deny": "ප්‍රතිෂේධනය",
"directories": "නාමාවලි",
"file": "ගොනුව",
"files": "ගොනු",
"folder": "බහාලුම",
"full documentation": "පූර්ණ ප්‍රලේඛනය",
"items": "අථක",
"modified": "සංශෝධිතයි",
"permit": "අවසරය",
"seconds": "තත්පර",
"theme-name-black": "කළු",
"theme-name-dark": "අඳුරු",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Dnes",
"Trash Can": "Kôš",
"Trash Can File Versioning": "Verzie súborov v koši",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX povolenia",
"Unavailable": "Nedostupné",
-1
View File
@@ -395,7 +395,6 @@
"Today": "Danes",
"Trash Can": "Koš",
"Trash Can File Versioning": "Beleženje različic datotek s Smetnjakom",
"Twitter": "Twitter",
"Type": "Vrsta",
"UNIX Permissions": "UNIX dovoljenja",
"Unavailable": "Ni na voljo",
-1
View File
@@ -64,7 +64,6 @@
"Scanning": "Duke skanuar",
"Select a version": "Zgjidh një version",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing është program falas dhe Open Source liçensuar si MPL v2.0.",
"Twitter": "Twitter",
"Type": "Lloji",
"Yes": "Po",
"days": "ditë",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Idag",
"Trash Can": "Papperskorgen",
"Trash Can File Versioning": "Papperskorgs filversionshantering",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX-behörigheter",
"Unavailable": "Otillgänglig",
-1
View File
@@ -448,7 +448,6 @@
"Today": "Bugün",
"Trash Can": "Çöp Kutusu",
"Trash Can File Versioning": "Çöp Kutusu Dosyası Sürümlendirme",
"Twitter": "Twitter",
"Type": "Tür",
"UNIX Permissions": "Unix İzinleri",
"Unavailable": "Kullanılamaz",
-1
View File
@@ -439,7 +439,6 @@
"Today": "Сьогодні",
"Trash Can": "Смітник",
"Trash Can File Versioning": "Версіювання файлів у кошику",
"Twitter": "Twitter",
"Type": "Тип",
"UNIX Permissions": "UNIX дозволи",
"Unavailable": "Недоступно",
-1
View File
@@ -205,7 +205,6 @@
"This can easily give hackers access to read and change any files on your computer.": "Th.tác này có thể khiến tin tặc dễ dàng tr.cập để đọc và th.đổi bất kỳ t.tin nào trên máy của bạn.",
"This is a major version upgrade.": "Đây là bản nâng cấp quan trọng.",
"Trash Can File Versioning": "Kiểu thùng rác",
"Twitter": "Twitter",
"Unknown": "Không biết",
"Unshared": "Chưa chia sẻ",
"Up to Date": "Đã đồng bộ",
-1
View File
@@ -448,7 +448,6 @@
"Today": "今天",
"Trash Can": "回收站",
"Trash Can File Versioning": "回收站式版本控制",
"Twitter": "推特",
"Type": "类型",
"UNIX Permissions": "UNIX权限",
"Unavailable": "无效",
-1
View File
@@ -434,7 +434,6 @@
"Today": "今天",
"Trash Can": "回收站",
"Trash Can File Versioning": "回收站式版本控制",
"Twitter": "Twitter",
"Type": "類型",
"UNIX Permissions": "UNIX權限",
"Unavailable": "無效",
+2 -1
View File
@@ -11,6 +11,7 @@
"Add Folder": "添加資料夾",
"Add Remote Device": "新增遠端裝置",
"Add devices from the introducer to our device list, for mutually shared folders.": "對於共享的資料夾,匯入引入者的裝置清單。",
"Add ignore patterns": "新增忽略模式",
"Add new folder?": "新增資料夾?",
"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.": "另外,完整地重新掃瞄的間隔將增大(時間 60,例如:新的預設值為 1 小時)。您也可以在選擇「否」後,手動配置每個資料夾的時間間隔。",
"Address": "位址",
@@ -18,6 +19,7 @@
"Advanced": "進階",
"Advanced Configuration": "進階配置",
"All Data": "全部資料",
"All Time": "所有時間",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "所有與此裝置分享的資料夾必須使用密碼保護起來,如此一來,沒有提供密碼將無法閱覽資料。",
"Allow Anonymous Usage Reporting?": "允許回報匿名數據?",
"Allowed Networks": "允許的網路",
@@ -391,7 +393,6 @@
"Time": "時間",
"Time the item was last modified": "前次修改時間",
"Trash Can File Versioning": "垃圾筒式檔案版本控制",
"Twitter": "Twitter",
"Type": "類型",
"UNIX Permissions": "UNIX 權限",
"Unavailable": "無法使用",
-2
View File
@@ -963,7 +963,6 @@
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases" target="_blank"><span class="far fa-file-alt"></span>&nbsp;<span translate>Changelog</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues" target="_blank"><span class="fas fa-bug"></span>&nbsp;<span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing" target="_blank"><span class="fas fa-wrench"></span>&nbsp;<span translate>Source Code</span></a></li>
<li><a class="navbar-link" href="https://twitter.com/syncthing" target="_blank"><span class="fab fa-twitter"></span>&nbsp;<span translate>Twitter</span></a></li>
</ul>
</div>
</nav>
@@ -1022,7 +1021,6 @@
<script type="text/javascript" src="syncthing/core/eventService.js"></script>
<script type="text/javascript" src="syncthing/core/identiconDirective.js"></script>
<script type="text/javascript" src="syncthing/core/languageSelectDirective.js"></script>
<script type="text/javascript" src="syncthing/core/lastErrorComponentFilter.js"></script>
<script type="text/javascript" src="syncthing/core/localeService.js"></script>
<script type="text/javascript" src="syncthing/core/modalDirective.js"></script>
<script type="text/javascript" src="syncthing/core/metricFilter.js"></script>
@@ -26,7 +26,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, guangwu, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Emil Lundberg, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, cui fliter, derekriemer, desbma, entity0xfe, georgespatton, ghjklw, guangwu, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
</div>
</div>
</div>
@@ -1,12 +0,0 @@
angular.module('syncthing.core')
.filter('lastErrorComponent', function () {
return function (input) {
if (input === undefined)
return "";
var parts = input.split(/:\s*/);
if (!parts || parts.length < 1) {
return input;
}
return parts[parts.length - 1];
};
});
@@ -2778,9 +2778,17 @@ angular.module('syncthing.core')
$scope.restoreVersions.tree.filterNodes(function (node) {
if (node.folder) return false;
if ($scope.restoreVersions.filters.text && node.key.indexOf($scope.restoreVersions.filters.text) < 0) {
return false;
if ($scope.restoreVersions.filters.text) {
// Use case-insensitive filter and convert backslashes to
// forward slashes to allow using them as path separators.
var filterText = $scope.restoreVersions.filters.text.toLowerCase().replace(/\\/g, '/');
var versionPath = node.key.toLowerCase().replace(/\\/g, '/');
if (versionPath.indexOf(filterText) < 0) {
return false;
}
}
if ($scope.restoreVersions.filterVersions(node.data.versions).length == 0) {
return false;
}
@@ -2869,7 +2877,7 @@ angular.module('syncthing.core')
};
$scope.hasReceiveOnlyChanged = function (folderCfg) {
if (!folderCfg || folderCfg.type !== ["receiveonly", "receiveencrypted"].indexOf(folderCfg.type) === -1) {
if (!folderCfg || ["receiveonly", "receiveencrypted"].indexOf(folderCfg.type) === -1) {
return false;
}
var counts = $scope.model[folderCfg.id];
@@ -3051,7 +3059,11 @@ angular.module('syncthing.core')
arch += " Container";
}
return $scope.version.version + ', ' + os + ' (' + arch + ')';
var verStr = $scope.version.version;
if ($scope.version.extra) {
verStr += ' (' + $scope.version.extra + ')';
}
return verStr + ', ' + os + ' (' + arch + ')';
};
$scope.versionBase = function () {
@@ -14,16 +14,16 @@
<input ng-if="editingDeviceNew()" name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
<div ng-if="!editingDeviceNew()" class="well well-sm form-control text-monospace" style="height: auto;" select-on-click>{{currentDevice.deviceID}}</div>
<div id="shareDeviceIdButtons" class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="copyToClipboard($event, currentDevice.deviceID)" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Copy' | translate }}">
<button data-container="body" type="button" class="btn btn-default" ng-click="copyToClipboard($event, currentDevice.deviceID)" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Copy' | translate }}">
<span class="fa fa-lg fa-clone"></span>
</button>
<button type="button" class="btn btn-default" ng-click="shareDeviceIdDialog('email')" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Share by Email' | translate }}">
<button data-container="body" type="button" class="btn btn-default" ng-click="shareDeviceIdDialog('email')" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Share by Email' | translate }}">
<span class="fa fa-lg fa-envelope-o"></span>
</button>
<button type="button" class="btn btn-default" ng-click="shareDeviceIdDialog('sms')" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Share by SMS' | translate }}">
<button data-container="body" type="button" class="btn btn-default" ng-click="shareDeviceIdDialog('sms')" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Share by SMS' | translate }}">
<span class="fa fa-lg fa-comments-o"></span>
</button>
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#idqr" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Show QR' | translate }}">
<button data-container="body" type="button" class="btn btn-default" data-toggle="modal" data-target="#idqr" ng-disabled="editingDeviceNew() && !deviceEditor.deviceID.$valid" tooltip data-original-title="{{ 'Show QR' | translate }}">
<span class="fa fa-lg fa-qrcode"></span>
</button>
</div>
@@ -7,7 +7,7 @@
<table class="table table-striped table-dynamic">
<tr dir-paginate="e in failed.errors | itemsPerPage: failed.perpage" current-page="failed.page" total-items="model[failed.folder].pullErrors" pagination-id="failed">
<td>{{e.path}}</td>
<td><abbr tooltip data-original-title="{{e.error}}">{{e.error | lastErrorComponent}}</abbr></td>
<td>{{e.error}}</td>
</tr>
</table>
<dir-pagination-controls on-page-change="refreshFailed(newPageNumber, failed.perpage)" pagination-id="failed"></dir-pagination-controls>
+26
View File
@@ -32,6 +32,7 @@ import (
"github.com/calmh/incontainer"
"github.com/julienschmidt/httprouter"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rcrowley/go-metrics"
"github.com/thejerf/suture/v4"
"github.com/vitrun/qart/qr"
@@ -351,6 +352,15 @@ func (s *service) Serve(ctx context.Context) error {
// Handle the special meta.js path
mux.HandleFunc("/meta.js", s.getJSMetadata)
// Handle Prometheus metrics
promHttpHandler := promhttp.Handler()
mux.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
// fetching metrics counts as an event, for the purpose of whether
// we should prepare folder summaries etc.
s.fss.OnEventRequest()
promHttpHandler.ServeHTTP(w, req)
})
guiCfg := s.cfg.GUI()
// Wrap everything in CSRF protection. The /rest prefix should be
@@ -713,6 +723,7 @@ func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) {
"version": build.Version,
"codename": build.Codename,
"longVersion": build.LongVersion,
"extra": build.Extra,
"os": runtime.GOOS,
"arch": runtime.GOARCH,
"isBeta": build.IsBeta,
@@ -1213,6 +1224,12 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
}
}
// Metrics data as text
buf := bytes.NewBuffer(nil)
wr := bufferedResponseWriter{Writer: buf}
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
files = append(files, fileEntry{name: "metrics.txt", data: buf.Bytes()})
// Heap and CPU Proofs as a pprof extension
var heapBuffer, cpuBuffer bytes.Buffer
filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
@@ -2042,3 +2059,12 @@ func httpError(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
type bufferedResponseWriter struct {
io.Writer
}
func (w bufferedResponseWriter) WriteHeader(int) {}
func (w bufferedResponseWriter) Header() http.Header {
return http.Header{}
}
+1 -1
View File
@@ -39,7 +39,7 @@ func emitLoginAttempt(success bool, username, address string, evLogger events.Lo
func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler, evLogger events.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if guiCfg.IsValidAPIKey(r.Header.Get("X-API-Key")) {
if hasValidAPIKeyHeader(r, guiCfg) {
next.ServeHTTP(w, r)
return
}
+12 -1
View File
@@ -59,7 +59,7 @@ func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidato
func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Allow requests carrying a valid API key
if m.apiKeyValidator.IsValidAPIKey(r.Header.Get("X-API-Key")) {
if hasValidAPIKeyHeader(r, m.apiKeyValidator) {
// Set the access-control-allow-origin header for CORS requests
// since a valid API key has been provided
w.Header().Add("Access-Control-Allow-Origin", "*")
@@ -178,3 +178,14 @@ func (m *csrfManager) load() {
m.tokens = append(m.tokens, s.Text())
}
}
func hasValidAPIKeyHeader(r *http.Request, validator apiKeyValidator) bool {
if key := r.Header.Get("X-API-Key"); validator.IsValidAPIKey(key) {
return true
}
if auth := r.Header.Get("Authorization"); strings.HasPrefix(strings.ToLower(auth), "bearer ") {
bearerToken := auth[len("bearer "):]
return validator.IsValidAPIKey(bearerToken)
}
return false
}
+216 -105
View File
@@ -44,8 +44,8 @@ import (
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil"
"github.com/syncthing/syncthing/lib/ur"
"github.com/syncthing/syncthing/lib/util"
"github.com/thejerf/suture/v4"
"golang.org/x/exp/slices"
)
var (
@@ -58,7 +58,7 @@ var (
func init() {
dev1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
apiCfg.GUIReturns(config.GUIConfiguration{APIKey: testAPIKey})
apiCfg.GUIReturns(config.GUIConfiguration{APIKey: testAPIKey, RawAddress: "127.0.0.1:0"})
}
func TestMain(m *testing.M) {
@@ -496,7 +496,9 @@ func TestAPIServiceRequests(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(cases[0].URL, func(t *testing.T) {
t.Parallel()
testHTTPRequest(t, baseURL, tc, testAPIKey)
})
}
@@ -557,69 +559,132 @@ func TestHTTPLogin(t *testing.T) {
cfg := newMockedConfig()
cfg.GUIReturns(config.GUIConfiguration{
User: "üser",
Password: "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq", // bcrypt of "räksmörgås" in UTF-8
User: "üser",
Password: "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq", // bcrypt of "räksmörgås" in UTF-8
RawAddress: "127.0.0.1:0",
APIKey: testAPIKey,
})
baseURL, cancel, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer cancel()
t.Cleanup(cancel)
// Verify rejection when not using authorization
t.Run("no auth is rejected", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for unauthed request", resp.StatusCode)
}
})
req, _ := http.NewRequest("GET", baseURL, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for unauthed request", resp.StatusCode)
}
t.Run("incorrect password is rejected", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.SetBasicAuth("üser", "rksmrgs")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for incorrect password", resp.StatusCode)
}
})
// Verify that incorrect password is rejected
t.Run("incorrect username is rejected", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.SetBasicAuth("user", "räksmörgås") // string literals in Go source code are in UTF-8
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for incorrect username", resp.StatusCode)
}
})
req.SetBasicAuth("üser", "rksmrgs")
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for incorrect password", resp.StatusCode)
}
t.Run("UTF-8 auth works", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.SetBasicAuth("üser", "räksmörgås") // string literals in Go source code are in UTF-8
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for authed request (UTF-8)", resp.StatusCode)
}
})
// Verify that incorrect username is rejected
t.Run("ISO-8859-1 auth work", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.SetBasicAuth("\xfcser", "r\xe4ksm\xf6rg\xe5s") // escaped ISO-8859-1
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for authed request (ISO-8859-1)", resp.StatusCode)
}
})
req.SetBasicAuth("user", "räksmörgås") // string literals in Go source code are in UTF-8
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for incorrect username", resp.StatusCode)
}
t.Run("bad X-API-Key is rejected", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.Header.Set("X-API-Key", testAPIKey+"X")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for bad API key", resp.StatusCode)
}
})
// Verify that UTF-8 auth works
t.Run("good X-API-Key is accepted", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.Header.Set("X-API-Key", testAPIKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for API key", resp.StatusCode)
}
})
req.SetBasicAuth("üser", "räksmörgås") // string literals in Go source code are in UTF-8
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for authed request (UTF-8)", resp.StatusCode)
}
t.Run("bad Bearer is rejected", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.Header.Set("Authorization", "Bearer "+testAPIKey+"X")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Unexpected non-401 return code %d for bad API key", resp.StatusCode)
}
})
// Verify that ISO-8859-1 auth
req.SetBasicAuth("\xfcser", "r\xe4ksm\xf6rg\xe5s") // escaped ISO-8859-1
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for authed request (ISO-8859-1)", resp.StatusCode)
}
t.Run("good Bearer is accepted", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL, nil)
req.Header.Set("Authorization", "Bearer "+testAPIKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected non-200 return code %d for API key", resp.StatusCode)
}
})
}
func startHTTP(cfg config.Wrapper) (string, context.CancelFunc, error) {
@@ -681,7 +746,7 @@ func TestCSRFRequired(t *testing.T) {
if err != nil {
t.Fatal("Unexpected error from getting base URL:", err)
}
defer cancel()
t.Cleanup(cancel)
cli := &http.Client{
Timeout: time.Minute,
@@ -709,42 +774,87 @@ func TestCSRFRequired(t *testing.T) {
}
}
// Calling on /rest without a token should fail
t.Run("/rest without a token should fail", func(t *testing.T) {
t.Parallel()
resp, err := cli.Get(baseURL + "/rest/system/config")
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Fatal("Getting /rest/system/config without CSRF token should fail, not", resp.Status)
}
})
resp, err = cli.Get(baseURL + "/rest/system/config")
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Fatal("Getting /rest/system/config without CSRF token should fail, not", resp.Status)
}
t.Run("/rest with a token should succeed", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("X-"+csrfTokenName, csrfTokenValue)
resp, err := cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal("Getting /rest/system/config with CSRF token should succeed, not", resp.Status)
}
})
// Calling on /rest with a token should succeed
t.Run("/rest with an incorrect API key should fail, X-API-Key version", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("X-API-Key", testAPIKey+"X")
resp, err := cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Fatal("Getting /rest/system/config with incorrect API token should fail, not", resp.Status)
}
})
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("X-"+csrfTokenName, csrfTokenValue)
resp, err = cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal("Getting /rest/system/config with CSRF token should succeed, not", resp.Status)
}
t.Run("/rest with an incorrect API key should fail, Bearer auth version", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("Authorization", "Bearer "+testAPIKey+"X")
resp, err := cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Fatal("Getting /rest/system/config with incorrect API token should fail, not", resp.Status)
}
})
// Calling on /rest with the API key should succeed
t.Run("/rest with the API key should succeed", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("X-API-Key", testAPIKey)
resp, err := cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal("Getting /rest/system/config with API key should succeed, not", resp.Status)
}
})
req, _ = http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("X-API-Key", testAPIKey)
resp, err = cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal("Getting /rest/system/config with API key should succeed, not", resp.Status)
}
t.Run("/rest with the API key as a bearer token should succeed", func(t *testing.T) {
t.Parallel()
req, _ := http.NewRequest("GET", baseURL+"/rest/system/config", nil)
req.Header.Set("Authorization", "Bearer "+testAPIKey)
resp, err := cli.Do(req)
if err != nil {
t.Fatal("Unexpected error from getting /rest/system/config:", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal("Getting /rest/system/config with API key should succeed, not", resp.Status)
}
})
}
func TestRandomString(t *testing.T) {
@@ -943,30 +1053,31 @@ func TestHostCheck(t *testing.T) {
t.Error("Incorrect host header, check disabled: expected 200 OK, not", resp.Status)
}
// A server bound to a wildcard address also doesn't do the check
if !testing.Short() {
// A server bound to a wildcard address also doesn't do the check
cfg = newMockedConfig()
cfg.GUIReturns(config.GUIConfiguration{
RawAddress: "0.0.0.0:0",
InsecureSkipHostCheck: true,
})
baseURL, cancel, err = startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer cancel()
cfg = newMockedConfig()
cfg.GUIReturns(config.GUIConfiguration{
RawAddress: "0.0.0.0:0",
})
baseURL, cancel, err = startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer cancel()
// A request with a suspicious Host header should be allowed
// A request with a suspicious Host header should be allowed
req, _ = http.NewRequest("GET", baseURL, nil)
req.Host = "example.com"
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Error("Incorrect host header, wildcard bound: expected 200 OK, not", resp.Status)
req, _ = http.NewRequest("GET", baseURL, nil)
req.Host = "example.com"
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Error("Incorrect host header, wildcard bound: expected 200 OK, not", resp.Status)
}
}
// This should all work over IPv6 as well
@@ -1202,7 +1313,7 @@ func TestBrowse(t *testing.T) {
for _, tc := range cases {
ret := browseFiles(ffs, tc.current)
if !util.EqualStrings(ret, tc.returns) {
if !slices.Equal(ret, tc.returns) {
t.Errorf("browseFiles(%q) => %q, expected %q", tc.current, ret, tc.returns)
}
}
+6 -6
View File
@@ -15,7 +15,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/structutil"
)
type configMuxBuilder struct {
@@ -212,7 +212,7 @@ func (c *configMuxBuilder) registerDefaultFolder(path string) {
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var cfg config.FolderConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
c.adjustFolder(w, r, cfg, true)
})
@@ -228,7 +228,7 @@ func (c *configMuxBuilder) registerDefaultDevice(path string) {
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var cfg config.DeviceConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
c.adjustDevice(w, r, cfg, true)
})
@@ -266,7 +266,7 @@ func (c *configMuxBuilder) registerOptions(path string) {
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var cfg config.OptionsConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
c.adjustOptions(w, r, cfg)
})
@@ -282,7 +282,7 @@ func (c *configMuxBuilder) registerLDAP(path string) {
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var cfg config.LDAPConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
c.adjustLDAP(w, r, cfg)
})
@@ -298,7 +298,7 @@ func (c *configMuxBuilder) registerGUI(path string) {
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var cfg config.GUIConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
c.adjustGUI(w, r, cfg)
})
+1 -1
View File
@@ -52,7 +52,7 @@ func writeBroadcasts(ctx context.Context, inbox <-chan []byte, port int) error {
var dsts []net.IP
for _, intf := range intfs {
if intf.Flags&net.FlagBroadcast == 0 {
if intf.Flags&net.FlagRunning == 0 || intf.Flags&net.FlagBroadcast == 0 {
continue
}
+1 -1
View File
@@ -67,7 +67,7 @@ func writeMulticasts(ctx context.Context, inbox <-chan []byte, addr string) erro
success := 0
for _, intf := range intfs {
if intf.Flags&net.FlagMulticast == 0 {
if intf.Flags&net.FlagRunning == 0 || intf.Flags&net.FlagMulticast == 0 {
continue
}
+19
View File
@@ -35,6 +35,7 @@ var (
IsCandidate bool
IsBeta bool
LongVersion string
Extra string
allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-[^\s]+)?$`)
@@ -46,6 +47,8 @@ var (
}
)
const versionExtraAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-. "
func init() {
if Version != "unknown-dev" {
// If not a generic dev build, version string should come from git describe
@@ -75,6 +78,7 @@ func setBuildData() {
IsRelease = exp.MatchString(Version)
IsCandidate = strings.Contains(Version, "-rc.")
IsBeta = strings.Contains(Version, "-")
Extra = filterString(os.Getenv("STVERSIONEXTRA"), versionExtraAllowedChars)
stamp, _ := strconv.Atoi(Stamp)
Date = time.Unix(int64(stamp), 0)
@@ -103,7 +107,22 @@ func TagsList() []string {
tags = append(tags, strings.ToLower(envVar))
}
}
if Extra != "" {
tags = append(tags, Extra)
}
sort.Strings(tags)
return tags
}
// filterString returns a copy of s with all characters not in allowedChars
// removed.
func filterString(s, allowedChars string) string {
var res strings.Builder
for _, c := range s {
if strings.ContainsRune(allowedChars, c) {
res.WriteRune(c)
}
}
return res.String()
}
+20
View File
@@ -35,3 +35,23 @@ func TestAllowedVersions(t *testing.T) {
}
}
}
func TestFilterString(t *testing.T) {
cases := []struct {
input string
filter string
output string
}{
{"abcba", "abc", "abcba"},
{"abcba", "ab", "abba"},
{"abcba", "c", "c"},
{"abcba", "!", ""},
{"Foo (v1.5)", versionExtraAllowedChars, "Foo v1.5"},
}
for i, c := range cases {
if out := filterString(c.input, c.filter); out != c.output {
t.Errorf("%d: %q != %q", i, out, c.output)
}
}
}
+44 -12
View File
@@ -16,14 +16,16 @@ import (
"net"
"net/url"
"os"
"reflect"
"sort"
"strconv"
"strings"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/netutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/structutil"
)
const (
@@ -42,9 +44,9 @@ var (
// "consumer" of the configuration as we don't want these saved to the
// config.
DefaultListenAddresses = []string{
util.Address("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(DefaultTCPPort))),
netutil.AddressURL("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(DefaultTCPPort))),
"dynamic+https://relays.syncthing.net/endpoint",
util.Address("quic", net.JoinHostPort("0.0.0.0", strconv.Itoa(DefaultQUICPort))),
netutil.AddressURL("quic", net.JoinHostPort("0.0.0.0", strconv.Itoa(DefaultQUICPort))),
}
DefaultGUIPort = 8384
// DefaultDiscoveryServersV4 should be substituted when the configuration
@@ -101,7 +103,7 @@ func New(myID protocol.DeviceID) Configuration {
cfg.Options.UnackedNotificationIDs = []string{"authenticationUserAndPassword"}
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
// Can't happen.
if err := cfg.prepare(myID); err != nil {
@@ -127,9 +129,9 @@ func (cfg *Configuration) ProbeFreePorts() error {
cfg.Options.RawListenAddresses = []string{"default"}
} else {
cfg.Options.RawListenAddresses = []string{
util.Address("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
netutil.AddressURL("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
"dynamic+https://relays.syncthing.net/endpoint",
util.Address("quic", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
netutil.AddressURL("quic", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
}
}
@@ -144,7 +146,7 @@ type xmlConfiguration struct {
func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, int, error) {
var cfg xmlConfiguration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
if err := xml.NewDecoder(r).Decode(&cfg); err != nil {
return Configuration{}, 0, err
@@ -166,7 +168,7 @@ func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
var cfg Configuration
util.SetDefaults(&cfg)
structutil.SetDefaults(&cfg)
if err := json.Unmarshal(bs, &cfg); err != nil {
return Configuration{}, err
@@ -259,7 +261,7 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) error {
cfg.removeDeprecatedProtocols()
util.FillNilExceptDeprecated(cfg)
structutil.FillNilExceptDeprecated(cfg)
// TestIssue1750 relies on migrations happening after preparing options.
cfg.applyMigrations()
@@ -556,8 +558,8 @@ loop:
func ensureNoUntrustedTrustingSharing(f *FolderConfiguration, devices []FolderDeviceConfiguration, existingDevices map[protocol.DeviceID]*DeviceConfiguration) []FolderDeviceConfiguration {
for i := 0; i < len(devices); i++ {
dev := devices[i]
if dev.EncryptionPassword != "" {
// There's a password set, no check required
if dev.EncryptionPassword != "" || f.Type == FolderTypeReceiveEncrypted {
// There's a password set or the folder is received encrypted, no check required
continue
}
if devCfg := existingDevices[dev.DeviceID]; devCfg.Untrusted {
@@ -636,7 +638,7 @@ func (defaults *Defaults) prepare(myID protocol.DeviceID, existingDevices map[pr
}
func ensureZeroForNodefault(empty interface{}, target interface{}) {
util.CopyMatchingTag(empty, target, "nodefault", func(v string) bool {
copyMatchingTag(empty, target, "nodefault", func(v string) bool {
if len(v) > 0 && v != "true" {
panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "true"`, v))
}
@@ -644,6 +646,36 @@ func ensureZeroForNodefault(empty interface{}, target interface{}) {
})
}
// copyMatchingTag copies fields tagged tag:"value" from "from" struct onto "to" struct.
func copyMatchingTag(from interface{}, to interface{}, tag string, shouldCopy func(value string) bool) {
fromStruct := reflect.ValueOf(from).Elem()
fromType := fromStruct.Type()
toStruct := reflect.ValueOf(to).Elem()
toType := toStruct.Type()
if fromType != toType {
panic(fmt.Sprintf("non equal types: %s != %s", fromType, toType))
}
for i := 0; i < toStruct.NumField(); i++ {
fromField := fromStruct.Field(i)
toField := toStruct.Field(i)
if !toField.CanSet() {
// Unexported fields
continue
}
structTag := toType.Field(i).Tag
v := structTag.Get(tag)
if shouldCopy(v) {
toField.Set(fromField)
}
}
}
func (i Ignores) Copy() Ignores {
out := Ignores{Lines: make([]string, len(i.Lines))}
copy(out.Lines, i.Lines)
+58
View File
@@ -1597,3 +1597,61 @@ func handleFile(name string) {
fd.Write(origin)
fd.Close()
}
func TestCopyMatching(t *testing.T) {
type Nested struct {
A int
}
type Test struct {
CopyA int
CopyB []string
CopyC Nested
CopyD *Nested
NoCopy int `restart:"true"`
}
from := Test{
CopyA: 1,
CopyB: []string{"friend", "foe"},
CopyC: Nested{
A: 2,
},
CopyD: &Nested{
A: 3,
},
NoCopy: 4,
}
to := Test{
CopyA: 11,
CopyB: []string{"foot", "toe"},
CopyC: Nested{
A: 22,
},
CopyD: &Nested{
A: 33,
},
NoCopy: 44,
}
// Copy empty fields
copyMatchingTag(&from, &to, "restart", func(v string) bool {
return v != "true"
})
if to.CopyA != 1 {
t.Error("CopyA")
}
if len(to.CopyB) != 2 || to.CopyB[0] != "friend" || to.CopyB[1] != "foe" {
t.Error("CopyB")
}
if to.CopyC.A != 2 {
t.Error("CopyC")
}
if to.CopyD.A != 3 {
t.Error("CopyC")
}
if to.NoCopy != 44 {
t.Error("NoCopy")
}
}
+1 -2
View File
@@ -21,7 +21,6 @@ import (
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
)
var (
@@ -244,7 +243,7 @@ func (f FolderConfiguration) RequiresRestartOnly() FolderConfiguration {
// copier, yet should not cause a restart.
blank := FolderConfiguration{}
util.CopyMatchingTag(&blank, &copy, "restart", func(v string) bool {
copyMatchingTag(&blank, &copy, "restart", func(v string) bool {
if len(v) > 0 && v != "false" {
panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "false"`, v))
}
+5 -5
View File
@@ -17,8 +17,8 @@ import (
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/netutil"
"github.com/syncthing/syncthing/lib/upgrade"
"github.com/syncthing/syncthing/lib/util"
)
// migrations is the set of config migration functions, with their target
@@ -197,11 +197,11 @@ func migrateToConfigV24(cfg *Configuration) {
}
func migrateToConfigV23(cfg *Configuration) {
permBits := fs.FileMode(0777)
permBits := fs.FileMode(0o777)
if build.IsWindows {
// Windows has no umask so we must chose a safer set of bits to
// begin with.
permBits = 0700
permBits = 0o700
}
// Upgrade code remains hardcoded for .stfolder despite configurable
@@ -391,14 +391,14 @@ func migrateToConfigV12(cfg *Configuration) {
// Change listen address schema
for i, addr := range cfg.Options.RawListenAddresses {
if len(addr) > 0 && !strings.HasPrefix(addr, "tcp://") {
cfg.Options.RawListenAddresses[i] = util.Address("tcp", addr)
cfg.Options.RawListenAddresses[i] = netutil.AddressURL("tcp", addr)
}
}
for i, device := range cfg.Devices {
for j, addr := range device.Addresses {
if addr != "dynamic" && addr != "" {
cfg.Devices[i].Addresses[j] = util.Address("tcp", addr)
cfg.Devices[i].Addresses[j] = netutil.AddressURL("tcp", addr)
}
}
}
+9 -8
View File
@@ -12,7 +12,8 @@ import (
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/stringutil"
"github.com/syncthing/syncthing/lib/structutil"
)
func (opts OptionsConfiguration) Copy() OptionsConfiguration {
@@ -29,10 +30,10 @@ func (opts OptionsConfiguration) Copy() OptionsConfiguration {
}
func (opts *OptionsConfiguration) prepare(guiPWIsSet bool) {
util.FillNilSlices(opts)
structutil.FillNilSlices(opts)
opts.RawListenAddresses = util.UniqueTrimmedStrings(opts.RawListenAddresses)
opts.RawGlobalAnnServers = util.UniqueTrimmedStrings(opts.RawGlobalAnnServers)
opts.RawListenAddresses = stringutil.UniqueTrimmedStrings(opts.RawListenAddresses)
opts.RawGlobalAnnServers = stringutil.UniqueTrimmedStrings(opts.RawGlobalAnnServers)
// Very short reconnection intervals are annoying
if opts.ReconnectIntervalS < 5 {
@@ -71,7 +72,7 @@ func (opts *OptionsConfiguration) prepare(guiPWIsSet bool) {
func (opts OptionsConfiguration) RequiresRestartOnly() OptionsConfiguration {
optsCopy := opts
blank := OptionsConfiguration{}
util.CopyMatchingTag(&blank, &optsCopy, "restart", func(v string) bool {
copyMatchingTag(&blank, &optsCopy, "restart", func(v string) bool {
if len(v) > 0 && v != "true" {
panic(fmt.Sprintf(`unexpected tag value: %s. Expected untagged or "true"`, v))
}
@@ -94,7 +95,7 @@ func (opts OptionsConfiguration) ListenAddresses() []string {
addresses = append(addresses, addr)
}
}
return util.UniqueTrimmedStrings(addresses)
return stringutil.UniqueTrimmedStrings(addresses)
}
func (opts OptionsConfiguration) StunServers() []string {
@@ -116,7 +117,7 @@ func (opts OptionsConfiguration) StunServers() []string {
}
}
addresses = util.UniqueTrimmedStrings(addresses)
addresses = stringutil.UniqueTrimmedStrings(addresses)
return addresses
}
@@ -135,7 +136,7 @@ func (opts OptionsConfiguration) GlobalDiscoveryServers() []string {
servers = append(servers, srv)
}
}
return util.UniqueTrimmedStrings(servers)
return stringutil.UniqueTrimmedStrings(servers)
}
func (opts OptionsConfiguration) MaxFolderConcurrency() int {
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/structutil"
)
type TestStruct struct {
@@ -20,7 +20,7 @@ type TestStruct struct {
func TestSizeDefaults(t *testing.T) {
x := &TestStruct{}
util.SetDefaults(x)
structutil.SetDefaults(x)
if !x.Size.Percentage() {
t.Error("not percentage")
+3 -3
View File
@@ -12,7 +12,7 @@ import (
"sort"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/structutil"
)
// internalVersioningConfiguration is used in XML serialization
@@ -39,7 +39,7 @@ func (c VersioningConfiguration) Copy() VersioningConfiguration {
}
func (c *VersioningConfiguration) UnmarshalJSON(data []byte) error {
util.SetDefaults(c)
structutil.SetDefaults(c)
type noCustomUnmarshal VersioningConfiguration
ptr := (*noCustomUnmarshal)(c)
return json.Unmarshal(data, ptr)
@@ -47,7 +47,7 @@ func (c *VersioningConfiguration) UnmarshalJSON(data []byte) error {
func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var intCfg internalVersioningConfiguration
util.SetDefaults(&intCfg)
structutil.SetDefaults(&intCfg)
if err := d.DecodeElement(&intCfg, &start); err != nil {
return err
}
+6 -10
View File
@@ -52,27 +52,23 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
return internalConn{}, err
}
var conn net.PacketConn
// We need to track who created the conn.
// Given we always pass the connection to quic, it assumes it's a remote connection it never closes it,
// So our wrapper around it needs to close it, but it only needs to close it if it's not the listening connection.
// If we created the conn we need to close it at the end. If we got a
// Transport from the registry we have no conn to close.
var createdConn net.PacketConn
listenConn := d.registry.Get(uri.Scheme, packetConnUnspecified)
if listenConn != nil {
conn = listenConn.(net.PacketConn)
} else {
transport, _ := d.registry.Get(uri.Scheme, transportConnUnspecified).(*quic.Transport)
if transport == nil {
if packetConn, err := net.ListenPacket("udp", ":0"); err != nil {
return internalConn{}, err
} else {
conn = packetConn
createdConn = packetConn
transport = &quic.Transport{Conn: packetConn}
}
}
ctx, cancel := context.WithTimeout(ctx, quicOperationTimeout)
defer cancel()
session, err := quic.DialContext(ctx, conn, addr, uri.Host, d.tlsCfg, quicConfig)
session, err := transport.Dial(ctx, addr, d.tlsCfg, quicConfig)
if err != nil {
if createdConn != nil {
_ = createdConn.Close()
+11 -6
View File
@@ -95,17 +95,22 @@ func (t *quicListener) serve(ctx context.Context) error {
l.Infoln("Listen (BEP/quic):", err)
return err
}
defer func() { _ = udpConn.Close() }()
defer udpConn.Close()
svc, conn := stun.New(t.cfg, t, udpConn)
defer conn.Close()
tracer := &writeTrackingTracer{}
quicTransport := &quic.Transport{
Conn: udpConn,
Tracer: tracer,
}
defer quicTransport.Close()
svc := stun.New(t.cfg, t, &transportPacketConn{tran: quicTransport}, tracer)
go svc.Serve(ctx)
t.registry.Register(t.uri.Scheme, conn)
defer t.registry.Unregister(t.uri.Scheme, conn)
t.registry.Register(t.uri.Scheme, quicTransport)
defer t.registry.Unregister(t.uri.Scheme, quicTransport)
listener, err := quic.Listen(conn, t.tlsCfg, quicConfig)
listener, err := quicTransport.Listen(t.tlsCfg, quicConfig)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
return err
+72 -6
View File
@@ -10,20 +10,22 @@
package connections
import (
"context"
"crypto/tls"
"net"
"net/url"
"sync/atomic"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/logging"
"github.com/syncthing/syncthing/lib/osutil"
)
var quicConfig = &quic.Config{
ConnectionIDLength: 4,
MaxIdleTimeout: 30 * time.Second,
KeepAlivePeriod: 15 * time.Second,
MaxIdleTimeout: 30 * time.Second,
KeepAlivePeriod: 15 * time.Second,
}
func quicNetwork(uri *url.URL) string {
@@ -61,11 +63,75 @@ func (q *quicTlsConn) Close() error {
}
func (q *quicTlsConn) ConnectionState() tls.ConnectionState {
return q.Connection.ConnectionState().TLS.ConnectionState
return q.Connection.ConnectionState().TLS
}
func packetConnUnspecified(conn interface{}) bool {
addr := conn.(net.PacketConn).LocalAddr()
func transportConnUnspecified(conn any) bool {
tran, ok := conn.(*quic.Transport)
if !ok {
return false
}
addr := tran.Conn.LocalAddr()
ip, err := osutil.IPFromAddr(addr)
return err == nil && ip.IsUnspecified()
}
type writeTrackingTracer struct {
lastWrite atomic.Int64 // unix nanos
}
func (t *writeTrackingTracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {
t.lastWrite.Store(time.Now().UnixNano())
}
func (t *writeTrackingTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src logging.ArbitraryLenConnectionID, _ []quic.VersionNumber) {
t.lastWrite.Store(time.Now().UnixNano())
}
func (t *writeTrackingTracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) {
}
func (t *writeTrackingTracer) LastWrite() time.Time {
return time.Unix(0, t.lastWrite.Load())
}
// A transportPacketConn is a net.PacketConn that uses a quic.Transport.
type transportPacketConn struct {
tran *quic.Transport
readDeadline atomic.Value // time.Time
}
func (t *transportPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
ctx := context.Background()
if deadline, ok := t.readDeadline.Load().(time.Time); ok && !deadline.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, deadline)
defer cancel()
}
return t.tran.ReadNonQUICPacket(ctx, p)
}
func (t *transportPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return t.tran.WriteTo(p, addr)
}
func (t *transportPacketConn) Close() error {
return errUnsupported
}
func (t *transportPacketConn) LocalAddr() net.Addr {
return t.tran.Conn.LocalAddr()
}
func (t *transportPacketConn) SetDeadline(deadline time.Time) error {
return t.SetReadDeadline(deadline)
}
func (t *transportPacketConn) SetReadDeadline(deadline time.Time) error {
t.readDeadline.Store(deadline)
return nil
}
func (t *transportPacketConn) SetWriteDeadline(_ time.Time) error {
return nil // yolo
}
+4
View File
@@ -9,6 +9,10 @@
package connections
import (
"fmt"
)
var errNotInBuild = fmt.Errorf("%w: disabled at build time", errUnsupported)
func init() {
+1 -1
View File
@@ -65,7 +65,7 @@ func (d *relayDialer) Dial(ctx context.Context, id protocol.DeviceID, uri *url.U
return newInternalConn(tc, connTypeRelayClient, false, d.wanPriority), nil
}
func (d *relayDialer) Priority(host string) int {
func (d *relayDialer) Priority(_ string) int {
return d.wanPriority
}
+11 -8
View File
@@ -23,6 +23,8 @@ import (
stdsync "sync"
"time"
"golang.org/x/exp/slices"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/discover"
@@ -30,9 +32,10 @@ import (
"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/semaphore"
"github.com/syncthing/syncthing/lib/stringutil"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
// Registers NAT service providers
_ "github.com/syncthing/syncthing/lib/pmp"
@@ -582,7 +585,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
// allowed additional number of connections (if limited).
numConns := 0
var numConnsMut stdsync.Mutex
dialSemaphore := util.NewSemaphore(dialMaxParallel)
dialSemaphore := semaphore.New(dialMaxParallel)
dialWG := new(stdsync.WaitGroup)
dialCtx, dialCancel := context.WithCancel(ctx)
defer func() {
@@ -698,7 +701,7 @@ func (s *service) resolveDeviceAddrs(ctx context.Context, cfg config.DeviceConfi
addrs = append(addrs, addr)
}
}
return util.UniqueTrimmedStrings(addrs)
return stringutil.UniqueTrimmedStrings(addrs)
}
type lanChecker struct {
@@ -875,7 +878,7 @@ func (s *service) checkAndSignalConnectLoopOnUpdatedDevices(from, to config.Conf
if oldDev, ok := oldDevices[dev.DeviceID]; !ok || oldDev.Paused {
s.dialNowDevices[dev.DeviceID] = struct{}{}
dial = true
} else if !util.EqualStrings(oldDev.Addresses, dev.Addresses) {
} else if !slices.Equal(oldDev.Addresses, dev.Addresses) {
dial = true
}
}
@@ -905,7 +908,7 @@ func (s *service) AllAddresses() []string {
}
}
s.listenersMut.RUnlock()
return util.UniqueTrimmedStrings(addrs)
return stringutil.UniqueTrimmedStrings(addrs)
}
func (s *service) ExternalAddresses() []string {
@@ -920,7 +923,7 @@ func (s *service) ExternalAddresses() []string {
}
}
s.listenersMut.RUnlock()
return util.UniqueTrimmedStrings(addrs)
return stringutil.UniqueTrimmedStrings(addrs)
}
func (s *service) ListenerStatus() map[string]ListenerStatusEntry {
@@ -1079,7 +1082,7 @@ func IsAllowedNetwork(host string, allowed []string) bool {
return false
}
func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, dialTargets []dialTarget, parentSema *util.Semaphore) (internalConn, bool) {
func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, dialTargets []dialTarget, parentSema *semaphore.Semaphore) (internalConn, bool) {
// Group targets into buckets by priority
dialTargetBuckets := make(map[int][]dialTarget, len(dialTargets))
for _, tgt := range dialTargets {
@@ -1095,7 +1098,7 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
// Sort the priorities so that we dial lowest first (which means highest...)
sort.Ints(priorities)
sema := util.MultiSemaphore{util.NewSemaphore(dialMaxParallelPerDevice), parentSema}
sema := semaphore.MultiSemaphore{semaphore.New(dialMaxParallelPerDevice), parentSema}
for _, prio := range priorities {
tgts := dialTargetBuckets[prio]
res := make(chan internalConn, len(tgts))
+1 -1
View File
@@ -214,7 +214,7 @@ func (t *tcpListener) Factory() listenerFactory {
return t.factory
}
func (t *tcpListener) NATType() string {
func (*tcpListener) NATType() string {
return "unknown"
}
+3 -17
View File
@@ -71,12 +71,9 @@ func getHostPortsForAllAdapters(port int) []string {
portStr := strconv.Itoa(port)
for _, network := range nets {
// Only IPv4 addresses, as v6 link local require an interface identifiers to work correctly
// And non link local in theory are globally routable anyway.
if network.IP.To4() == nil {
continue
}
if network.IP.IsLinkLocalUnicast() || (isV4Local(network.IP) && network.IP.IsGlobalUnicast()) {
// Only accept IPv4 link-local unicast and the private ranges defined in RFC 1918 and RFC 4193
// IPv6 link-local addresses require an interface identifier to work correctly
if (network.IP.To4() != nil && network.IP.IsLinkLocalUnicast()) || network.IP.IsPrivate() {
hostPorts = append(hostPorts, net.JoinHostPort(network.IP.String(), portStr))
}
}
@@ -107,17 +104,6 @@ func resolve(network, hostPort string) (net.IP, int, error) {
return net.IPv4zero, 0, net.UnknownNetworkError(network)
}
func isV4Local(ip net.IP) bool {
// See https://go-review.googlesource.com/c/go/+/162998/
// We only take the V4 part of that.
if ip4 := ip.To4(); ip4 != nil {
return ip4[0] == 10 ||
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
(ip4[0] == 192 && ip4[1] == 168)
}
return false
}
func maybeReplacePort(uri *url.URL, laddr net.Addr) *url.URL {
if laddr == nil {
return uri
+2 -2
View File
@@ -23,9 +23,9 @@ import (
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sha256"
"github.com/syncthing/syncthing/lib/stringutil"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
"github.com/thejerf/suture/v4"
)
@@ -1042,7 +1042,7 @@ func (db *Lowlevel) loadMetadataTracker(folder string) (*metadataTracker, error)
}
if age := time.Since(meta.Created()); age > db.recheckInterval {
l.Infof("Stored folder metadata for %q is %v old; recalculating", folder, util.NiceDurationString(age))
l.Infof("Stored folder metadata for %q is %v old; recalculating", folder, stringutil.NiceDurationString(age))
return db.getMetaAndCheck(folder)
}
+2 -2
View File
@@ -241,9 +241,9 @@ func (s *fakeDiscoveryServer) handler(w http.ResponseWriter, r *http.Request) {
type fakeAddressLister struct{}
func (f *fakeAddressLister) ExternalAddresses() []string {
func (*fakeAddressLister) ExternalAddresses() []string {
return []string{"tcp://0.0.0.0:22000"}
}
func (f *fakeAddressLister) AllAddresses() []string {
func (*fakeAddressLister) AllAddresses() []string {
return []string{"tcp://0.0.0.0:22000", "tcp://192.168.0.1:22000"}
}
+5 -5
View File
@@ -22,9 +22,9 @@ import (
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/stringutil"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
)
// The Manager aggregates results from multiple Finders. Each Finder has
@@ -158,7 +158,7 @@ func (m *manager) Lookup(ctx context.Context, deviceID protocol.DeviceID) (addre
}
m.mut.RUnlock()
addresses = util.UniqueTrimmedStrings(addresses)
addresses = stringutil.UniqueTrimmedStrings(addresses)
sort.Strings(addresses)
l.Debugln("lookup results for", deviceID)
@@ -167,11 +167,11 @@ func (m *manager) Lookup(ctx context.Context, deviceID protocol.DeviceID) (addre
return addresses, nil
}
func (m *manager) String() string {
func (*manager) String() string {
return "discovery cache"
}
func (m *manager) Error() error {
func (*manager) Error() error {
return nil
}
@@ -223,7 +223,7 @@ func (m *manager) Cache() map[protocol.DeviceID]CacheEntry {
m.mut.RUnlock()
for k, v := range res {
v.Addresses = util.UniqueTrimmedStrings(v.Addresses)
v.Addresses = stringutil.UniqueTrimmedStrings(v.Addresses)
res[k] = v
}
+3
View File
@@ -297,6 +297,7 @@ loop:
case e := <-l.events:
// Incoming events get sent
l.sendEvent(e)
metricEvents.WithLabelValues(e.Type.String(), metricEventStateCreated).Inc()
case fn := <-l.funcs:
// Subscriptions are handled here.
@@ -345,9 +346,11 @@ func (l *logger) sendEvent(e Event) {
select {
case s.events <- e:
metricEvents.WithLabelValues(e.Type.String(), metricEventStateDelivered).Inc()
case <-l.timeout.C:
// if s.events is not ready, drop the event
timedOut = true
metricEvents.WithLabelValues(e.Type.String(), metricEventStateDropped).Inc()
}
// If stop returns false it already sent something to the
+25
View File
@@ -0,0 +1,25 @@
// Copyright (C) 2023 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package events
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var metricEvents = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "events",
Name: "total",
Help: "Total number of created/forwarded/dropped events",
}, []string{"event", "state"})
const (
metricEventStateCreated = "created"
metricEventStateDelivered = "delivered"
metricEventStateDropped = "dropped"
)
+5 -1
View File
@@ -28,6 +28,7 @@ const (
filesystemWrapperTypeError
filesystemWrapperTypeWalk
filesystemWrapperTypeLog
filesystemWrapperTypeMetrics
)
type XattrFilter interface {
@@ -275,6 +276,8 @@ func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem
fs = mtimeOpt.apply(fs)
}
fs = &metricsFS{next: fs}
if l.ShouldDebug("walkfs") {
return NewWalkFilesystem(&logFilesystem{fs})
}
@@ -290,7 +293,8 @@ func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem
// root, represents an internal file that should always be ignored. The file
// path must be clean (i.e., in canonical shortest form).
func IsInternal(file string) bool {
// fs cannot import config, so we hard code .stfolder here (config.DefaultMarkerName)
// fs cannot import config or versioner, so we hard code .stfolder
// (config.DefaultMarkerName) and .stversions (versioner.DefaultPath)
internals := []string{".stfolder", ".stignore", ".stversions"}
for _, internal := range internals {
if file == internal {
+9 -1
View File
@@ -320,7 +320,15 @@ func TestCopyRange(tttt *testing.T) {
t.Fatal(err)
}
if err := impl(src.(basicFile), dst.(basicFile), testCase.srcOffset, testCase.dstOffset, testCase.copySize); err != nil {
srcBasic, ok := unwrap(src).(basicFile)
if !ok {
t.Fatal("src file is not a basic file")
}
dstBasic, ok := unwrap(dst).(basicFile)
if !ok {
t.Fatal("dst file is not a basic file")
}
if err := impl(srcBasic, dstBasic, testCase.srcOffset, testCase.dstOffset, testCase.copySize); err != nil {
if err == syscall.ENOTSUP {
// Test runner can adjust directory in which to run the tests, that allow broader tests.
t.Skip("Not supported on the current filesystem, set STFSTESTPATH env var.")
+339
View File
@@ -0,0 +1,339 @@
// Copyright (C) 2023 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package fs
import (
"context"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/syncthing/syncthing/lib/protocol"
)
var (
metricTotalOperationSeconds = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "fs",
Name: "operation_seconds_total",
Help: "Total time spent in filesystem operations, per filesystem root and operation",
}, []string{"root", "operation"})
metricTotalOperationsCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "fs",
Name: "operations_total",
Help: "Total number of filesystem operations, per filesystem root and operation",
}, []string{"root", "operation"})
metricTotalBytesCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "fs",
Name: "operation_bytes_total",
Help: "Total number of filesystem bytes transferred, per filesystem root and operation",
}, []string{"root", "operation"})
)
const (
// fs operations
metricOpChmod = "chmod"
metricOpLchmod = "lchmod"
metricOpChtimes = "chtimes"
metricOpCreate = "create"
metricOpCreateSymlink = "createsymlink"
metricOpDirNames = "dirnames"
metricOpLstat = "lstat"
metricOpMkdir = "mdkir"
metricOpMkdirAll = "mkdirall"
metricOpOpen = "open"
metricOpOpenFile = "openfile"
metricOpReadSymlink = "readsymlink"
metricOpRemove = "remove"
metricOpRemoveAll = "removeall"
metricOpRename = "rename"
metricOpStat = "stat"
metricOpSymlinksSupported = "symlinkssupported"
metricOpWalk = "walk"
metricOpWatch = "watch"
metricOpHide = "hide"
metricOpUnhide = "unhide"
metricOpGlob = "glob"
metricOpRoots = "roots"
metricOpUsage = "usage"
metricOpType = "type"
metricOpURI = "uri"
metricOpOptions = "options"
metricOpSameFile = "samefile"
metricOpPlatformData = "platformdata"
metricOpGetXattr = "getxattr"
metricOpSetXattr = "setxattr"
// file operations
metricOpRead = "read"
metricOpReadAt = "readat"
metricOpWrite = "write"
metricOpWriteAt = "writeat"
metricOpTruncate = "truncate"
metricOpSeek = "seek"
metricOpSync = "sync"
metricOpClose = "close"
metricOpName = "name"
)
type metricsFS struct {
next Filesystem
}
var _ Filesystem = (*metricsFS)(nil)
func (m *metricsFS) account(op string) func(bytes int) {
t0 := time.Now()
root := m.next.URI()
return func(bytes int) {
metricTotalOperationSeconds.WithLabelValues(root, op).Add(time.Since(t0).Seconds())
metricTotalOperationsCount.WithLabelValues(root, op).Inc()
if bytes >= 0 {
metricTotalBytesCount.WithLabelValues(root, op).Add(float64(bytes))
}
}
}
func (m *metricsFS) Chmod(name string, mode FileMode) error {
defer m.account(metricOpChmod)(-1)
return m.next.Chmod(name, mode)
}
func (m *metricsFS) Lchown(name string, uid, gid string) error {
defer m.account(metricOpLchmod)(-1)
return m.next.Lchown(name, uid, gid)
}
func (m *metricsFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
defer m.account(metricOpChtimes)(-1)
return m.next.Chtimes(name, atime, mtime)
}
func (m *metricsFS) Create(name string) (File, error) {
defer m.account(metricOpCreate)(-1)
f, err := m.next.Create(name)
if err != nil {
return nil, err
}
return &metricsFile{next: f, fs: m}, nil
}
func (m *metricsFS) CreateSymlink(target, name string) error {
defer m.account(metricOpCreateSymlink)(-1)
return m.next.CreateSymlink(target, name)
}
func (m *metricsFS) DirNames(name string) ([]string, error) {
defer m.account(metricOpDirNames)(-1)
return m.next.DirNames(name)
}
func (m *metricsFS) Lstat(name string) (FileInfo, error) {
defer m.account(metricOpLstat)(-1)
return m.next.Lstat(name)
}
func (m *metricsFS) Mkdir(name string, perm FileMode) error {
defer m.account(metricOpMkdir)(-1)
return m.next.Mkdir(name, perm)
}
func (m *metricsFS) MkdirAll(name string, perm FileMode) error {
defer m.account(metricOpMkdirAll)(-1)
return m.next.MkdirAll(name, perm)
}
func (m *metricsFS) Open(name string) (File, error) {
defer m.account(metricOpOpen)(-1)
f, err := m.next.Open(name)
if err != nil {
return nil, err
}
return &metricsFile{next: f, fs: m}, nil
}
func (m *metricsFS) OpenFile(name string, flags int, mode FileMode) (File, error) {
defer m.account(metricOpOpenFile)(-1)
f, err := m.next.OpenFile(name, flags, mode)
if err != nil {
return nil, err
}
return &metricsFile{next: f, fs: m}, nil
}
func (m *metricsFS) ReadSymlink(name string) (string, error) {
defer m.account(metricOpReadSymlink)(-1)
return m.next.ReadSymlink(name)
}
func (m *metricsFS) Remove(name string) error {
defer m.account(metricOpRemove)(-1)
return m.next.Remove(name)
}
func (m *metricsFS) RemoveAll(name string) error {
defer m.account(metricOpRemoveAll)(-1)
return m.next.RemoveAll(name)
}
func (m *metricsFS) Rename(oldname, newname string) error {
defer m.account(metricOpRename)(-1)
return m.next.Rename(oldname, newname)
}
func (m *metricsFS) Stat(name string) (FileInfo, error) {
defer m.account(metricOpStat)(-1)
return m.next.Stat(name)
}
func (m *metricsFS) SymlinksSupported() bool {
defer m.account(metricOpSymlinksSupported)(-1)
return m.next.SymlinksSupported()
}
func (m *metricsFS) Walk(name string, walkFn WalkFunc) error {
defer m.account(metricOpWalk)(-1)
return m.next.Walk(name, walkFn)
}
func (m *metricsFS) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
defer m.account(metricOpWatch)(-1)
return m.next.Watch(path, ignore, ctx, ignorePerms)
}
func (m *metricsFS) Hide(name string) error {
defer m.account(metricOpHide)(-1)
return m.next.Hide(name)
}
func (m *metricsFS) Unhide(name string) error {
defer m.account(metricOpUnhide)(-1)
return m.next.Unhide(name)
}
func (m *metricsFS) Glob(pattern string) ([]string, error) {
defer m.account(metricOpGlob)(-1)
return m.next.Glob(pattern)
}
func (m *metricsFS) Roots() ([]string, error) {
defer m.account(metricOpRoots)(-1)
return m.next.Roots()
}
func (m *metricsFS) Usage(name string) (Usage, error) {
defer m.account(metricOpUsage)(-1)
return m.next.Usage(name)
}
func (m *metricsFS) Type() FilesystemType {
defer m.account(metricOpType)(-1)
return m.next.Type()
}
func (m *metricsFS) URI() string {
defer m.account(metricOpURI)(-1)
return m.next.URI()
}
func (m *metricsFS) Options() []Option {
defer m.account(metricOpOptions)(-1)
return m.next.Options()
}
func (m *metricsFS) SameFile(fi1, fi2 FileInfo) bool {
defer m.account(metricOpSameFile)(-1)
return m.next.SameFile(fi1, fi2)
}
func (m *metricsFS) PlatformData(name string, withOwnership, withXattrs bool, xattrFilter XattrFilter) (protocol.PlatformData, error) {
defer m.account(metricOpPlatformData)(-1)
return m.next.PlatformData(name, withOwnership, withXattrs, xattrFilter)
}
func (m *metricsFS) GetXattr(name string, xattrFilter XattrFilter) ([]protocol.Xattr, error) {
defer m.account(metricOpGetXattr)(-1)
return m.next.GetXattr(name, xattrFilter)
}
func (m *metricsFS) SetXattr(path string, xattrs []protocol.Xattr, xattrFilter XattrFilter) error {
defer m.account(metricOpSetXattr)(-1)
return m.next.SetXattr(path, xattrs, xattrFilter)
}
func (m *metricsFS) underlying() (Filesystem, bool) {
return m.next, true
}
func (m *metricsFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeMetrics
}
type metricsFile struct {
fs *metricsFS
next File
}
func (m *metricsFile) Read(p []byte) (n int, err error) {
acc := m.fs.account(metricOpRead)
defer func() { acc(n) }()
return m.next.Read(p)
}
func (m *metricsFile) ReadAt(p []byte, off int64) (n int, err error) {
acc := m.fs.account(metricOpReadAt)
defer func() { acc(n) }()
return m.next.ReadAt(p, off)
}
func (m *metricsFile) Seek(offset int64, whence int) (int64, error) {
defer m.fs.account(metricOpSeek)(-1)
return m.next.Seek(offset, whence)
}
func (m *metricsFile) Stat() (FileInfo, error) {
defer m.fs.account(metricOpStat)(-1)
return m.next.Stat()
}
func (m *metricsFile) Sync() error {
defer m.fs.account(metricOpSync)(-1)
return m.next.Sync()
}
func (m *metricsFile) Truncate(size int64) error {
defer m.fs.account(metricOpTruncate)(-1)
return m.next.Truncate(size)
}
func (m *metricsFile) Write(p []byte) (n int, err error) {
acc := m.fs.account(metricOpWrite)
defer func() { acc(n) }()
return m.next.Write(p)
}
func (m *metricsFile) WriteAt(p []byte, off int64) (n int, err error) {
acc := m.fs.account(metricOpWriteAt)
defer func() { acc(n) }()
return m.next.WriteAt(p, off)
}
func (m *metricsFile) Close() error {
defer m.fs.account(metricOpClose)(-1)
return m.next.Close()
}
func (m *metricsFile) Name() string {
defer m.fs.account(metricOpName)(-1)
return m.next.Name()
}
func (m *metricsFile) unwrap() File {
return m.next
}

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