Compare commits

...

39 Commits

Author SHA1 Message Date
Simon Frei
77578e8aac gui: Don't set default path editing existing folders without label (fixes #4297)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4298
LGTM: calmh
2017-08-10 15:31:25 +00:00
Jakob Borg
1fc2ab444b lib/model: Remove ineffective symlink recovery attempt 2017-08-08 15:30:28 +02:00
Jakob Borg
fa5c890ff6 lib/versioner: Clean the versions dir of symlinks, not the full folder
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4289
2017-08-08 13:13:08 +00:00
Jakob Borg
a3c17f8f81 lib/model: Disable symlink attack test on Windows 2017-08-08 08:05:24 +02:00
Jakob Borg
f1f21bf220 lib/model, lib/versioner: Prevent symlink attack via versioning (fixes #4286)
Prior to this, the following is possible:

- Create a symlink "foo -> /somewhere", it gets synced
- Delete "foo", it gets versioned
- Create "foo/bar", it gets synced
- Delete "foo/bar", it gets versioned in "/somewhere/bar"

With this change, versioners should never version symlinks.
2017-08-07 07:57:10 +02:00
MaximAL
54155cb42d gui: Add title attributes for shared devices/folders
Skip-check: authors

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4266
LGTM: AudriusButkevicius
2017-07-27 17:31:14 +00:00
Jakob Borg
414c58174b build: Move -installsuffix behind an explicit option (fixes #4272) 2017-07-27 12:55:07 +02:00
Audrius Butkevicius
94acc20dd6 cmd/strelaysrv: Fix a few connection and routine leaks (fixes #4245)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4273
2017-07-26 19:18:00 +00:00
NoLooseEnds
8e9119eedf assets: Add Mac folder icon
Skip-check: authors

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4238
2017-07-20 13:23:54 +00:00
Simon Frei
a04b92332f gui, lib/config: Add default path for new folders (fixes #2157)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4192
2017-07-20 13:16:54 +00:00
HairyFotr
0ad10b0fee all: Typos
Skip-check: authors

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4263
2017-07-20 13:10:46 +00:00
Ross Smith II
0ca2ed7ad7 build: Use maximum compression when archiving
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4229
2017-07-17 13:20:13 +00:00
Jakob Borg
fa4226cae4 authors: Add rasa 2017-07-17 15:06:47 +02:00
Jakob Borg
cc63236a2e build: Use Go's default GOPATH if it's valid
As of Go 1.8 it's valid to not have a GOPATH set. We ask the Go tool
what the GOPATH seems to be, and if we find ourselves (or a valid copy
of ourselves...) in that location we do not fiddle with the GOPATH.
2017-07-16 21:39:29 +01:00
Jakob Borg
6623657ef3 build: The default build step already includes linting 2017-07-15 17:06:22 +02:00
Jose Manuel Delicado
4405117bea gui: HTML accessibility updates
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4258
LGTM: calmh
2017-07-15 09:54:37 +00:00
Jakob Borg
e371800878 authors: Fixup jmdaweb 2017-07-14 07:53:18 +02:00
Jakob Borg
7a47646534 authors: Add jmdaweb 2017-07-14 07:49:08 +02:00
Jakob Borg
d475ad7ce1 gui, man: Update docs & translations 2017-07-13 08:55:12 +02:00
Jakob Borg
7c8418f493 build: Support builds outside of GOPATH
This adds support for building with the source placed anywhere and no
GOPATH set. The build script handles this by creating a temporary GOPATH
in the system temp dir (or another specified location) and mirroring the
source there before building. The resulting binaries etc still end up in
the same place as usual, meaning at least the "build", "install", "tar",
"zip", "deb", "snap", "test", "vet", "lint", "metalint" and "clean"
commands work without a GOPATH. To this end these commands internally
use fully qualified package paths like
"github.com/syncthing/syncthing/cmd/..." instead of "./cmd/..." like
before.

There is a new command "gopath" that prepares and echoes the directory
of the temporary GOPATH. This can be used to run other non-build go
commands:

export GOPATH=$(go run build.go gopath)  // GOPATH is now set
go test -v -race github.com/syncthing/syncthing/cmd/...

There is a new option "-no-build-gopath" that prevents the
check-and-copy step, instead assuming the temporary GOPATH is already
created and up to date. This is a performance optimization for build
servers running multiple builds commands in sequence:

go run build.go gopath // creates a temporary GOPATH
go run build.go -no-build-gopath -goos=... tar // reuses GOPATH
go run build.go -no-build-gopath -goos=... tar // reuses GOPATH

The temporary GOPATH is placed in the system temporary directory
(os.TempDir()) unless overridden by the STTMPDIR variable. It is named
after the hash of the current directory where build.go is run. The
reason for this is that the name should be unique to a source checkout
without risk for conflict, but still persistent between runs of
build.go.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4253
LGTM: AudriusButkevicius, imsodin
2017-07-11 07:57:58 +00:00
Jakob Borg
200a7fc844 meta: Move metadata checks into meta directory, make them tests
This moves a few things from script/ to a new directory meta/, and makes
them real Go tests. These are the authors, copyright, metalint and gofmt
checks. That means that they can now be run by

go test -v ./meta

and optionally filtered by the usual -run thing to go test. Also -short
will cut down on the metalint stuff and exclude the authors check (which
is slow because it runs git lots of times).

Mainly this makes everything easier on things like build servers where
we can now just run tests instead of do a bunch of scripting.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4252
2017-07-07 20:43:26 +00:00
Jakob Borg
5a38e0ba3f script: Trivial lint error in changelog.go 2017-07-07 21:56:12 +02:00
Jakob Borg
c77490c32d authors: Fixup author email mistakes 2017-07-07 21:42:50 +02:00
Simon Frei
b75c9f2bbb lib/ignores: Don't add text from includes to lines (fixes #4249)
Otherwise all the lines from includes will be shown in the web UI instead of
just the #include ... line. This problem was introduced in #3996.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4248
LGTM: calmh
2017-07-06 11:44:11 +00:00
Siyuan Liu
322bedbb04 gui: Show remaining bytes in remote device panel (fixes #4227)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4246
LGTM: AudriusButkevicius, calmh
2017-07-05 09:19:29 +00:00
Jakob Borg
487655b365 gui, man: Update docs & translations 2017-07-05 07:45:22 +02:00
Siyuan Liu
03c678a810 lib/versioner: Interpret versions path relative to folder path (fixes #4188)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4243
2017-07-03 14:50:51 +00:00
Jakob Borg
b79f8aceb8 authors: Fixup liusy182 2017-07-03 16:40:02 +02:00
Jakob Borg
92e8c4303a authors: Add liusy182 2017-07-03 16:33:41 +02:00
Jakob Borg
e735a3a25c build: Builds from "release" branch are not branch builds 2017-06-30 14:02:16 +02:00
Simon Frei
8d13e01342 gui: Fix discovered devices list (follow-up to #4186)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4239
2017-06-29 13:47:42 +00:00
Jakob Borg
34f8cc8620 gui: Rename assets/lang/README-FIRST to README for greater visibility
README is rendered in the GitHub web UI
2017-06-28 18:58:41 +02:00
Jakob Borg
add10c98fa lib/db: Fix test for Go 1.9's smarter time.Time values 2017-06-28 14:36:04 +02:00
Jakob Borg
6503326073 gui, man: Update docs & translations 2017-06-28 07:45:20 +02:00
Jakob Borg
db1dc9985a lib/upgrade: 0.x to 1.0 is a minor upgrade
This removes the special handling of minor versions as major when the
actual major is zero, and adds the special case that upgrades from 0.x
to 1.x are considered minor. 0.x to 2.x or 1.x to 2.x etc are still
considered major.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4226
2017-06-25 14:17:43 +00:00
Jakob Borg
3641c97667 gui, man: Update docs & translations 2017-06-21 07:45:20 +02:00
Jakob Borg
8f214fe4a9 lib/config: Ignored folders that are added to the config should not remain ignored (fixes #4219)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4220
2017-06-17 18:40:28 +00:00
Simon Frei
98cfc204ca gui: Add dropdown menu to choose discovered devices to device modal (fixes #4157)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4186
LGTM: AudriusButkevicius, calmh
2017-06-16 15:27:09 +00:00
Jakob Borg
d0061c172c gui, man: Update docs & translations 2017-06-14 07:45:21 +02:00
100 changed files with 1153 additions and 503 deletions

View File

@@ -62,15 +62,17 @@ Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
Jochen Voss (seehuhn) <voss@seehuhn.de>
Johan Vromans (sciurius) <jvromans@squirrel.nl>
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
Kevin Allen (ironmig) <kma1660@gmail.com>
Kevin White, Jr. (kwhite17) <kevinwhite1710@gmail.com>
Kurt Fitzner (Kudalufi) <kurt@va1der.ca>
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
Leo Arias (elopio) <yo@elopio.net>
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
Lode Hoste (Zillode) <zillode@zillode.be>
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
@@ -93,6 +95,7 @@ Phill Luby (pluby) <phill.luby@newredo.com>
Piotr Bejda (piobpl) <piotrb10@gmail.com>
Robert Carosi (nov1n) <robert@carosi.nl>
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
Ross Smith II (rasa) <ross@smithii.com>
Ryan Sullivan (KayoticSully) <kayoticsully@gmail.com>
Sacheendra Talluri (sacheendra) <sacheendra.t@gmail.com>
Scott Klupfel (kluppy) <kluppy@going2blue.com>
@@ -112,6 +115,6 @@ Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
Victor Buinsky (buinsky) <vix_booja@tut.by>
Vil Brekin (Vilbrekin) <vilbrekin@gmail.com>
William A. Kennington III (wkennington) <william@wkennington.com>
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de>
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de> <wulf@weich-kr.de>
Xavier O. (damajor) <damajor@gmail.com>
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>

7
NICKS
View File

@@ -57,6 +57,8 @@ jayachithra <s.k.jayachithra@gmail.com>
jedie <github.com@jensdiemer.de>
jedie <git@jensdiemer.de>
jgke <jgke@jgke.fi>
jmdaweb <jmdaweb@hotmail.com>
jmdaweb <jmdaweb@users.noreply.github.com>
jpjp <jamespatterson@operamail.com>
jpjp <jpjp@users.noreply.github.com>
kamadak <kamada@nanohz.org>
@@ -70,9 +72,12 @@ kralo <max.schulze@online.de>
kralo <kralo@users.noreply.github.com>
krozycki <rozycki.karol@gmail.com>
Kudalufi <kurt@va1der.ca>
Kudalufi <kurt.fitzner@gmail.com>
kwhite17 <kevinwhite1710@gmail.com>
letiemble <laurent.etiemble@gmail.com>
letiemble <laurent.etiemble@monobjc.net>
liusy182 <liusy182@gmail.com>
liusy182 <liusy182@hotmail.com>
lkwg82 <lkwg82@gmx.de>
LordLandon <lordlandon@gmail.com>
majedev <majed.alhajry@gmail.com>
@@ -106,6 +111,7 @@ ProactiveServices <ProactiveServices@users.noreply.github.com>
pyfisch <pyfisch@gmail.com>
qbit <qbit@deftly.net>
ralder <ralder@yandex.ru>
rasa <ross@smithii.com>
Rewt0r <rewt0r@gmx.com>
Rewt0r <Rewt0r@users.noreply.github.com>
rumpelsepp <stefan@sevenbyte.org>
@@ -136,6 +142,7 @@ wkennington <william@wkennington.com>
WSGCSysadmin <e.meitner@willystreet.coop>
wweich <wweich@users.noreply.github.com>
wweich <wweich@gmx.de>
wweich <wulf@weich-kr.de>
xduugu <cedric@gmx.ca>
zaynetro <romanznet@gmail.com>
Zillode <zillode@zillode.be>

View File

@@ -100,7 +100,7 @@ All code is licensed under the [MPLv2 License][7].
[1]: https://docs.syncthing.net/specs/bep-v1.html
[2]: https://docs.syncthing.net/intro/getting-started.html
[3]: https://github.com/syncthing/syncthing/blob/master/etc
[4]: http://www.freenode.net/
[4]: https://www.freenode.net/
[5]: https://docs.syncthing.net/dev/building.html
[6]: https://docs.syncthing.net/
[7]: https://github.com/syncthing/syncthing/blob/master/LICENSE

View File

Binary file not shown.

358
build.go
View File

@@ -12,7 +12,9 @@ import (
"archive/tar"
"archive/zip"
"bytes"
"compress/flate"
"compress/gzip"
"crypto/sha256"
"errors"
"flag"
"fmt"
@@ -32,14 +34,17 @@ import (
)
var (
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
goos string
noupgrade bool
version string
goVersion float64
race bool
debug = os.Getenv("BUILDDEBUG") != ""
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
goos string
noupgrade bool
version string
goVersion float64
race bool
debug = os.Getenv("BUILDDEBUG") != ""
noBuildGopath bool
extraTags string
installSuffix string
)
type target struct {
@@ -65,7 +70,7 @@ var targets = map[string]target{
"all": {
// Only valid for the "build" and "install" commands as it lacks all
// the archive creation stuff.
buildPkg: "./cmd/...",
buildPkg: "github.com/syncthing/syncthing/cmd/...",
tags: []string{"purego"},
},
"syncthing": {
@@ -75,7 +80,7 @@ var targets = map[string]target{
debdeps: []string{"libc6", "procps"},
debpost: "script/post-upgrade",
description: "Open Source Continuous File Synchronization",
buildPkg: "./cmd/syncthing",
buildPkg: "github.com/syncthing/syncthing/cmd/syncthing",
binaryName: "syncthing", // .exe will be added automatically for Windows builds
archiveFiles: []archiveFile{
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
@@ -110,7 +115,7 @@ var targets = map[string]target{
debname: "syncthing-discosrv",
debdeps: []string{"libc6"},
description: "Syncthing Discovery Server",
buildPkg: "./cmd/stdiscosrv",
buildPkg: "github.com/syncthing/syncthing/cmd/stdiscosrv",
binaryName: "stdiscosrv", // .exe will be added automatically for Windows builds
archiveFiles: []archiveFile{
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
@@ -132,7 +137,7 @@ var targets = map[string]target{
debname: "syncthing-relaysrv",
debdeps: []string{"libc6"},
description: "Syncthing Relay Server",
buildPkg: "./cmd/strelaysrv",
buildPkg: "github.com/syncthing/syncthing/cmd/strelaysrv",
binaryName: "strelaysrv", // .exe will be added automatically for Windows builds
archiveFiles: []archiveFile{
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
@@ -153,7 +158,7 @@ var targets = map[string]target{
debname: "syncthing-relaypoolsrv",
debdeps: []string{"libc6"},
description: "Syncthing Relay Pool Server",
buildPkg: "./cmd/strelaypoolsrv",
buildPkg: "github.com/syncthing/syncthing/cmd/strelaypoolsrv",
binaryName: "strelaypoolsrv", // .exe will be added automatically for Windows builds
archiveFiles: []archiveFile{
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
@@ -170,41 +175,6 @@ var targets = map[string]target{
},
}
var (
// fast linters complete in a fraction of a second and might as well be
// run always as part of the build
fastLinters = []string{
"deadcode",
"golint",
"ineffassign",
"vet",
}
// slow linters take several seconds and are run only as part of the
// "metalint" command.
slowLinters = []string{
"gosimple",
"staticcheck",
"structcheck",
"unused",
"varcheck",
}
// Which parts of the tree to lint
lintDirs = []string{".", "./lib/...", "./cmd/..."}
// Messages to ignore
lintExcludes = []string{
".pb.go",
"should have comment",
"protocol.Vector composite literal uses unkeyed fields",
"cli.Requires composite literal uses unkeyed fields",
"Use DialContext instead", // Go 1.7
"os.SEEK_SET is deprecated", // Go 1.7
"SA4017", // staticcheck "is a pure function but its return value is ignored"
}
)
func init() {
// The "syncthing" target includes a few more files found in the "etc"
// and "extra" dirs.
@@ -222,9 +192,10 @@ func init() {
}
func main() {
log.SetOutput(os.Stdout)
log.SetFlags(0)
parseFlags()
if debug {
t0 := time.Now()
defer func() {
@@ -232,16 +203,25 @@ func main() {
}()
}
if os.Getenv("GOPATH") == "" {
setGoPath()
if gopath() == "" {
gopath, err := temporaryBuildDir()
if err != nil {
log.Fatal(err)
}
if !noBuildGopath {
lazyRebuildAssets()
if err := buildGOPATH(gopath); err != nil {
log.Fatal(err)
}
}
os.Setenv("GOPATH", gopath)
log.Println("GOPATH is", gopath)
}
// Set path to $GOPATH/bin:$PATH so that we can for sure find tools we
// might have installed during "build.go setup".
os.Setenv("PATH", fmt.Sprintf("%s%cbin%c%s", os.Getenv("GOPATH"), os.PathSeparator, os.PathListSeparator, os.Getenv("PATH")))
parseFlags()
checkArchitecture()
// Invoking build.go with no parameters at all builds everything (incrementally),
@@ -284,22 +264,23 @@ func runCommand(cmd string, target target) {
if noupgrade {
tags = []string{"noupgrade"}
}
tags = append(tags, strings.Fields(extraTags)...)
install(target, tags)
metalint(fastLinters, lintDirs)
metalintShort()
case "build":
var tags []string
if noupgrade {
tags = []string{"noupgrade"}
}
tags = append(tags, strings.Fields(extraTags)...)
build(target, tags)
metalint(fastLinters, lintDirs)
case "test":
test("./lib/...", "./cmd/...")
test("github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
case "bench":
bench("./lib/...", "./cmd/...")
bench("github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
case "assets":
rebuildAssets()
@@ -329,41 +310,38 @@ func runCommand(cmd string, target target) {
clean()
case "vet":
metalint(fastLinters, lintDirs)
metalintShort()
case "lint":
metalint(fastLinters, lintDirs)
metalintShort()
case "metalint":
metalint(fastLinters, lintDirs)
metalint(slowLinters, lintDirs)
metalint()
case "version":
fmt.Println(getVersion())
case "gopath":
gopath, err := temporaryBuildDir()
if err != nil {
log.Fatal(err)
}
fmt.Println(gopath)
default:
log.Fatalf("Unknown command %q", cmd)
}
}
// setGoPath sets GOPATH correctly with the assumption that we are
// in $GOPATH/src/github.com/syncthing/syncthing.
func setGoPath() {
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
gopath := filepath.Clean(filepath.Join(cwd, "../../../../"))
log.Println("GOPATH is", gopath)
os.Setenv("GOPATH", gopath)
}
func parseFlags() {
flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
flag.BoolVar(&noupgrade, "no-upgrade", noupgrade, "Disable upgrade functionality")
flag.StringVar(&version, "version", getVersion(), "Set compiled in version string")
flag.BoolVar(&race, "race", race, "Use race detector")
flag.BoolVar(&noBuildGopath, "no-build-gopath", noBuildGopath, "Don't build GOPATH, assume it's OK")
flag.StringVar(&extraTags, "tags", extraTags, "Extra tags, space separated")
flag.StringVar(&installSuffix, "installsuffix", installSuffix, "Install suffix, optional")
flag.Parse()
}
@@ -390,7 +368,7 @@ func setup() {
runPrint("go", "get", "-u", pkg)
}
runPrint("go", "install", "-v", "./vendor/github.com/gogo/protobuf/protoc-gen-gogofast")
runPrint("go", "install", "-v", "github.com/syncthing/syncthing/vendor/github.com/gogo/protobuf/protoc-gen-gogofast")
}
func test(pkgs ...string) {
@@ -429,6 +407,9 @@ func install(target target, tags []string) {
if len(tags) > 0 {
args = append(args, "-tags", strings.Join(tags, " "))
}
if installSuffix != "" {
args = append(args, "-installsuffix", installSuffix)
}
if race {
args = append(args, "-race")
}
@@ -444,11 +425,14 @@ func build(target target, tags []string) {
tags = append(target.tags, tags...)
rmr(target.binaryName)
rmr(target.BinaryName())
args := []string{"build", "-i", "-v", "-ldflags", ldflags()}
if len(tags) > 0 {
args = append(args, "-tags", strings.Join(tags, " "))
}
if installSuffix != "" {
args = append(args, "-installsuffix", installSuffix)
}
if race {
args = append(args, "-race")
}
@@ -472,22 +456,20 @@ func buildTar(target target) {
build(target, tags)
if goos == "darwin" {
macosCodesign(target.binaryName)
macosCodesign(target.BinaryName())
}
for i := range target.archiveFiles {
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.binaryName, 1)
target.archiveFiles[i].dst = strings.Replace(target.archiveFiles[i].dst, "{{binary}}", target.binaryName, 1)
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.BinaryName(), 1)
target.archiveFiles[i].dst = strings.Replace(target.archiveFiles[i].dst, "{{binary}}", target.BinaryName(), 1)
target.archiveFiles[i].dst = name + "/" + target.archiveFiles[i].dst
}
tarGz(filename, target.archiveFiles)
log.Println(filename)
fmt.Println(filename)
}
func buildZip(target target) {
target.binaryName += ".exe"
name := archiveName(target)
filename := name + ".zip"
@@ -500,13 +482,13 @@ func buildZip(target target) {
build(target, tags)
for i := range target.archiveFiles {
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.binaryName, 1)
target.archiveFiles[i].dst = strings.Replace(target.archiveFiles[i].dst, "{{binary}}", target.binaryName, 1)
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.BinaryName(), 1)
target.archiveFiles[i].dst = strings.Replace(target.archiveFiles[i].dst, "{{binary}}", target.BinaryName(), 1)
target.archiveFiles[i].dst = name + "/" + target.archiveFiles[i].dst
}
zipFile(filename, target.archiveFiles)
log.Println(filename)
fmt.Println(filename)
}
func buildDeb(target target) {
@@ -526,8 +508,8 @@ func buildDeb(target target) {
build(target, []string{"noupgrade"})
for i := range target.installationFiles {
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.binaryName, 1)
target.installationFiles[i].dst = strings.Replace(target.installationFiles[i].dst, "{{binary}}", target.binaryName, 1)
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
target.installationFiles[i].dst = strings.Replace(target.installationFiles[i].dst, "{{binary}}", target.BinaryName(), 1)
}
for _, af := range target.installationFiles {
@@ -605,21 +587,36 @@ func buildSnap(target target) {
runPrint("snapcraft")
}
// copyFile copies a file from src to dst, ensuring the containing directory
// exists. The permission bits are copied as well. If dst already exists and
// the contents are identical to src the modification time is not updated.
func copyFile(src, dst string, perm os.FileMode) error {
dstDir := filepath.Dir(dst)
os.MkdirAll(dstDir, 0755) // ignore error
srcFd, err := os.Open(src)
in, err := ioutil.ReadFile(src)
if err != nil {
return err
}
defer srcFd.Close()
dstFd, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm)
out, err := ioutil.ReadFile(dst)
if err != nil {
// The destination probably doesn't exist, we should create
// it.
goto copy
}
if bytes.Equal(in, out) {
// The permission bits may have changed without the contents
// changing so we always mirror them.
os.Chmod(dst, perm)
return nil
}
copy:
os.MkdirAll(filepath.Dir(dst), 0777)
if err := ioutil.WriteFile(dst, in, perm); err != nil {
return err
}
defer dstFd.Close()
_, err = io.Copy(dstFd, srcFd)
return err
return nil
}
func listFiles(dir string) []string {
@@ -674,7 +671,7 @@ func shouldRebuildAssets(target, srcdir string) bool {
}
func proto() {
runPrint("go", "generate", "./lib/...")
runPrint("go", "generate", "github.com/syncthing/syncthing/lib/...")
}
func translate() {
@@ -801,8 +798,9 @@ func getBranchSuffix() string {
}
branch = parts[len(parts)-1]
if branch == "master" {
// master builds are the default.
switch branch {
case "master", "release":
// these are not special
return ""
}
@@ -926,7 +924,10 @@ func tarGz(out string, files []archiveFile) {
log.Fatal(err)
}
gw := gzip.NewWriter(fd)
gw, err := gzip.NewWriterLevel(fd, gzip.BestCompression)
if err != nil {
log.Fatal(err)
}
tw := tar.NewWriter(gw)
for _, f := range files {
@@ -979,6 +980,21 @@ func zipFile(out string, files []archiveFile) {
zw := zip.NewWriter(fd)
var fw *flate.Writer
// Register the deflator.
zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
var err error
if fw == nil {
// Creating a flate compressor for every file is
// expensive, create one and reuse it.
fw, err = flate.NewWriter(out, flate.BestCompression)
} else {
fw.Reset(out)
}
return fw, err
})
for _, f := range files {
sf, err := os.Open(f.src)
if err != nil {
@@ -1054,59 +1070,129 @@ func macosCodesign(file string) {
}
}
func metalint(linters []string, dirs []string) {
ok := true
if isGometalinterInstalled() {
if !gometalinter(linters, dirs, lintExcludes...) {
ok = false
}
}
if !ok {
log.Fatal("Build succeeded, but there were lint warnings")
}
func metalint() {
lazyRebuildAssets()
runPrint("go", "test", "-run", "Metalint", "./meta")
}
func isGometalinterInstalled() bool {
if _, err := runError("gometalinter", "--disable-all"); err != nil {
log.Println("gometalinter is not installed")
return false
}
return true
func metalintShort() {
lazyRebuildAssets()
runPrint("go", "test", "-short", "-run", "Metalint", "./meta")
}
func gometalinter(linters []string, dirs []string, excludes ...string) bool {
params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
func temporaryBuildDir() (string, error) {
// The base of our temp dir is "syncthing-xxxxxxxx" where the x:es
// are eight bytes from the sha256 of our working directory. We do
// this because we want a name in the global temp dir that doesn't
// conflict with someone else building syncthing on the same
// machine, yet is persistent between runs from the same source
// directory.
wd, err := os.Getwd()
if err != nil {
return "", err
}
hash := sha256.Sum256([]byte(wd))
base := fmt.Sprintf("syncthing-%x", hash[:4])
for _, linter := range linters {
params = append(params, "--enable="+linter)
// The temp dir is taken from $STTMPDIR if set, otherwise the system
// default (potentially infrluenced by $TMPDIR on unixes).
var tmpDir string
if t := os.Getenv("STTMPDIR"); t != "" {
tmpDir = t
} else {
tmpDir = os.TempDir()
}
for _, exclude := range excludes {
params = append(params, "--exclude="+exclude)
return filepath.Join(tmpDir, base), nil
}
func buildGOPATH(gopath string) error {
pkg := filepath.Join(gopath, "src/github.com/syncthing/syncthing")
dirs := []string{"cmd", "lib", "meta", "script", "test", "vendor"}
if debug {
t0 := time.Now()
log.Println("build temporary GOPATH in", gopath)
defer func() {
log.Println("... in", time.Since(t0))
}()
}
// Walk the sources and copy the files into the temporary GOPATH.
// Remember which files are supposed to be present so we can clean
// out everything else in the next step. The copyFile() step will
// only actually copy the file if it doesn't exist or the contents
// differ.
exists := map[string]struct{}{}
for _, dir := range dirs {
params = append(params, dir)
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
dst := filepath.Join(pkg, path)
exists[dst] = struct{}{}
if err := copyFile(path, dst, info.Mode()); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
bs, _ := runError("gometalinter", params...)
// Walk the temporary GOPATH and remove any files that we wouldn't
// have copied there in the previous step.
nerr := 0
lines := make(map[string]struct{})
for _, line := range strings.Split(string(bs), "\n") {
if line == "" {
continue
filepath.Walk(pkg, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if _, ok := lines[line]; ok {
continue
if info.IsDir() {
return nil
}
log.Println(line)
if strings.Contains(line, "executable file not found") {
log.Println(` - Try "go run build.go setup" to install missing tools`)
if _, ok := exists[path]; !ok {
os.Remove(path)
}
lines[line] = struct{}{}
nerr++
}
return nil
})
return nerr == 0
return nil
}
func gopath() string {
if gopath := os.Getenv("GOPATH"); gopath != "" {
// The env var is set, use that.
return gopath
}
// Ask Go what it thinks.
bs, err := runError("go", "env", "GOPATH")
if err != nil {
return ""
}
// We got something. Check if we are in fact available in that location.
gopath := string(bs)
if _, err := os.Stat(filepath.Join(gopath, "src/github.com/syncthing/syncthing/build.go")); err == nil {
// That seems to be the gopath.
return gopath
}
// The gopath is not valid.
return ""
}
func (t target) BinaryName() string {
if goos == "windows" {
return t.binaryName + ".exe"
}
return t.binaryName
}

View File

@@ -2,15 +2,15 @@
<html lang="en" ng-app="syncthing" ng-controller="relayDataController">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content=""/>
<meta name="author" content=""/>
<title>Relay stats</title>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css"/>
<style>
#map {
@@ -36,9 +36,9 @@
<body class="ng-cloak">
<div class="container">
<h1>Relay Pool Data</h2>
<h1>Relay Pool Data</h1>
<div ng-if="relays === undefined" class="text-center">
<img src="//cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif"/>
<img src="//cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif" alt=""/>
<p>Please wait while we gather data</p>
</div>
<div>
@@ -184,10 +184,10 @@
</div>
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="//maps.googleapis.com/maps/api/js"></script>
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script type="text/javascript" src="//maps.googleapis.com/maps/api/js"></script>
</body>
<script>
@@ -395,12 +395,12 @@
<span ng-if="relay.status.options['global-rate'] != undefined">
<span ng-if="relay.status.options['global-rate'] > 0">Global rate limit: {{ relay.status.options['global-rate'] | bytes }}/s</span>
<span ng-if="relay.status.options['global-rate'] == 0">Global rate limit: unlimited</span>
</br>
<br/>
</span>
<span ng-if="relay.status.options['per-session-rate'] != undefined">
<span ng-if="relay.status.options['per-session-rate'] > 0">Session rate limit: {{ relay.status.options['per-session-rate'] | bytes }}/s</span>
<span ng-if="relay.status.options['per-session-rate'] == 0">Session rate limit: unlimited</span>
</br>
<br/>
</span>
</div>
<div ng-if="!relay.status">

View File

@@ -59,6 +59,13 @@ func listener(proto, addr string, config *tls.Config) {
func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config) {
conn := tls.Server(tcpConn, config)
if err := conn.SetDeadline(time.Now().Add(messageTimeout)); err != nil {
if debug {
log.Println("Weird error setting deadline:", err, "on", conn.RemoteAddr())
}
conn.Close()
return
}
err := conn.Handshake()
if err != nil {
if debug {
@@ -81,6 +88,7 @@ func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config) {
conn.Close()
return
}
conn.SetDeadline(time.Time{})
id := syncthingprotocol.NewDeviceID(certs[0].Raw)
@@ -277,6 +285,7 @@ func sessionConnectionHandler(conn net.Conn) {
if debug {
log.Println("Weird error setting deadline:", err, "on", conn.RemoteAddr())
}
conn.Close()
return
}

View File

@@ -16,8 +16,16 @@ var rc *rateCalculator
func statusService(addr string) {
rc = newRateCalculator(360, 10*time.Second, &bytesProxied)
http.HandleFunc("/status", getStatus)
if err := http.ListenAndServe(addr, nil); err != nil {
handler := http.NewServeMux()
handler.HandleFunc("/status", getStatus)
srv := http.Server{
Addr: addr,
Handler: handler,
ReadTimeout: 15 * time.Second,
}
srv.SetKeepAlivesEnabled(false)
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}

View File

@@ -943,7 +943,7 @@ func TestEventMasks(t *testing.T) {
}
expected = 0
if mask := svc.getEventMask("WeirdEvent,something else that doens't exist"); mask != expected {
if mask := svc.getEventMask("WeirdEvent,something else that doesn't exist"); mask != expected {
t.Errorf("incorrect parsed mask %x != %x", int64(mask), int64(expected))
}

View File

@@ -1074,7 +1074,7 @@ func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Mode
if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
// Can potentially block if the utility we are invoking doesn't
// fork, and just execs, hence keep it in it's own routine.
// fork, and just execs, hence keep it in its own routine.
<-api.startedOnce
go openURL(guiCfg.URL())
}

View File

@@ -5,7 +5,7 @@ the "Upstart" service manager on Linux. To have syncthing start when you login
place "user/syncthing.conf" in the "/home/[username]/.config/upstart/" folder.
To have syncthing start when the system boots place "system/syncthing.conf"
in the "/etc/init/" folder.
To manualy start syncthing via Upstart when using the system configuration use:
To manually start syncthing via Upstart when using the system configuration use:
```
sudo initctl start syncthing

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когато добавяш ново устройство помни, че твоето устройство също трябва да бъде добавено от другата страна.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когато добавяш нов идентификатор на папка помни, че той се използва за свързване на папките на различни устройства. Главни/малки букви са от значение и трябва да са еднакви на всички устройства.",
"Yes": "Да",
"You can also select one of these nearby devices:": "Също така може да изберете едно от следните устройтва намиращи се наблизо:",
"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 must keep at least one version.": "Трябва да пазиш поне една версия.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quant s'afig un nou dispositiu, hi ha que tindre en compte que aquest dispositiu deu ser afegit també en l'altre costat.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Quant s'afig una nova carpeta, hi ha que tindre en compte que l'ID de la carpeta s'utilitza per a juntar les carpetes entre dispositius. Són sensibles a les majúscules i deuen coincidir exactament entre tots els dispositius.",
"Yes": "Sí",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Pots canviar la teua elecció en qualsevol moment en el dialog Ajustos",
"You can read more about the two release channels at the link below.": "Pots llegir més sobre els dos canals de versions en l'enllaç de baix.",
"You must keep at least one version.": "Es deu mantindre al menys una versió.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Při přidávání nového přístroje mějte na paměti, že je ho třeba také zadat na druhé straně.",
"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.": "Při přidávání nového adresáře mějte na paměti, že jeho ID je použito ke svázání adresářů napříč přístoji. Rozlišují se malá a velká písmena a musí přesně souhlasit mezi všemi přístroji.",
"Yes": "Ano",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Vaši volbu můžete kdykoliv změnit v dialogu nastavení.",
"You can read more about the two release channels at the link below.": "O kandidátech na vydání si můžete přečíst více v odkazu níže.",
"You must keep at least one version.": "Je třeba ponechat alespoň jednu verzi.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Når der tilføjes en ny enhed, vær da opmærksom på, at denne enhed også skal tilføjes på den anden side.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Når der tilføjes en ny enhed, vær da opmærksom på at samme ID bruges til at forbinde mapperne på de forskellige enheder. Der er forskel på store og små bogstaver, og ID skal være fuldstændig identisk på alle enheder.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Du kan altid ændre dit valg under indstillinger.",
"You can read more about the two release channels at the link below.": "Du kan læse mere om de to udgivelseskanaler på links herunder.",
"You must keep at least one version.": "Du skal beholde mindst én version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Beachte beim Hinzufügen eines neuen Gerätes, dass dieses Gerät auch auf den anderen Geräten hinzugefügt werden muss.",
"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.": "Beachte bitte beim Hinzufügen eines neuen Ordners, dass die Ordnerkennung dazu verwendet wird, Ordner zwischen Geräten zu verbinden. Die Kennung muss also auf allen Geräten gleich sein, die Groß- und Kleinschreibung muss dabei beachtet werden.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Sie können Ihre Wahl jederzeit in den Einstellungen ändern.",
"You can read more about the two release channels at the link below.": "Über den untenstehenden Link können Sie mehr über die zwei Veröffentlichungskanäle erfahren.",
"You must keep at least one version.": "Du musst mindestens eine Version behalten.",

View File

@@ -77,7 +77,7 @@
"Failed Items": "Αρχεία που απέτυχαν",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.",
"File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία",
"File Versioning": "Τήρηση εκδόσεων",
"File Versioning": "Τήρηση εκδόσεων αρχείων",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Τα δικαιώματα των αρχείων θα αγνοούνται όταν κοιτάζω για αλλαγές. Αφορά συστήματα αρχείων FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Τα αρχεία που σβήνονται ή αντικαθίστανται από το Syncthing μετακινούνται στον κατάλογο .stversions.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Τα αρχεία που σβήνονται ή αντικαθιστούνται από το Syncthing μετακινούνται σε έναν φάκελο .stversions με χρονοσφραγίδα.",
@@ -237,7 +237,7 @@
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Η μέγιστη ηλικία παλιότερων εκδόσεων (σε ημέρες, αν δώσεις 0 οι παλιότερες εκδόσεις θα διατηρούνται για πάντα).",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Το ποσοστό του ελάχιστου διαθέσιμου αποθηκευτικόυ χώρου πρέπει να είναι έναν μη-αρνητικός αριθμός μεταξύ του 0 και του 100 (συμπεριλαμβανομένων)",
"The number of days must be a number and cannot be blank.": "Ο αριθμός ημερών πρέπει να είναι αριθμός και σίγουρα όχι κενό.",
"The number of days to keep files in the trash can. Zero means forever.": "Ο αριθμός ημερών που θα διατηρούντα τα αρχεία στον κάδο. Μηδέν σημαίνει διατήρηση για πάντα.",
"The number of days to keep files in the trash can. Zero means forever.": "Ο αριθμός ημερών που θα διατηρούνται τα αρχεία στον κάδο. Μηδέν σημαίνει διατήρηση για πάντα.",
"The number of old versions to keep, per file.": "Πόσες παλιότερες εκδόσεις θα διατηρούνται, ανά αρχείο.",
"The number of versions must be a number and cannot be blank.": "Ο αριθμός εκδόσεων πρέπει να είναι αριθμός και σίγουρα όχι κενό.",
"The path cannot be blank.": "Το μονοπάτι δεν μπορεί να είναι κενό.",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Θυμήσου πως όταν προσθέτεις μια νέα συσκευή, ετούτη η συσκευή θα πρέπει να προστεθεί και στην άλλη πλευρά.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Όταν προσθέτεις έναν νέο φάκελο, θυμήσου πως η ταυτότητα ενός φακέλου χρησιμοποιείται για να να συσχετίσει φακέλους μεταξύ συσκευών. Η ταυτότητα του φακέλου θα πρέπει να είναι η ίδια σε όλες τις συσκευές και έχουν σημασία τα πεζά ή κεφαλαία γράμματα.",
"Yes": "Ναι",
"You can also select one of these nearby devices:": "Μπορείτε επίσης να επιλέξετε μια από αυτές τις κοντινές συσκευές:",
"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 must keep at least one version.": "Πρέπει να τηρήσεις τουλάχιστον μια έκδοση.",

View File

@@ -167,7 +167,7 @@
"Random": "Random",
"Reduced by ignore patterns": "Reduced by ignore patterns",
"Release Notes": "Release Notes",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Release candidates contain the latest features and fixes. They are similar to the traditional fortnightly Syncthing releases.",
"Remote Devices": "Remote Devices",
"Remove": "Remove",
"Required identifier for the folder. Must be the same on all cluster devices.": "Required identifier for the folder. Must be the same on all cluster devices.",
@@ -273,7 +273,8 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Yes",
"You can change your choice at any time in the Settings dialog.": "You can change your choice at any time in the Settings dialog.",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "You can change your choice at any time in the Settings dialogue.",
"You can read more about the two release channels at the link below.": "You can read more about the two release channels at the link below.",
"You must keep at least one version.": "You must keep at least one version.",
"days": "days",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Yes",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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 can read more about the two release channels at the link below.",
"You must keep at least one version.": "You must keep at least one version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Dum la aldonado de nova aparato, memoru ke ĉi tiu aparato devas esti aldonita en la alia flanko ankaŭ.",
"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.": "Dum la aldonado de nova dosierujo, memoru ke la Dosieruja ID estas uzita por ligi la dosierujojn kune inter aparatoj. Ili estas literfakodistingaj kaj devas kongrui precize inter ĉiuj aparatoj.",
"Yes": "Jes",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Vi povas ŝanĝi vian elekton iam ajn en la Agorda dialogo.",
"You can read more about the two release channels at the link below.": "Vi povas legi plu pri la du eldonkanaloj per la malsupra ligilo.",
"You must keep at least one version.": "Vi devas konservi almenaŭ unu version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Cuando añada un nuevo dispositivo, tenga en cuenta que este debe añadirse también en el otro lado.",
"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.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
"Yes": "Si",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Puedes cambiar tu elección en cualquier momento en el panel de Ajustes.",
"You can read more about the two release channels at the link below.": "Puedes leer más sobre los dos método de publicación de versiones en el siguiente enlace.",
"You must keep at least one version.": "Debes mantener al menos una versión.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Cuando añada un nuevo dispositivo, tenga en cuenta que este debe añadirse también en el otro lado.",
"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.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
"Yes": "Si",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Puedes cambiar tu elección en cualquier momento en el panel de Ajustes.",
"You can read more about the two release channels at the link below.": "Puedes leer más sobre los dos método de publicación de versiones en el siguiente enlace.",
"You must keep at least one version.": "Debes mantener al menos una versión.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Tresna bat gehitzen duzularik, gogoan atxik ezazu zurea bestaldean gehitu behar dela ere",
"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.": "Partekatze bat gehitzen delarik, gogoan atxik ezazu bere IDa erabilia dela errepertorioak lotzeko tresnen bitartez. ID-a hautskorra da eta partekatze hontan parte hartzen duten tresna guzietan berdina izan behar du.",
"Yes": "Bai",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Zure hautua aldatzen ahal duzu \"Konfigurazio\" leihatilan",
"You can read more about the two release channels at the link below.": "Bi banaketa kanal hauen bidez gehiago jakin dezakezu, lokarri honen bidez ",
"You must keep at least one version.": "Bertsio bat bederen behar duzu atxiki",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lisättäessä laitetta, muista että tämä laite tulee myös lisätä toiseen laitteeseen.",
"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.": "Lisättäessä uutta kansiota, muista että kansion ID:tä käytetään solmimaan kansiot yhteen laitteiden välillä. Ne ovat riippuvaisia kirjankoosta ja niiden tulee täsmätä kaikkien laitteiden välillä.",
"Yes": "Kyllä",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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 can read more about the two release channels at the link below.",
"You must keep at least one version.": "Sinun tulee säilyttää ainakin yksi versio.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lorsque vous ajoutez un appareil, gardez à l'esprit que le votre doit aussi être ajouté de l'autre coté.",
"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.": "Lorsqu'un nouveau partage est ajouté, gardez à l'esprit que son ID est utilisée pour lier les répertoires à travers les appareils. L'ID est sensible à la casse et sera forcément la même sur tous les appareils participant à ce partage.",
"Yes": "Oui",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Vous pouvez changer votre choix dans la boîte de dialogue \"Configuration\".",
"You can read more about the two release channels at the link below.": "Vous pouvez en savoir plus sur les deux canaux de distribution via le lien ci-dessous.",
"You must keep at least one version.": "Vous devez garder au minimum une version.",

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "L'appareil portant cette ID est déjà présent.",
"A device with that ID is already added.": "L'appareil portant cet ID est déjà présent.",
"A negative number of days doesn't make sense.": "Ce champ n'accepte qu'un entier positif ou nul.",
"A new major version may not be compatible with previous versions.": "Une nouvelle version majeure peut présenter des incompatibilités avec les versions antérieures.",
"API Key": "Clé API",
@@ -48,7 +48,7 @@
"Danger!": "Attention !",
"Deleted": "Supprimé",
"Device": "Appareil",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "L'appareil \"{{name}}\" ({{device}} à {{address}}) veut se connecter. L'acceptez-vous ?",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" ({{device}}), actuellement à {{address}}, demande à se connecter.\nAcceptez-vous de l'ajouter à votre liste d'appareils connus ?",
"Device ID": "ID de l'appareil",
"Device Identification": "Identifiant de l'appareil",
"Device Name": "Nom de l'appareil",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lorsque vous ajoutez un appareil, gardez à l'esprit que le votre doit aussi être ajouté de l'autre coté.",
"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.": "Lorsqu'un nouveau partage est ajouté, gardez à l'esprit que son ID est utilisée pour lier les répertoires à travers les appareils. L'ID est sensible à la casse et sera forcément la même sur tous les appareils participant à ce partage.",
"Yes": "Oui",
"You can also select one of these nearby devices:": "Vous pouvez également sélectionner l'un de ces appareils proches :",
"You can change your choice at any time in the Settings dialog.": "Vous pouvez changer votre choix dans la boîte de dialogue \"Configuration\".",
"You can read more about the two release channels at the link below.": "Vous pouvez en savoir plus sur les deux canaux de distribution via le lien ci-dessous.",
"You must keep at least one version.": "Vous devez garder au minimum une version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Hâld by it taheakjen fan in nij apparaat yn de holle dat it apparaat oan de oare kant ek taheakke wurde moat. ",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Hâld by it taheakjen fan in nije map yn de holle dat de map-ID brûkt wurd om de mappen tusken apparaten mei-inoar te ferbinen. Se binne haadlettergefoelich en moatte oer alle apparaten eksakt oerienkomme.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "Jo kinne ek ien fan dizze tichtbye apparaten selektearje:",
"You can change your choice at any time in the Settings dialog.": "Jo kinne jo kar op elk stuit oanpasse yn it Ynstellingsdialooch.",
"You can read more about the two release channels at the link below.": "Jo kinne mear lêze oer de twa útjeftekanalen fia de ûndersteande link.",
"You must keep at least one version.": "Jo moatte minstens ien ferzje bewarje.",

View File

@@ -158,8 +158,8 @@
"Please consult the release notes before performing a major upgrade.": "Nagyobb frissítés előtt ellenőrizni kell a kiadási megjegyzéseket.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Be kell állítani a grafikus felület felhasználónevét és jelszavát a Beállítások párbeszédablakban.",
"Please wait": "Türelem",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Prefix indicating that the file can be deleted if preventing directory removal": "Előtag, amely jelzi, hogy a fájl törölhető, ha tiltva van a mappák eltávolítása",
"Prefix indicating that the pattern should be matched without case sensitivity": "Előtag, amely jelzi, hogy a mintát nagy- ill. kisbetűérzékenység nélkül kell illeszteni.",
"Preview": "Előnézet",
"Preview Usage Report": "Használati jelentés áttekintése",
"Quick guide to supported patterns": "Rövid útmutató a használható mintákról",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Új eszköz hozzáadásakor nem szabad elfeledkezni arról, hogy a másik oldalon ezt az eszközt is hozzá kell adni.",
"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.": "Új eszköz hozzáadásakor észben kell tartani, hogy a mappaazonosító arra való, hogy összekösse a mappákat az eszközökön. Az azonosító kisbetű-nagybetű érzékeny és pontosan egyeznie kell az eszközökön.",
"Yes": "Igen",
"You can also select one of these nearby devices:": "Az alábbi közelben lévő eszközök közül lehet választani:",
"You can change your choice at any time in the Settings dialog.": "A beállításoknál bármikor módosíthatod a választásodat.",
"You can read more about the two release channels at the link below.": "A két kiadási csatornáról az alábbi linken olvashatsz további információkat.",
"You must keep at least one version.": "Legalább egy verziót meg kell tartani.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Yes",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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 can read more about the two release channels at the link below.",
"You must keep at least one version.": "You must keep at least one version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando si aggiunge un nuovo dispositivo, tenere presente che il dispositivo deve essere aggiunto anche dall'altra parte.",
"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.": "Quando aggiungi una nuova cartella, ricordati che gli ID vengono utilizzati per collegare le cartelle nei dispositivi. Distinguono maiuscole e minuscole e devono corrispondere esattamente su tutti i dispositivi.",
"Yes": "Sì",
"You can also select one of these nearby devices:": "È anche possibile selezionare uno di questi dispositivi nelle vicinanze:",
"You can change your choice at any time in the Settings dialog.": "Puoi sempre cambiare la tua scelta nel dialogo Impostazioni.",
"You can read more about the two release channels at the link below.": "Puoi ottenere piu informazioni riguarda i due canali di rilascio nel collegamento sottostante.",
"You must keep at least one version.": "È necessario mantenere almeno una versione.",

View File

@@ -1,7 +1,7 @@
{
"A device with that ID is already added.": "指定されたIDのデバイスは登録済みです。",
"A negative number of days doesn't make sense.": "負の日数は指定できません。",
"A new major version may not be compatible with previous versions.": "新しいメジャーバージョンは以前のバージョンと互換性がないかもしれません。",
"A negative number of days doesn't make sense.": "日数は0以上で指定してください。",
"A new major version may not be compatible with previous versions.": "新しいメジャーバージョンは以前のバージョンと互換性がない場合があります。",
"API Key": "APIキー",
"About": "Syncthingについて",
"Action": "動作",
@@ -10,24 +10,24 @@
"Add Device": "デバイスを追加",
"Add Folder": "フォルダーを追加",
"Add Remote Device": "接続先デバイスを追加",
"Add devices from the introducer to our device list, for mutually shared folders.": "紹介者デバイスから紹介されたデバイスは、相互に共有しているフォルダーがある場合、このデバイス上でも登録されます。",
"Add new folder?": "新しいフォルダーとして追加しますか?",
"Add devices from the introducer to our device list, for mutually shared folders.": "紹介者デバイスから紹介されたデバイスは、相互に共有しているフォルダーがある場合、このデバイス上にも追加されます。",
"Add new folder?": "新しいフォルダーとして追加しますか",
"Address": "アドレス",
"Addresses": "アドレス",
"Advanced": "高度な設定",
"Advanced Configuration": "高度な設定",
"Advanced settings": "高度な設定",
"All Data": "全てのデータ",
"Allow Anonymous Usage Reporting?": "匿名で用状況をレポートすることを許可しますか?",
"Allow Anonymous Usage Reporting?": "匿名で使用状況をレポートすることを許可しますか",
"Allowed Networks": "許可されているネットワーク",
"Alphabetic": "アルファベット順",
"An external command handles the versioning. It has to remove the file from the shared folder.": "外部コマンドにバージョン管理を任せます。ここで指定するコマンドは、共有フォルダーからファイルを削除するものでなくてはなりません。",
"An external command handles the versioning. It has to remove the file from the synced folder.": "外部コマンドにバージョンを管理させます。ここで指定するコマンドは、同期フォルダーからファイルを削除するものでなくてはなりません。",
"Anonymous Usage Reporting": "匿名での用状況レポート",
"Any devices configured on an introducer device will be added to this device as well.": "紹介者デバイス上で設定されたデバイスは、このデバイス上も追加されます。",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動アップグレードは、安定リリース版とリリース候補版のいずれかを選べるようになりました。",
"Anonymous Usage Reporting": "匿名での使用状況レポート",
"Any devices configured on an introducer device will be added to this device as well.": "紹介者デバイス上で設定されたデバイスは、このデバイス上も追加されます。",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動アップグレードは、安定版とリリース候補版のいずれかを選べるようになりました。",
"Automatic upgrades": "自動アップグレード",
"Be careful!": "注意してください。",
"Be careful!": "注意",
"Bugs": "バグ",
"CPU Utilization": "CPU使用率",
"Changelog": "更新履歴",
@@ -35,7 +35,7 @@
"Click to see discovery failures": "接続に失敗した探索サーバーを確認するにはクリックしてください",
"Close": "閉じる",
"Command": "コマンド",
"Comment, when used at the start of a line": "行頭で使用された場合、コメント行",
"Comment, when used at the start of a line": "行頭で使用するとコメント行になります",
"Compression": "圧縮",
"Configured": "設定値",
"Connection Error": "接続エラー",
@@ -45,12 +45,12 @@
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "無視パターンを作成中。既存のファイルが {{path}} にある場合は上書きされます。",
"Danger!": "危険",
"Danger!": "危険",
"Deleted": "削除",
"Device": "デバイス",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "デバイス「{{name}}」 ({{address}} の {{device}}) が接続を求めています。新しいデバイスとして追加しますか?",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "デバイス「{{name}}」 ({{address}} の {{device}}) が接続を求めています。新しいデバイスとして追加しますか",
"Device ID": "デバイスID",
"Device Identification": "デバイス識別情報",
"Device Identification": "デバイスID",
"Device Name": "デバイス名",
"Devices": "デバイス",
"Disconnected": "切断中",
@@ -65,24 +65,24 @@
"Edit Device": "デバイスの編集",
"Edit Folder": "フォルダーの編集",
"Editing": "編集中",
"Editing {%path%}.": "{{path}} を編集中",
"Editing {%path%}.": "{{path}} を編集中",
"Enable NAT traversal": "NATトラバーサルを有効にする",
"Enable Relaying": "中継サーバー経由の通信を有効にする",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "0以上の数 (例: 2.35) を入力し、単位を選択してください。パーセントはディスク容量全体に対する割合です。",
"Enter a non-privileged port number (1024 - 65535).": "非特権ポート番号 (1024 - 65535) を入力してください。",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "アドレスを指定する場合は「tcp://IPアドレス:ポート」または「tcp://ホスト名:ポート」コンマで区切って入力してください。自動探索を行う場合は「dynamic」と入力してください。",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "アドレスを指定する場合は「tcp://IPアドレス:ポート, tcp://ホスト名:ポート」のようにコンマで区切って入力してください。自動探索を行う場合は「dynamic」と入力してください。",
"Enter ignore patterns, one per line.": "無視するファイル名のパターンを、一行につき一条件で入力してください。",
"Error": "エラー",
"External File Versioning": "外部バージョン管理",
"Failed Items": "失敗した項目",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "お使いのネットワークIPv6の接続性がない場合、IPv6のサーバーへの接続に失敗しても異常ではありません。",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "お使いのネットワークIPv6を利用していない場合、IPv6のサーバーへの接続に失敗しても異常ではありません。",
"File Pull Order": "ファイルを取得する順序",
"File Versioning": "ファイルのバージョン管理",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "ファイルの変更を探すときにパーミッションを無視します。FATファイルシステムでご利用ください。",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたりしたファイルは、.stversions ディレクトリに移動します。",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Syncthingがファイルを置き換えたり削除したりするとき、古い内容を .stversions フォルダーに移動します。",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたりしたファイルは、タイムスタンプ付きのファイル名で .stversions ディレクトリに移動します。",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Syncthingがファイルを置き換えたり削除したりするとき、古い内容はファイル名にタイムスタンプを付けて .stversions フォルダーに移動します。",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "パーミッションの変更を検知しません。FATファイルシステムでご利用ください。",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたファイルは、.stversions ディレクトリに移動します。",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたファイルは、.stversions フォルダーに移動します。",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたファイルは、タイムスタンプ付きのファイル名で .stversions ディレクトリに移動します。",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Syncthingによって置き換えられたり削除されたファイルは、タイムスタンプ付きのファイル名で .stversions フォルダーに移動します。",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "ファイルを他のデバイスによる変更から保護します。一方、このデバイスでの変更は他のデバイスに送信されます。",
"Folder": "フォルダー",
"Folder ID": "フォルダーID",
@@ -94,12 +94,12 @@
"GUI Authentication Password": "GUI認証パスワード",
"GUI Authentication User": "GUI認証ユーザー名",
"GUI Listen Address": "GUI待ち受けアドレス",
"GUI Listen Addresses": "GUI待ち受けアドレスリスト",
"GUI Listen Addresses": "GUI待ち受けアドレス",
"GUI Theme": "GUIテーマ",
"Generate": "生成",
"Global Changes": "全変更点",
"Global Discovery": "大域探索",
"Global Discovery Servers": "大域探索サーバー",
"Global Discovery": "グローバル探索",
"Global Discovery Servers": "グローバル探索サーバー",
"Global State": "グローバル状態",
"Help": "ヘルプ",
"Home page": "ホームページ",
@@ -107,14 +107,14 @@
"Ignore Patterns": "無視するファイル名",
"Ignore Permissions": "パーミッションを無視する",
"Incoming Rate Limit (KiB/s)": "下り帯域制限 (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "間違った設定を行うと、フォルダーの内容を壊したり、Syncthingを操作不能にしたりする可能性があります。",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "間違った設定を行うと、フォルダーの内容を壊したり、Syncthingが動作しなくなる可能性があります。",
"Introduced By": "紹介元",
"Introducer": "紹介者デバイス",
"Inversion of the given condition (i.e. do not exclude)": "条件の否定 (つまり、無視しないという意味になります)",
"Keep Versions": "保するバージョン数",
"Keep Versions": "保するバージョン数",
"Largest First": "大きい順",
"Last File Received": "最後に受信したファイル",
"Last Scan": "最終スキャン時",
"Last Scan": "最終スキャン時",
"Last seen": "最終接続日時",
"Later": "後で設定",
"Latest Change": "最終変更内容",
@@ -125,7 +125,7 @@
"Local State (Total)": "ローカル状態 (合計)",
"Major Upgrade": "メジャーアップグレード",
"Master": "マスター",
"Maximum Age": "最大寿命",
"Maximum Age": "最大保存日数",
"Metadata Only": "メタデータのみ",
"Minimum Free Disk Space": "同期を停止する最小空きディスク容量",
"Move to top of queue": "最優先にする",
@@ -149,9 +149,9 @@
"Outgoing Rate Limit (KiB/s)": "上り帯域制限 (KiB/s)",
"Override Changes": "他のデバイスの変更を上書きする",
"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": "ローカルコンピュータ上のフォルダーパス。存在しない場合は作成されます。チルダ (~) で次のフォルダーを短縮入力できます:",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "古いバージョンを保存するパス (空欄の場合、デフォルトで共有フォルダー内の .stversions ディレクトリになります)。",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "古いバージョンを保存するパス (空欄の場合、デフォルトで .stversions になります)。",
"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": "ローカルコンピュータ上のフォルダーパス。フォルダーが存在しない場合は作成されます。チルダ (~) で次のフォルダーを短縮入力できます:",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "古いバージョンを保存するパス (空欄の場合、デフォルトで共有フォルダー内の .stversions ディレクトリ)",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "古いバージョンを保存するパス (空欄の場合、デフォルトでフォルダー内の .stversions フォルダー)",
"Pause": "一時停止",
"Pause All": "すべて一時停止",
"Paused": "一時停止中",
@@ -161,8 +161,8 @@
"Prefix indicating that the file can be deleted if preventing directory removal": "このファイルが中に残っているためにディレクトリを削除できない場合、このファイルごと消してもよいことを示す接頭辞",
"Prefix indicating that the pattern should be matched without case sensitivity": "大文字・小文字を同一視してマッチさせる接頭辞",
"Preview": "プレビュー",
"Preview Usage Report": "用状況レポートのプレビュー",
"Quick guide to supported patterns": "サポートされているパターンの簡易ガイド",
"Preview Usage Report": "使用状況レポートのプレビュー",
"Quick guide to supported patterns": "サポートされているパターンのクイックガイド",
"RAM Utilization": "メモリ使用量",
"Random": "ランダム",
"Reduced by ignore patterns": "無視パターン該当分を除く",
@@ -191,28 +191,28 @@
"Share": "共有",
"Share Folder": "フォルダーを共有する",
"Share Folders With Device": "このデバイスと共有するフォルダー",
"Share With Devices": "共有対象のデバイス",
"Share this folder?": "このフォルダーを共有しますか?",
"Shared With": "共有相手",
"Share With Devices": "共有するデバイス",
"Share this folder?": "このフォルダーを共有しますか",
"Shared With": "共有中のデバイス",
"Show ID": "IDを表示",
"Show QR": "QRコードを表示",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "ステータス画面でデバイスIDの代わりに表示されます。他のデバイスに対してもデフォルトの名前として通知されます。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "ステータス画面でデバイスIDの代わりに表示されます。空欄の場合、相手デバイスが通知してきた名前更新されます。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "ステータス画面でデバイスIDの代わりに表示されます。空欄にすると相手デバイスが通知してきた名前更新されます。",
"Shutdown": "シャットダウン",
"Shutdown Complete": "シャットダウン完了",
"Simple File Versioning": "単純バージョン管理",
"Single level wildcard (matches within a directory only)": "ワイルドカード (単一のディレクトリ内だけでマッチします)",
"Smallest First": "小さい順",
"Source Code": "ソースコード",
"Stable releases and release candidates": "安定リリース版とリリース候補版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "安定リリース版はおよそ2週間後にリリースされます。その間にリリース候補版としてテストされます。",
"Stable releases only": "安定リリース版のみ",
"Stable releases and release candidates": "安定版とリリース候補版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "安定版は、リリース候補版としてのテスト後およそ2週間リリースされます。",
"Stable releases only": "安定版のみ",
"Staggered File Versioning": "期間別バージョン管理",
"Start Browser": "起動時にウェブブラウザーで状態を表示する",
"Statistics": "統計情報",
"Stopped": "停止中",
"Support": "サポート",
"Sync Protocol Listen Addresses": "Syncプロトコルの待ち受けアドレスリスト",
"Sync Protocol Listen Addresses": "同期プロトコルの待ち受けアドレス",
"Syncing": "同期中",
"Syncthing has been shut down.": "Syncthingをシャットダウンしました。",
"Syncthing includes the following software or portions thereof:": "Syncthingは以下のソフトウェアまたはその一部を内包しています:",
@@ -223,26 +223,26 @@
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthingの管理画面が、パスワードなしで外部からアクセスできるように設定されています。",
"The aggregated statistics are publicly available at the URL below.": "集計結果は次のURLで公開されています。",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "設定が保存されましたが、まだ有効になっていません。新しい設定を有効にするにはSyncthingを再起動してください。",
"The device ID cannot be blank.": "デバイスIDは空欄にできません。",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ここに入力するデバイスIDは、接続したい相手側デバイスの [メニュー]→[IDを表示] で確認することができます。スペースとハイフンは入力しなくてもかまいません。",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "用状況レポートは暗号化されて毎日送信されます。この情報はプラットフォーム、フォルダーの大きさ、アプリのバージョンを調査するために使われます。送信するデータセットが変更された場合、このダイアログで再度確認が求められます。",
"The device ID cannot be blank.": "デバイスIDを入力してください。",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ここに入力するデバイスIDは、接続したい相手側デバイスの [メニュー]→[IDを表示] で確認できます。スペースとハイフンは入力しなくてもかまいません。",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "使用状況レポートは暗号化されて毎日送信されます。この情報はプラットフォーム、フォルダーのサイズ、アプリケーションのバージョンを調査するために使われます。送信するデータセットが変更された場合、このダイアログで再度確認が求められます。",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "入力されたデバイスIDが正しくありません。デバイスIDは52文字または56文字で、アルファベットと数字からなります。スペースとハイフンは入力してもしなくてもかまいません。",
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "第1コマンドライン引数はフォルダーのパス、第2引数はフォルダー内の相対パスです。",
"The folder ID cannot be blank.": "フォルダーIDは空欄にできません。",
"The folder ID must be unique.": "フォルダーIDが重複しています。",
"The folder path cannot be blank.": "フォルダーパスは空欄にできません。",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保存間隔は次の通りです。最近1時間は30秒ごとに古いバージョンを保存します。同様に、最近1日間は1時間ごとに、最近30日間は1日ごと、その後最大寿命までは1週間ごとに、古いバージョンを保存します。",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保存間隔は次の通りです。初めの1時間は30秒ごとに古いバージョンを保存します。同様に、初めの1日間は1時間ごと、初めの30日間は1日ごと、その後最大保存日数までは1週間ごとに、古いバージョンを保存します。",
"The following items could not be synchronized.": "以下の項目は同期できませんでした。",
"The maximum age must be a number and cannot be blank.": "最大寿命は数値を指定してください。空欄にはできません。",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "古いバージョンを保する最大日数 (0を指定すると無期限になります)。",
"The maximum age must be a number and cannot be blank.": "最大保存日数には数値を指定してください。空欄にはできません。",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "古いバージョンを保する最大日数 (0で無期限)",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "最小空きディスク容量はパーセントで、0から100の値を入力してください。",
"The number of days must be a number and cannot be blank.": "日数は数値を指定してください。空欄にはできません。",
"The number of days to keep files in the trash can. Zero means forever.": "ゴミ箱ファイルを保する日数。0を指定すると無期限になります。",
"The number of old versions to keep, per file.": "ファイルごとに古いバージョンをいくつ保するかを指定します。",
"The number of versions must be a number and cannot be blank.": "保するバージョン数は数値を指定してください。空欄にはできません。",
"The path cannot be blank.": "パスは空欄にできません。",
"The rate limit must be a non-negative number (0: no limit)": "帯域制限の値として負の数は指定できません (0を指定すると無制限になります)。",
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔として負の数は指定できません。",
"The number of days to keep files in the trash can. Zero means forever.": "ゴミ箱ファイルを保する日数 (0で無期限)",
"The number of old versions to keep, per file.": "ファイルごとに古いバージョンをいくつ保するかを指定します。",
"The number of versions must be a number and cannot be blank.": "保するバージョン数は数値を指定してください。空欄にはできません。",
"The path cannot be blank.": "パスを入力してください。",
"The rate limit must be a non-negative number (0: no limit)": "帯域制限値は0以上で指定して下さい。 (0で無制限)",
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔は0秒以上で指定してください。",
"They are retried automatically and will be synced when the error is resolved.": "エラーが解決すると、自動的に再試行され同期されます。",
"This Device": "このデバイス",
"This can easily give hackers access to read and change any files on your computer.": "この設定のままでは、あなたのコンピューターにある任意のファイルを、他者が簡単に盗み見たり書き換えたりすることができます。",
@@ -261,21 +261,22 @@
"Upgrading": "アップグレード中",
"Upload Rate": "アップロード速度",
"Uptime": "稼働時間",
"Usage reporting is always enabled for candidate releases.": "リリース候補版では常に用状況レポートが送信されます。",
"Usage reporting is always enabled for candidate releases.": "リリース候補版では常に使用状況レポートが送信されます。",
"Use HTTPS for GUI": "GUIにHTTPSを使用する",
"Version": "バージョン",
"Versions Path": "古いバージョンを保存するパス",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "古いバージョンは、最大寿命もしくは期間ごとの最大保存数を超えた場合、自動的に削除されます。",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "古いバージョンは、最大保存日数もしくは期間ごとの最大保存数を超えた場合、自動的に削除されます。",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolder}}」の親ディレクトリです。",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolderLabel}}」 ({{otherFolder}}) の親ディレクトリです。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolder}}」のサブディレクトリです。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolderLabel}}」 ({{otherFolder}}) のサブディレクトリです。",
"When adding a new device, keep in mind that this device must be added on the other side too.": "新しいデバイスを追加する際は、相手側デバイスにもこのデバイスを追加する必要があることに留意してください。",
"When adding a new device, keep in mind that this device must be added on the other side too.": "新しいデバイスを追加する際は、相手側デバイスにもこのデバイスを追加してください。",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "新しいフォルダーを追加する際、フォルダーIDはデバイス間でフォルダーの対応づけに使われることに注意してください。フォルダーIDは大文字と小文字が区別され、共有するすべてのデバイスの間で完全に一致しなくてはなりません。",
"Yes": "はい",
"You can change your choice at any time in the Settings dialog.": "どちらを選ぶかは設定ダイアログからいつでも変更できます。",
"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.": "2種類のリリースチャネルについての詳細は、以下のリンク先を参照してください。",
"You must keep at least one version.": "少なくとも一つのバージョンを保存してください。",
"You must keep at least one version.": "少なくとも一つのバージョンを保持する必要があります。",
"days": "日",
"directories": "個のディレクトリ",
"files": "個のファイル",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "새 장치를 추가할 시 추가한 기기 쪽에서도 이 장치를 추가해야 합니다.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "새 폴더를 추가할 시 폴더 ID는 장치간에 폴더를 묶을 때 사용됩니다. 대소문자를 구분하며 모든 장치에서 같은 ID를 사용해야 합니다.",
"Yes": "예",
"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 must keep at least one version.": "최소 한 개의 버전은 유지해야 합니다.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Pridėdami įrenginį, turėkite omeny, kad šis įrenginys taip pat turi būti pridėtas kitoje pusėje.",
"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.": "Kai įvedate naują aplanką neužmirškite, kad jis bus naudojamas visuose įrenginiuose. Svarbu visur įvesti visiškai tokį pat aplanko vardą neužmirštant apie didžiąsias ir mažąsias raides.",
"Yes": "Taip",
"You can also select one of these nearby devices:": "Jūs taip pat galite pasirinkti vieną iš šių šalia esančių įrenginių:",
"You can change your choice at any time in the Settings dialog.": "Jūs bet kuriuo metu galite pakeisti savo pasirinkimą nustatymų dialoge.",
"You can read more about the two release channels at the link below.": "Jūs galite perskaityti daugiau apie šiuos du laidos kanalus, pasinaudodami žemiau esančia nuoroda.",
"You must keep at least one version.": "Būtina saugoti bent vieną versiją.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Når en ny mappe blir lagt til, husk at Mappe-ID blir brukt til å binde sammen mapper mellom enheter. Det er forskjell på store og små bokstaver, så IDene må være identiske på alle enhetene.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Du kan endre ditt valg når som helst i innstillingene.",
"You can read more about the two release channels at the link below.": "Du kan lese mer om de to nye utgivelseskanalene i lenken nedenfor.",
"You must keep at least one version.": "Du må beholde minst én versjon",

View File

@@ -9,7 +9,7 @@
"Add": "Toevoegen",
"Add Device": "Apparaat toevoegen",
"Add Folder": "Map toevoegen",
"Add Remote Device": "Voeg extern apparaat toe",
"Add Remote Device": "Extern apparaat toevoegen",
"Add devices from the introducer to our device list, for mutually shared folders.": "Voeg apparaten van het introductieapparaat toe aan de lijst met apparaten voor gemeenschappelijk gedeelde mappen.",
"Add new folder?": "Nieuwe map toevoegen?",
"Address": "Adres",
@@ -63,11 +63,11 @@
"Downloading": "Bezig met downloaden",
"Edit": "Bewerk",
"Edit Device": "Bewerk apparaat",
"Edit Folder": "Bewerk map",
"Edit Folder": "Map bewerken",
"Editing": "Bezig met bewerken",
"Editing {%path%}.": "Bezig met bewerken van {{path}}.",
"Enable NAT traversal": "Activeer NAT traversal",
"Enable Relaying": "Activeer doorsturen",
"Enable NAT traversal": "NAT traversal inschakelen",
"Enable Relaying": "Doorsturen inschakelen",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Voer door komma's gescheiden (\"tcp://ip:port\", \"tcp://host:port\") adressen in of voer \"dynamisch\" in om automatische ontdekking van het adres uit te voeren.",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Wanneer een nieuw toestel wordt toegevoegd, houd er dan rekening mee dat dit toestel ook aan de andere kant moet worden toegevoegd.",
"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.": "Houd er bij het toevoegen van nieuwe mappen rekening mee dat het map-ID gebruikt wordt om mappen tussen apparaten te verbinden. Dit ID is hoofdlettergevoelig en moet identiek zijn op andere apparaten.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Je kan je keuze op elk moment aanpassen in de Instellingen.",
"You can read more about the two release channels at the link below.": "Je kan meer te weten komen over de twee uitgavekanalen via de link hieronder.",
"You must keep at least one version.": "Minstens 1 versie moet bewaard blijven.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Hugs at når ei ny eining vert lagt til, må ho òg leggjast til på andre sida.",
"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.": "Hugs at når ei ny mappe vert lagt til, vert mappe-ID-en brukt til å binda saman mappene mellom einingane. Det er skilnad på store og små bokstavar, så ID-ane må vera identiske på alle einingane.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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 can read more about the two release channels at the link below.",
"You must keep at least one version.": "Du må behalda minst ein versjon.",

View File

@@ -10,7 +10,7 @@
"Add Device": "Dodaj urządzenie",
"Add Folder": "Dodaj folder",
"Add Remote Device": "Dodaj urządzenie zdalne",
"Add devices from the introducer to our device list, for mutually shared folders.": "Add devices from the introducer to our device list, for mutually shared folders.",
"Add devices from the introducer to our device list, for mutually shared folders.": "Dodaj urządzenia od wprowadzającego do tej maszyny alby obustronnie dzielić katalogi.",
"Add new folder?": "Dodać nowy folder?",
"Address": "Adres",
"Addresses": "Adresy",
@@ -19,20 +19,20 @@
"Advanced settings": "Ustawienia zaawansowane",
"All Data": "Wszystkie dane",
"Allow Anonymous Usage Reporting?": "Zezwalaj na anonimowe statystyki użycia?",
"Allowed Networks": "Allowed Networks",
"Allowed Networks": "Dozwolone sieci",
"Alphabetic": "Alfabetycznie",
"An external command handles the versioning. It has to remove the file from the shared folder.": "An external command handles the versioning. It has to remove the file from the shared folder.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Zewnętrzna komenda obsługuje kontrolę wersji. Musi ona usuć ten plik z synchronizowanego folderu.",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Zewnętrzna komenda odpowiedzialna za wersjonowanie. Musi usunąć plik ze współdzielonego folderu.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Zewnętrzna komenda odpowiedzialna za wersjonowanie. Musi usuwać ten plik z synchronizowanego folderu.",
"Anonymous Usage Reporting": "Anonimowe statystyki użycia",
"Any devices configured on an introducer device will be added to this device as well.": "Wszystkie urządzenia skonfigurowane na urządzeniu wprowadzającym zostaną dodane także do tego urządzenia.",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatic upgrade now offers the choice between stable releases and release candidates.",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatyczne aktualizacje pozwalają teraz wybrać pomiędzy wydaniami stabilnymi a wersjami kandydującymi.",
"Automatic upgrades": "Automatyczne aktualizacje",
"Be careful!": "Uważaj!",
"Bugs": "Błędy",
"CPU Utilization": "Użycie CPU",
"Changelog": "Historia zmian",
"Clean out after": "Uporządkuj",
"Click to see discovery failures": "Click to see discovery failures",
"Click to see discovery failures": "Kliknij aby zobaczyć błędy odnajdywania",
"Close": "Zamknij",
"Command": "Polecenie",
"Comment, when used at the start of a line": "Komentarz, jeżeli użyty na początku linii",
@@ -43,8 +43,8 @@
"Copied from elsewhere": "Skopiowane z innego miejsca ",
"Copied from original": "Skopiowane z oryginału",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016: ",
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
"Copyright © 2014-2017 the following Contributors:": "Prawa autorskie © 2014-2017 dla następujących autorów:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Ustawienie wzorów ignorowania, nadpisze istniejący plik w {{path}}.",
"Danger!": "Niebezpieczne!",
"Deleted": "Usunięto",
"Device": "Urządzenie",
@@ -56,7 +56,7 @@
"Disconnected": "Rozłączony",
"Discovered": "Odkryte",
"Discovery": "Odnajdywanie",
"Discovery Failures": "Discovery Failures",
"Discovery Failures": "Błędy odnajdowania",
"Documentation": "Dokumentacja",
"Download Rate": "Prędkość pobierania",
"Downloaded": "Pobrane",
@@ -65,23 +65,23 @@
"Edit Device": "Edytuj urządzenie",
"Edit Folder": "Edytuj folder",
"Editing": "Edytowanie",
"Editing {%path%}.": "Editing {{path}}.",
"Editing {%path%}.": "Edytowanie {{path}}.",
"Enable NAT traversal": "Włącz trawersowanie NAT",
"Enable Relaying": "Włącz przekazywanie",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Wpisz nieujemną liczbę (np. \"2.35\") oraz wybierz jednostkę. Wartość procentowa odnosi się do rozmiaru całego dysku.",
"Enter a non-privileged port number (1024 - 65535).": "Wpisz nieuprzywilejowany numer portu (1024-65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Wpisz oddzielone przecinkiem adresy (\"tcp://ip:port\", \"tcp://host:port\") lub \"dynamic\" by przeprowadzić automatyczne odnalezienie adresu.",
"Enter ignore patterns, one per line.": "Wprowadź wzorce ignorowania, jeden w każdej linii.",
"Error": "Błąd",
"External File Versioning": "Zewnętrzne wersjonowanie pliku",
"Failed Items": "Niepowodzenia",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Błąd połączenia do serwerów IPv6 może wystąpić, jeśli brakuje połączenia po IPv6 w ogóle.",
"File Pull Order": "Kolejność pobierania plików",
"File Versioning": "Kontrola wersji",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Uprawnienia plików są ignorowane przy poszukiwaniu zmian. Używaj w systemie plików FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Files are moved to .stversions directory when replaced or deleted by Syncthing.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Pliki są przenoszone do folderu .stversions przez Syncthing, kiedy zostaną zmienione lub usunięte.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Pliki przenoszone są do folderu .stversions gdy są zastępowane bądź usuwane przez Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Pliki są datowane i przenoszone do folderu .stversions przez Syncthing, kiedy zostaną zmienione lub usunięte.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Pliki przenoszone są do wersji oznaczonych datą w folderze .stversions kiedy są zastępowane bądź usuwane przez Syncthing",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Pliki są zabezpieczone przed zmianami na innym urządzeniu, jednak zmiany w tym urządzeniu będą wysłane do reszty.",
"Folder": "Folder",
@@ -93,11 +93,11 @@
"GUI": "GUI",
"GUI Authentication Password": "Hasło",
"GUI Authentication User": "Użytkownik",
"GUI Listen Address": "GUI Listen Address",
"GUI Listen Address": "Adres nasłuchu GUI",
"GUI Listen Addresses": "Adres nasłuchiwania",
"GUI Theme": "Motyw GUI",
"Generate": "Generuj",
"Global Changes": "Globalne zmiany",
"Global Changes": "Zmiany globalne",
"Global Discovery": "Globalne odnajdywanie",
"Global Discovery Servers": "Globalne serwery odkrywania",
"Global State": "Status globalny",
@@ -118,7 +118,7 @@
"Last seen": "Ostatnio widziany",
"Later": "Później",
"Latest Change": "Ostatnia zmiana",
"Learn more": "Learn more",
"Learn more": "Zobacz więcej",
"Listeners": "Nasłuchujący",
"Local Discovery": "Lokalne odnajdywanie",
"Local State": "Status lokalny",
@@ -150,7 +150,7 @@
"Override Changes": "Nadpisz zmiany",
"Path": "Ścieżka",
"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": "Ścieżka do lokalnego folderu. Zostanie utworzona jeżeli nie istnieje.\nZnak tyldy (~) może zostać użyty jako skrót do",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Ścieżka gdzie wersje będą przechowywane (pozostaw puste dla domyślnego folderu .stversions we współ. folderze).",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Ścieżka gdzie będą przechowywane wersje (pozostaw puste dla domyślnego folderu .stversions)",
"Pause": "Zatrzymaj",
"Pause All": "Zatrzymaj wszystkie",
@@ -158,8 +158,8 @@
"Please consult the release notes before performing a major upgrade.": "Zaleca się przeanalizowanie \"release notes\" przed przeprowadzeniem znaczącej aktualizacji.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ustaw proszę użytkownika i hasło dostępowe do GUI w Ustawieniach",
"Please wait": "Proszę czekać",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefiks wskazujący, że plik może zostać usunięty w przypadku zapobiegania usunięciu katalogu",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefiks wskazujący, że wzorzec powinien być dopasowany bez rozróżniania wielkości liter",
"Preview": "Podgląd",
"Preview Usage Report": "Podgląd raportu użycia.",
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
@@ -167,7 +167,7 @@
"Random": "Losowo",
"Reduced by ignore patterns": "Ograniczono przez wzorce ignorowania",
"Release Notes": "Informacje o wydaniu",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Wydania kandydujące zawierają najnowsze funkcje oraz poprawki błędów. Są one podobne do tradycyjnych co dwutygodniowych wydań Syncthing.",
"Remote Devices": "Urządzenia zdalne",
"Remove": "Usuń",
"Required identifier for the folder. Must be the same on all cluster devices.": "Wymagany identyfikator dla folderu. Musi być taki sam na wszystkich urządzeniach.",
@@ -204,8 +204,8 @@
"Single level wildcard (matches within a directory only)": "Wieloznaczność na poziomie plików (uwzględnia nazwy plików)",
"Smallest First": "Najmniejsze na początku",
"Source Code": "Kod źródłowy",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
"Stable releases and release candidates": "Wydania stabilne i wydania kandydujące",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Wydania stabilne są opóźnione ok. dwa tygodnie. W tym czasie są testowane jako wydania kandydujące.",
"Stable releases only": "Tylko stabilne wydania",
"Staggered File Versioning": "Rozbudowane wersjonowanie pliku",
"Start Browser": "Uruchom przeglądarkę",
@@ -247,7 +247,7 @@
"This Device": "To urządzenie",
"This can easily give hackers access to read and change any files on your computer.": "Może to umożliwić osobom trzecim dostęp do odczytu i zmian dowolnych plików na urządzeniu.",
"This is a major version upgrade.": "To jest ważna aktualizacja",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Te ustawienia kontrolują ilość potrzebnej wolnej przestrzeni na dysku domowym (np. indeksowanie bazy danych).",
"Time": "Czas",
"Trash Can File Versioning": "Kontrola werjsi plików w koszu",
"Type": "Typ",
@@ -261,7 +261,7 @@
"Upgrading": "Aktualizowanie",
"Upload Rate": "Prędkość wysyłania",
"Uptime": "Czas działania",
"Usage reporting is always enabled for candidate releases.": "Usage reporting is always enabled for candidate releases.",
"Usage reporting is always enabled for candidate releases.": "Raportowanie użycia dla wydań kandydujących jest zawsze włączone.",
"Use HTTPS for GUI": "Używaj HTTPS",
"Version": "Wersja",
"Versions Path": "Ścieżka wersji",
@@ -273,8 +273,9 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Gdy dodajesz nowe urządzenie, pamiętaj że urządzenie musi zostać dodane także po drugiej stronie.",
"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.": "Przy dodawaniu nowego folderu, pamiętaj, że ID użyte jest do łączenia folderów pomiędzy urządzeniami. Wielkość liter ciągu ma znaczenie musi zgadzać się na wszystkich urządzeniach.",
"Yes": "Tak",
"You can change your choice at any time in the Settings dialog.": "Możesz zmienić swój wybór w dowolnej chwili w oknie Ustawi.",
"You can read more about the two release channels at the link below.": "You can read more about the two release channels at the link below.",
"You can also select one of these nearby devices:": "Możesz również wybrać jedno z pobliskich urządz:",
"You can change your choice at any time in the Settings dialog.": "Możesz zmienić swój wybór w dowolnej chwili w Ustawieniach",
"You can read more about the two release channels at the link below.": "Możesz więcej poczytać na temat obydwu wydań klikając poniższy link.",
"You must keep at least one version.": "Musisz posiadać przynajmniej jedną wersję",
"days": "dni",
"directories": "katalogi",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando estiver adicionando um dispositivo, lembre-se de que este dispositivo deve ser adicionado do outro lado também.",
"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.": "Quando adicionar uma nova pasta, lembre-se que o ID da pasta é utilizado para ligar pastas entre dispositivos. Ele é sensível às diferenças entre maiúsculas e minúsculas e deve ser o mesmo em todos os dispositivos.",
"Yes": "Sim",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Você pode mudar de ideia a qualquer momento na tela de configurações.",
"You can read more about the two release channels at the link below.": "Você pode se informar melhor sobre os dois canais de lançamento no link abaixo.",
"You must keep at least one version.": "Você deve manter pelo menos uma versão.",

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "Já foi adicionado um dispositivo com esse ID anteriormente.lavelwildcard\n",
"A device with that ID is already added.": "Já foi adicionado um dispositivo com esse ID anteriormente.",
"A negative number of days doesn't make sense.": "Um número negativo de dias não faz sentido.",
"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",
@@ -136,7 +136,7 @@
"Newest First": "Primeiro os mais recentes",
"No": "Não",
"No File Versioning": "Nenhuma",
"No upgrades": "Não existem actualizações",
"No upgrades": "Sem actualizações",
"Normal": "Normal",
"Notice": "Avisos",
"OK": "OK",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando adicionar um novo dispositivo, lembre-se que este dispositivo tem que ser adicionado do outro lado também.",
"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.": "Quando adicionar uma nova pasta, lembre-se que o ID da pasta é utilizado para ligar as pastas entre dispositivos. É sensível às diferenças entre maiúsculas e minúsculas e tem que ter uma correspondência perfeita entre todos os dispositivos.",
"Yes": "Sim",
"You can also select one of these nearby devices:": "Também pode seleccionar um destes dispositivos que estão próximos:",
"You can change your choice at any time in the Settings dialog.": "Pode modificar a sua escolha em qualquer altura nas configurações.",
"You can read more about the two release channels at the link below.": "Pode ler mais sobre os dois canais de lançamento na ligação abaixo.",
"You must keep at least one version.": "Tem que manter pelo menos uma versão.",

View File

@@ -19,9 +19,9 @@
"Advanced settings": "Дополнительные настройки",
"All Data": "Все данные",
"Allow Anonymous Usage Reporting?": "Разрешить анонимный отчет об использовании?",
"Allowed Networks": "Allowed Networks",
"Allowed Networks": "Разрешённые сети",
"Alphabetic": "По алфавиту",
"An external command handles the versioning. It has to remove the file from the shared folder.": "An external command handles the versioning. It has to remove the file from the shared folder.",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Для версионирования используется внешняя программа. Ей нужно удалить файл из общей папки.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Внешний процесс управляет версиями файлов. Процесс удалит файл из синхронизируемой папки.",
"Anonymous Usage Reporting": "Анонимный отчет об использовании",
"Any devices configured on an introducer device will be added to this device as well.": "Все устройства, подключённые к устройству-рекомендателю, будут добавлены к текущему устройству.",
@@ -43,7 +43,7 @@
"Copied from elsewhere": "Скопировано из другого места",
"Copied from original": "Скопировано с оригинала",
"Copyright © 2014-2016 the following Contributors:": "Авторские права © 20142016 принадлежат:",
"Copyright © 2014-2017 the following Contributors:": "Авторское право © 2014-2017 следующие участники:",
"Copyright © 2014-2017 the following Contributors:": "Авторские права © 20142017 следующие участники:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
"Danger!": "Опасно!",
"Deleted": "Удалено",
@@ -65,23 +65,23 @@
"Edit Device": "Редактирование устройства",
"Edit Folder": "Редактирование папки",
"Editing": "Редактирование",
"Editing {%path%}.": "Editing {{path}}.",
"Editing {%path%}.": "Правка {{path}}.",
"Enable NAT traversal": "Включить NAT traversal",
"Enable Relaying": "Включить релеи",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
"Enter a non-privileged port number (1024 - 65535).": "Введите непривилегированный порт (102465535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Введите через запятую («tcp://ip:port», «tcp://host:port») адреса, либо «dynamic», чтобы выполнить автоматическое обнаружение адреса.",
"Enter ignore patterns, one per line.": "Введите шаблоны игнорирования, по одному на строку.",
"Error": "Ошибка",
"External File Versioning": "Внешний контроль версий файлов",
"Failed Items": "Сбои",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Если нет IPv6-соединений, при подключении к IPv6-серверам произойдёт ошибка.",
"File Pull Order": "Порядок получения файлов",
"File Versioning": "Управление версиями",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Права на файлы игнорируются при поиске изменений. Используется на файловой системе FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Files are moved to .stversions directory when replaced or deleted by Syncthing.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, они помещаются в папку .stversions",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Файлы перемещаются в папку .stversions после их замены или удаления системой Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, их версии с таймштампами помещаются в папку .stversions",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Файлы с временнОй меткой версии помещаются в папку .stversions при их замене или удалении Syncthing.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Файлы защищены от изменений сделанных на других устройствах, но изменения сделанные на этом устройстве будут отправлены всему кластеру.",
"Folder": "Папка",
@@ -97,7 +97,7 @@
"GUI Listen Addresses": "Адрес панели управления",
"GUI Theme": "Тема оформления",
"Generate": "Сгенерировать",
"Global Changes": "Global Changes",
"Global Changes": "Глобальные изменения",
"Global Discovery": "Глобальное обнаружение",
"Global Discovery Servers": "Серверы глобального обнаружения",
"Global State": "Глобальное состояние",
@@ -150,7 +150,7 @@
"Override Changes": "Перезаписать изменения",
"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": "Путь к папке на локальном компьютере. Если её не существует, то она будет создана. Тильда (~) может использоваться как сокращение для",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Путь, в котором нужно хранить версии (оставьте пустым для папки по умолчанию .stversions внутри общей папки).",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Путь, где должны храниться версии (оставьте пустым, чтобы использовать папку по умолчанию .stversions внутри папки).",
"Pause": "Пауза",
"Pause All": "Приостановить все",
@@ -247,7 +247,7 @@
"This Device": "Это устройство",
"This can easily give hackers access to read and change any files on your computer.": "Это может дать доступ хакерам для чтения и изменения любых файлов на вашем компьютере.",
"This is a major version upgrade.": "Это обновление основной версии продукта.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Эта настройка управляет свободным местом, необходимым на домашнем диске (например, для базы индексов).",
"Time": "Время",
"Trash Can File Versioning": "Использовать версионность для файлов в Корзине",
"Type": "Тип",
@@ -266,13 +266,14 @@
"Version": "Версия",
"Versions Path": "Путь к версиям",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версии удаляются автоматически, если они существуют дольше максимального срока или превышают разрешённое количество файлов за интервал.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a parent directory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a parent directory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Внимание! Этот путь — родительская директория уже существующей папки «{{otherFolder}}».",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Внимание! Этот путь — родительская директория уже существующей папки «{{otherFolderLabel}}» ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Осторожно, этот путь является подкаталогом существующей папки «{{otherFolder}}».",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a subdirectory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Внимание! Этот путь — поддиректория уже существующей папки «{{otherFolderLabel}}» ({{otherFolder}}).",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когда добавляете устройство, помните о том, что это же устройство должно быть добавлено и другой стороной.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когда добавляете новую папку, помните, что ID папок используются для того, чтобы связывать папки между всеми устройствами. Они чувствительны к регистру и должны совпадать на всех используемых устройствах.",
"Yes": "Да",
"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 must keep at least one version.": "Вы должны хранить как минимум одну версию.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Áno",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Voľbu môžete kedykoľvek zmeniť v dialógu Nastavenia.",
"You can read more about the two release channels at the link below.": "O dvoch vydávacích kanáloch si môžete viacej prečítať v odkaze nižšie.",
"You must keep at least one version.": "Musíte ponechať aspoň jednu verziu",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet, kom ihåg att den här enheten måste läggas till på den andra enheten också.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "När du lägger till ny mapp, tänk på att mapp-ID knyter ihop mappar mellan olika enheter. De skiftlägeskänsliga och måste matcha precis mellan alla enheter.",
"Yes": "Ja",
"You can also select one of these nearby devices:": "Du kan också välja en av dessa närliggande enheter:",
"You can change your choice at any time in the Settings dialog.": "Du kan ändra ditt val när som helst i inställningsdialogrutan.",
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två publiceringsskanalerna på länken nedan.",
"You must keep at least one version.": "Du måste behålla åtminstone en version.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Yeni bir aygıt eklendiğinde, bu aygıtın karşı tarafa da eklenmesi gerektiğini unutmayın.",
"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.": "Yeni bir klasör eklendiğinde, Klasör ID'nin klasörleri aygıtlar arasında bağlantılandırmak için kullanıldığını unutmayın. Klasör ID'ler büyük - küçük harf duyarlıdır ve tüm aygıtlarda tamı tamına eşleşmelidir.",
"Yes": "Evet",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Seçiminizi herhangi bir zamanda Ayarlar penceresinde değiştirebilirsiniz.",
"You can read more about the two release channels at the link below.": "İki dağıtım kanalıyla ilgili daha çoğunu aşağıdaki bağlantıdan okuyabilirsiniz.",
"You must keep at least one version.": "En az bir sürümü tutmalısınız.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Коли додаєте новий вузол, пам’ятайте, що цей вузол повинен бути доданий і на іншій стороні.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Коли додаєте нову директорію, пам’ятайте, що ID цієї директорії використовується для того, щоб зв’язувати директорії разом між пристроями. Назви повинні точно співпадати між усіма пристроями, регістр символів має значення.",
"Yes": "Так",
"You can also select one of these nearby devices:": "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 must keep at least one version.": "Ви повинні зберігати щонайменше одну версію.",

View File

@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Khi thêm một thiết bị mới, hãy nhớ rằng thiết bị này cũng phải được thêm vào máy kia.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Khi thêm một thư mục mới, hãy nhớ rằng ID thư mục được dùng để gắn kết thư mục giữa các thiết bị với nhau. Chúng phải chính xác từng chữ, cả viết hoa và thường giữa tất cả thiết bị.",
"Yes": "Phải",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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 can read more about the two release channels at the link below.",
"You must keep at least one version.": "Bạn phải giữ ít nhất một phiên bản.",

View File

@@ -31,7 +31,7 @@
"Bugs": "问题回报",
"CPU Utilization": "CPU使用率",
"Changelog": "更新日志",
"Clean out after": "在该时间后清除",
"Clean out after": "在该时间后清除",
"Click to see discovery failures": "点击查看发现错误",
"Close": "关闭",
"Command": "命令",
@@ -111,7 +111,7 @@
"Introduced By": "介绍自",
"Introducer": "介绍人设备",
"Inversion of the given condition (i.e. do not exclude)": "对本条件取反(例如:不要排除某项)",
"Keep Versions": "保留历史版本数量",
"Keep Versions": "保留版本数量",
"Largest First": "大文件优先",
"Last File Received": "最后接收的文件",
"Last Scan": "最后扫描",
@@ -125,7 +125,7 @@
"Local State (Total)": "本地状态汇总",
"Major Upgrade": "重大更新",
"Master": "自主",
"Maximum Age": "历史版本最长保留时间",
"Maximum Age": "最长保留时间",
"Metadata Only": "仅元数据",
"Minimum Free Disk Space": "最低可用磁盘空间",
"Move to top of queue": "移动到队列顶端",
@@ -158,8 +158,8 @@
"Please consult the release notes before performing a major upgrade.": "请在进行重大更新前查看发布说明。",
"Please set a GUI Authentication User and Password in the Settings dialog.": "请在设置对话框中设置 GUI 验证用户及其密码。",
"Please wait": "请稍候",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Prefix indicating that the file can be deleted if preventing directory removal": "表示如果删除了阻止目录则文件可被删除的前缀",
"Prefix indicating that the pattern should be matched without case sensitivity": "表示该模式匹配忽略了大小写差异的前缀",
"Preview": "预览",
"Preview Usage Report": "预览使用报告",
"Quick guide to supported patterns": "支持的通配符的简单教程:",
@@ -273,6 +273,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "若您在本机添加新设备,记住您也必须在这个设备上添加本机。",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "若你添加了新文件夹,记住文件夹 ID 是用以在不同设备间建立联系的。在不同设备间拥有相同 ID 的文件夹将会被同步。且文件夹 ID 区分大小写。",
"Yes": "是",
"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 must keep at least one version.": "您必须保留至少一个版本。",

View File

@@ -9,32 +9,32 @@
-->
<html lang="en" ng-app="syncthing" ng-controller="SyncthingController" class="ng-cloak">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" href="assets/img/favicon-{{syncthingStatus()}}.png">
<link rel="mask-icon" href="assets/img/safari-pinned-tab.svg" color="#0882c8">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content=""/>
<meta name="author" content=""/>
<link rel="shortcut icon" href="assets/img/favicon-{{syncthingStatus()}}.png"/>
<link rel="mask-icon" href="assets/img/safari-pinned-tab.svg" color="#0882c8"/>
<title ng-bind="thisDeviceName() + ' | Syncthing'"></title>
<link href="vendor/bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="assets/font/raleway.css" rel="stylesheet">
<link href="vendor/font-awesome/css/font-awesome.css" rel="stylesheet">
<link href="assets/css/overrides.css" rel="stylesheet">
<link href="assets/css/theme.css" rel="stylesheet">
<link href="vendor/bootstrap/css/bootstrap.css" rel="stylesheet"/>
<link href="assets/font/raleway.css" rel="stylesheet"/>
<link href="vendor/font-awesome/css/font-awesome.css" rel="stylesheet"/>
<link href="assets/css/overrides.css" rel="stylesheet"/>
<link href="assets/css/theme.css" rel="stylesheet"/>
</head>
<body>
<script src="syncthing/development/logbar.js"></script>
<script type="text/javascript" src="syncthing/development/logbar.js"></script>
<div ng-if="version.isDevelopmentVersion" ng-include="'syncthing/development/logbar.html'"></div>
<!-- Top bar -->
<nav class="navbar navbar-top navbar-default" role="navigation">
<div class="container">
<span class="navbar-brand" aria-hidden="true">
<img class="logo hidden-xs" src="assets/img/logo-horizontal.svg" height="32" width="117"/>
<img class="logo hidden visible-xs" src="assets/img/favicon-default.png" height="32"/>
<img class="logo hidden-xs" src="assets/img/logo-horizontal.svg" height="32" width="117" alt=""/>
<img class="logo hidden visible-xs" src="assets/img/favicon-default.png" height="32" alt=""/>
</span>
<p class="navbar-text hidden-xs" ng-class="{'hidden-sm':upgradeInfo && upgradeInfo.newer}">{{thisDeviceName()}}</p>
<ul class="nav navbar-nav navbar-right">
@@ -394,7 +394,7 @@
</tr>
<tr>
<th><span class="fa fa-fw fa-share-alt"></span>&nbsp;<span translate>Shared With</span></th>
<td class="text-right">{{sharesFolder(folder)}}</td>
<td class="text-right" title="{{sharesFolder(folder)}}">{{sharesFolder(folder)}}</td>
</tr>
<tr>
<th><span class="fa fa-fw fa-clock-o"></span>&nbsp;<span translate>Last Scan</span></th>
@@ -564,7 +564,7 @@
<span ng-switch="deviceStatus(deviceCfg)" class="pull-right text-{{deviceClass(deviceCfg)}}">
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="syncing">
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | number:0}}%)
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | number:0}}%, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
</span>
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs">&#9724;</span></span>
@@ -645,7 +645,7 @@
</tr>
<tr ng-if="deviceFolders(deviceCfg).length > 0">
<th><span class="fa fa-fw fa-folder"></span>&nbsp;<span translate>Folders</span></th>
<td class="text-right">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
<td class="text-right" title="{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
</tr>
</tbody>
</table>
@@ -721,41 +721,41 @@
<ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>
<!-- vendor scripts -->
<script src="vendor/jquery/jquery-2.2.2.js"></script>
<script src="vendor/angular/angular.js"></script>
<script src="vendor/angular/angular-sanitize.js"></script>
<script src="vendor/angular/angular-translate.js"></script>
<script src="vendor/angular/angular-translate-loader-static-files.js"></script>
<script src="vendor/angular/angular-dirPagination.js"></script>
<script src="vendor/bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="vendor/jquery/jquery-2.2.2.js"></script>
<script type="text/javascript" src="vendor/angular/angular.js"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.js"></script>
<script type="text/javascript" src="vendor/angular/angular-translate.js"></script>
<script type="text/javascript" src="vendor/angular/angular-translate-loader-static-files.js"></script>
<script type="text/javascript" src="vendor/angular/angular-dirPagination.js"></script>
<script type="text/javascript" src="vendor/bootstrap/js/bootstrap.js"></script>
<!-- / vendor scripts -->
<!-- gui application code -->
<script src="syncthing/core/module.js"></script>
<script src="syncthing/core/alwaysNumberFilter.js"></script>
<script src="syncthing/core/basenameFilter.js"></script>
<script src="syncthing/core/binaryFilter.js"></script>
<script src="syncthing/core/durationFilter.js"></script>
<script src="syncthing/core/eventService.js"></script>
<script src="syncthing/core/identiconDirective.js"></script>
<script src="syncthing/core/languageSelectDirective.js"></script>
<script src="syncthing/core/lastErrorComponentFilter.js"></script>
<script src="syncthing/core/localeService.js"></script>
<script src="syncthing/core/modalDirective.js"></script>
<script src="syncthing/core/naturalFilter.js"></script>
<script src="syncthing/core/metricFilter.js"></script>
<script src="syncthing/core/notificationDirective.js"></script>
<script src="syncthing/core/pathIsSubDirDirective.js"></script>
<script src="syncthing/core/popoverDirective.js"></script>
<script src="syncthing/core/selectOnClickDirective.js"></script>
<script src="syncthing/core/syncthingController.js"></script>
<script src="syncthing/core/tooltipDirective.js"></script>
<script src="syncthing/core/uniqueFolderDirective.js"></script>
<script src="syncthing/core/validDeviceidDirective.js"></script>
<script src="assets/lang/valid-langs.js"></script>
<script src="assets/lang/prettyprint.js"></script>
<script src="meta.js"></script>
<script src="syncthing/app.js"></script>
<script type="text/javascript" src="syncthing/core/module.js"></script>
<script type="text/javascript" src="syncthing/core/alwaysNumberFilter.js"></script>
<script type="text/javascript" src="syncthing/core/basenameFilter.js"></script>
<script type="text/javascript" src="syncthing/core/binaryFilter.js"></script>
<script type="text/javascript" src="syncthing/core/durationFilter.js"></script>
<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/naturalFilter.js"></script>
<script type="text/javascript" src="syncthing/core/metricFilter.js"></script>
<script type="text/javascript" src="syncthing/core/notificationDirective.js"></script>
<script type="text/javascript" src="syncthing/core/pathIsSubDirDirective.js"></script>
<script type="text/javascript" src="syncthing/core/popoverDirective.js"></script>
<script type="text/javascript" src="syncthing/core/selectOnClickDirective.js"></script>
<script type="text/javascript" src="syncthing/core/syncthingController.js"></script>
<script type="text/javascript" src="syncthing/core/tooltipDirective.js"></script>
<script type="text/javascript" src="syncthing/core/uniqueFolderDirective.js"></script>
<script type="text/javascript" src="syncthing/core/validDeviceidDirective.js"></script>
<script type="text/javascript" src="assets/lang/valid-langs.js"></script>
<script type="text/javascript" src="assets/lang/prettyprint.js"></script>
<script type="text/javascript" src="meta.js"></script>
<script type="text/javascript" src="syncthing/app.js"></script>
<!-- / gui application code -->
</body>

View File

@@ -12,7 +12,7 @@
<p translate>Copyright &copy; 2014-2017 the following Contributors:</p>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Alexander Graf, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Simon Frei, Stefan Tatschner, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Alexandre Viau, Andrew Dunham, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Heiko Zuerker, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jochen Voss, Johan Vromans, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Niels Peter Roest, Pascal Jungblut, Peter Hoeg, Phill Luby, Piotr Bejda, Robert Carosi, Roman Zaynetdinov, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Tim Abell, Tim Howes, Tobias Nygren, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Wulf Weich, Xavier O., Yannic A.
Jakob Borg, Audrius Butkevicius, Alexander Graf, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Simon Frei, Stefan Tatschner, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Alexandre Viau, Andrew Dunham, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Heiko Zuerker, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jochen Voss, Johan Vromans, Jose Manuel Delicado, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Niels Peter Roest, Pascal Jungblut, Peter Hoeg, Phill Luby, Piotr Bejda, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Tim Abell, Tim Howes, Tobias Nygren, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Wulf Weich, Xavier O., Yannic A.
</div>
</div>
<hr/>

View File

@@ -74,7 +74,8 @@ angular.module('syncthing.core')
staggeredCleanInterval: 3600,
staggeredVersionsPath: "",
externalCommand: "",
autoNormalize: true
autoNormalize: true,
path: ""
};
$scope.localStateTotal = {
@@ -232,7 +233,8 @@ angular.module('syncthing.core')
address: arg.data.addr
};
$scope.completion[arg.data.id] = {
_total: 100
_total: 100,
_needBytes: 0
};
}
});
@@ -378,7 +380,8 @@ angular.module('syncthing.core')
$scope.devices = $scope.config.devices;
$scope.devices.forEach(function (deviceCfg) {
$scope.completion[deviceCfg.deviceID] = {
_total: 100
_total: 100,
_needBytes: 0
};
});
$scope.devices.sort(deviceCompare);
@@ -463,7 +466,7 @@ angular.module('syncthing.core')
function recalcCompletion(device) {
var total = 0, needed = 0, deletes = 0;
for (var folder in $scope.completion[device]) {
if (folder === "_total") {
if (folder === "_total" || folder === '_needBytes') {
continue;
}
total += $scope.completion[device][folder].globalBytes;
@@ -472,8 +475,10 @@ angular.module('syncthing.core')
}
if (total == 0) {
$scope.completion[device]._total = 100;
$scope.completion[device]._needBytes = 0;
} else {
$scope.completion[device]._total = 100 * (1 - needed / total);
$scope.completion[device]._needBytes = needed
}
if (needed == 0 && deletes > 0) {
@@ -481,6 +486,7 @@ angular.module('syncthing.core')
// to do. Drop down the completion percentage to indicate
// that we have stuff to do.
$scope.completion[device]._total = 95;
$scope.completion[device]._needBytes = 0;
}
console.log("recalcCompletion", device, $scope.completion[device]);
@@ -601,6 +607,25 @@ angular.module('syncthing.core')
$scope.neededTotal = data.total;
}
function pathJoin(base, name) {
base = expandTilde(base);
if (base[base.length - 1] !== $scope.system.pathSeparator) {
return base + $scope.system.pathSeparator + name;
}
return base + name;
}
function expandTilde(path) {
if (path && path.trim().charAt(0) === '~') {
return $scope.system.tilde + path.trim().substring(1);
}
return path;
}
function shouldSetDefaultFolderPath() {
return $scope.config.options && $scope.config.options.defaultFolderPath && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine
}
$scope.neededPageChanged = function (page) {
$scope.neededCurrentPage = page;
refreshNeed($scope.neededFolder);
@@ -1185,7 +1210,19 @@ angular.module('syncthing.core')
$scope.addDevice = function (deviceID, name) {
return $http.get(urlbase + '/system/discovery')
.success(function (registry) {
$scope.discovery = registry;
$scope.discovery = [];
outer:
for (var id in registry) {
if ($scope.discovery.length === 5) {
break;
}
for (var i = 0; i < $scope.devices.length; i++) {
if ($scope.devices[i].deviceID === id) {
continue outer;
}
}
$scope.discovery.push(id);
}
})
.then(function () {
$scope.currentDevice = {
@@ -1343,9 +1380,10 @@ angular.module('syncthing.core')
$scope.directoryList = [];
$scope.$watch('currentFolder.path', function (newvalue) {
if (newvalue && newvalue.trim().charAt(0) === '~') {
$scope.currentFolder.path = $scope.system.tilde + newvalue.trim().substring(1);
if (!newvalue) {
return;
}
$scope.currentFolder.path = expandTilde(newvalue);
$http.get(urlbase + '/system/browse', {
params: { current: newvalue }
}).success(function (data) {
@@ -1353,6 +1391,20 @@ angular.module('syncthing.core')
}).error($scope.emitHTTPError);
});
$scope.$watch('currentFolder.label', function (newvalue) {
if (!newvalue || !shouldSetDefaultFolderPath()) {
return;
}
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
});
$scope.$watch('currentFolder.id', function (newvalue) {
if (!newvalue || !shouldSetDefaultFolderPath() || $scope.currentFolder.label) {
return;
}
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
});
$scope.loadFormIntoScope = function (form) {
console.log('loadFormIntoScope',form.$name);
switch (form.$name) {
@@ -1377,6 +1429,7 @@ angular.module('syncthing.core')
};
$scope.editFolder = function (folderCfg) {
$scope.editingExisting = true;
$scope.currentFolder = angular.copy(folderCfg);
if ($scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
@@ -1419,21 +1472,21 @@ angular.module('syncthing.core')
}
$scope.currentFolder.externalCommand = $scope.currentFolder.externalCommand || "";
$scope.editingExisting = true;
$scope.editFolderModal();
};
$scope.addFolder = function () {
$http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
$scope.editingExisting = false;
$scope.currentFolder = angular.copy($scope.folderDefaults);
$scope.currentFolder.id = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
$scope.editingExisting = false;
$scope.editFolderModal();
});
};
$scope.addFolderAndShare = function (folder, folderLabel, device) {
$scope.dismissFolderRejection(folder, device);
$scope.editingExisting = false;
$scope.currentFolder = angular.copy($scope.folderDefaults);
$scope.currentFolder.id = folder;
$scope.currentFolder.label = folderLabel;
@@ -1442,7 +1495,6 @@ angular.module('syncthing.core')
};
$scope.currentFolder.selectedDevices[device] = true;
$scope.editingExisting = false;
$scope.editFolderModal();
};

View File

@@ -1,5 +1,5 @@
<div class="dev-top-bar" id="dev-top-bar" style="display: none">
<link href="assets/css/dev.css" rel="stylesheet">
<link href="assets/css/dev.css" rel="stylesheet"/>
<div class="row">
<div class="col-xs-4"><b>DEV</b></div>
<div id="log" class="col-xs-8">

View File

@@ -3,22 +3,30 @@
<form role="form" name="deviceEditor">
<div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
<label translate for="deviceID">Device ID</label>
<input ng-if="!editingExisting" name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required valid-deviceid list="discovery-list" />
<datalist id="discovery-list" ng-if="!editingExisting">
<option ng-repeat="(id, data) in discovery" value="{{id}}" />
</datalist>
<div ng-if="!editingExisting">
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true"/>
<datalist id="discovery-list">
<option ng-repeat="id in discovery" value="{{id}}" />
</datalist>
<p class="help-block" ng-if="discovery">
<span translate>You can also select one of these nearby devices:</span>
<ul>
<li ng-repeat="id in discovery"><a ng-click="currentDevice.deviceID = id">{{id}}</a></li>
</ul>
</p>
<p class="help-block">
<span translate ng-if="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">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).</span>
<span translate ng-show="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">When adding a new device, keep in mind that this device must be added on the other side too.</span>
<span translate ng-if="deviceEditor.deviceID.$error.required && deviceEditor.deviceID.$dirty">The device ID cannot be blank.</span>
<span translate ng-if="deviceEditor.deviceID.$error.validDeviceid && deviceEditor.deviceID.$dirty">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.</span>
<span translate ng-if="deviceEditor.deviceID.$error.unique && deviceEditor.deviceID.$dirty">A device with that ID is already added.</span>
</p>
</div>
<div ng-if="editingExisting" class="well well-sm text-monospace" select-on-click>{{currentDevice.deviceID}}</div>
<p class="help-block">
<span translate ng-if="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">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).</span>
<span translate ng-show="!editingExisting && (deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine)">When adding a new device, keep in mind that this device must be added on the other side too.</span>
<span translate ng-if="deviceEditor.deviceID.$error.required && deviceEditor.deviceID.$dirty">The device ID cannot be blank.</span>
<span translate ng-if="deviceEditor.deviceID.$error.validDeviceid && deviceEditor.deviceID.$dirty">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.</span>
<span translate ng-if="deviceEditor.deviceID.$error.unique && deviceEditor.deviceID.$dirty">A device with that ID is already added.</span>
</p>
</div>
<div class="form-group">
<label translate for="name">Device Name</label>
<input id="name" class="form-control" type="text" ng-model="currentDevice.name"></input>
<input id="name" class="form-control" type="text" ng-model="currentDevice.name" />
<p translate ng-if="currentDevice.deviceID == myID" class="help-block">Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.</p>
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
</div>

View File

@@ -2,21 +2,25 @@
<modal id="globalChanges" status="default" icon="{{'history'}}" heading="{{'Global Changes' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<table>
<tr>
<th translate>Device</th>
<th translate>Action</th>
<th translate>Type</th>
<th translate>Path</th>
<th translate>Time</th>
</tr>
<tr ng-repeat="changeEvent in globalChangeEvents">
<td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td>
<td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td>
<td>{{changeEvent.data.action}}</td>
<td>{{changeEvent.data.type}}</td>
<td class="globalChanges-path-col">{{changeEvent.data.path}}</td>
<td class="globalChanges-time-col">{{changeEvent.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr>
<thead>
<tr>
<th translate>Device</th>
<th translate>Action</th>
<th translate>Type</th>
<th translate>Path</th>
<th translate>Time</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="changeEvent in globalChangeEvents">
<td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td>
<td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td>
<td>{{changeEvent.data.action}}</td>
<td>{{changeEvent.data.type}}</td>
<td class="globalChanges-path-col">{{changeEvent.data.path}}</td>
<td class="globalChanges-time-col">{{changeEvent.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">

View File

@@ -1,7 +1,7 @@
<modal id="idqr" status="info" icon="qrcode" heading="{{'Device Identification' | translate}} - {{deviceName(currentDevice)}}" large="yes" closeable="yes">
<div class="modal-body">
<div class="well well-sm text-monospace text-center" select-on-click>{{currentDevice.deviceID}}</div>
<img ng-if="currentDevice.deviceID" class="center-block img-thumbnail" ng-src="qr/?text={{currentDevice.deviceID}}"/>
<img ng-if="currentDevice.deviceID" class="center-block img-thumbnail" ng-src="qr/?text={{currentDevice.deviceID}}" alt="qr code"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">

View File

@@ -12,7 +12,7 @@
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
<label for="folderID"><span translate>Folder ID</span></label>
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required unique-folder value="{{currentFolder.id}}"/>
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}"/>
<p class="help-block">
<span translate ng-if="folderEditor.folderID.$valid || folderEditor.folderID.$pristine">Required identifier for the folder. Must be the same on all cluster devices.</span>
<span translate ng-if="folderEditor.folderID.$error.uniqueFolder">The folder ID must be unique.</span>
@@ -21,7 +21,7 @@
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
<label translate for="folderPath">Folder Path</label>
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required path-is-sub-dir/>
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required="" aria-required="true" path-is-sub-dir/>
<datalist id="directory-list">
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
</datalist>
@@ -45,7 +45,7 @@
<div class="col-md-4" ng-repeat="device in otherDevices()">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]"> {{deviceName(device)}}
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]"/> {{deviceName(device)}}
</label>
</div>
</div>
@@ -68,7 +68,7 @@
<div class="col-md-6">
<div class="form-group" ng-class="{'has-error': folderEditor.rescanIntervalS.$invalid && folderEditor.rescanIntervalS.$dirty}">
<label for="rescanIntervalS"><span translate>Rescan Interval</span> (s)</label><br/>
<input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.rescanIntervalS" required min="0">
<input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.rescanIntervalS" required="" aria-required="true" min="0"/>
<p class="help-block">
<span translate ng-if="!folderEditor.rescanIntervalS.$valid && folderEditor.rescanIntervalS.$dirty">The rescan interval must be a non-negative number of seconds.</span>
</p>
@@ -77,7 +77,7 @@
<div class="col-md-6 form-horizontal">
<div class="form-group" ng-class="{'has-error': folderEditor.minDiskFree.$invalid && folderEditor.minDiskFree.$dirty}">
<label class="col-xs-12" for="minDiskFree"><span translate>Minimum Free Disk Space</span></label><br/>
<div class="col-xs-9"><input name="minDiskFree" id="minDiskFree" class="form-control" type="number" ng-model="currentFolder.minDiskFree.value" required min="0" step="0.01"></div>
<div class="col-xs-9"><input name="minDiskFree" id="minDiskFree" class="form-control" type="number" ng-model="currentFolder.minDiskFree.value" required="" aria-required="true" min="0" step="0.01"/></div>
<div class="col-xs-3"><select class="col-sm-3 form-control" ng-model="currentFolder.minDiskFree.unit">
<option value="%">%</option>
<option value="kB">kB</option>
@@ -106,7 +106,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentFolder.ignorePerms"> <span translate>Ignore Permissions</span>
<input type="checkbox" ng-model="currentFolder.ignorePerms"/> <span translate>Ignore Permissions</span>
</label>
</div>
<p translate class="help-block">File permission bits are ignored when looking for changes. Use on FAT file systems.</p>
@@ -140,7 +140,7 @@
<p translate class="help-block">Files are moved to .stversions directory when replaced or deleted by Syncthing.</p>
<label translate for="trashcanClean">Clean out after</label>
<div class="input-group">
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required min="0">
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required="" aria-required="true" min="0"/>
<div class="input-group-addon" translate>days</div>
</div>
<p class="help-block">
@@ -152,7 +152,7 @@
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
<p translate class="help-block">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</p>
<label translate for="simpleKeep">Keep Versions</label>
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required min="1">
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required="" aria-required="true" min="1"/>
<p class="help-block">
<span translate ng-if="folderEditor.simpleKeep.$valid || folderEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
<span translate ng-if="folderEditor.simpleKeep.$error.required && folderEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
@@ -163,7 +163,7 @@
<p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p>
<p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p>
<label translate for="staggeredMaxAge">Maximum Age</label>
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required min="0">
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required="" aria-required="true" min="0"/>
<p class="help-block">
<span translate ng-if="folderEditor.staggeredMaxAge.$valid || folderEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
<span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
@@ -172,13 +172,13 @@
</div>
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
<label translate for="staggeredVersionsPath">Versions Path</label>
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath">
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath"/>
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</p>
</div>
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
<p translate class="help-block">An external command handles the versioning. It has to remove the file from the shared folder.</p>
<label translate for="externalCommand">Command</label>
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder.externalCommand" required>
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder.externalCommand" required="" aria-required="true" />
<p class="help-block">
<span translate ng-if="folderEditor.externalCommand.$valid || folderEditor.externalCommand.$pristine">The first command line parameter is the folder path and the second parameter is the relative path in the folder.</span>
<span translate ng-if="folderEditor.externalCommand.$error.required && folderEditor.externalCommand.$dirty">The path cannot be blank.</span>

View File

@@ -6,18 +6,18 @@
<div class="col-md-6">
<div class="form-group">
<label translate for="DeviceName">Device Name</label>
<input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.deviceName">
<input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.deviceName"/>
</div>
<div class="form-group">
<label translate for="ListenAddressesStr">Sync Protocol Listen Addresses</label>&emsp;<a href="https://docs.syncthing.net/users/config.html#listen-addresses" target="_blank"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Help</span></a>
<input id="ListenAddressesStr" class="form-control" type="text" ng-model="tmpOptions._listenAddressesStr">
<input id="ListenAddressesStr" class="form-control" type="text" ng-model="tmpOptions._listenAddressesStr"/>
</div>
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxRecvKbps.$invalid && settingsEditor.MaxRecvKbps.$dirty}">
<label translate for="MaxRecvKbps">Incoming Rate Limit (KiB/s)</label>
<input id="MaxRecvKbps" name="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.maxRecvKbps" min="0">
<input id="MaxRecvKbps" name="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.maxRecvKbps" min="0"/>
<p class="help-block">
<span translate ng-if="settingsEditor.MaxRecvKbps.$error.min && settingsEditor.MaxRecvKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span>
</p>
@@ -25,7 +25,7 @@
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxSendKbps.$invalid && settingsEditor.MaxSendKbps.$dirty}">
<label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label>
<input id="MaxSendKbps" name="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.maxSendKbps" min="0">
<input id="MaxSendKbps" name="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.maxSendKbps" min="0"/>
<p class="help-block">
<span translate ng-if="settingsEditor.MaxSendKbps.$error.min && settingsEditor.MaxSendKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span>
</p>
@@ -36,7 +36,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input id="NATEnabled" type="checkbox" ng-model="tmpOptions.natEnabled"> <span translate>Enable NAT traversal</span>
<input id="NATEnabled" type="checkbox" ng-model="tmpOptions.natEnabled"/> <span translate>Enable NAT traversal</span>
</label>
</div>
</div>
@@ -45,7 +45,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.localAnnounceEnabled"> <span translate>Local Discovery</span>
<input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.localAnnounceEnabled"/> <span translate>Local Discovery</span>
</label>
</div>
</div>
@@ -57,7 +57,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"> <span translate>Global Discovery</span>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"/> <span translate>Global Discovery</span>
</label>
</div>
</div>
@@ -66,7 +66,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input id="RelaysEnabled" type="checkbox" ng-model="tmpOptions.relaysEnabled"> <span translate>Enable Relaying</span>
<input id="RelaysEnabled" type="checkbox" ng-model="tmpOptions.relaysEnabled"/> <span translate>Enable Relaying</span>
</label>
</div>
</div>
@@ -76,13 +76,13 @@
<div class="clearfix"></div>
<div class="form-group">
<label translate for="GlobalAnnServersStr">Global Discovery Servers</label>
<input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions._globalAnnounceServersStr">
<input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions._globalAnnounceServersStr"/>
</div>
<div class="form-horizontal">
<div class="form-group" ng-class="{'has-error': settingsEditor.minHomeDiskFree.$invalid && settingsEditor.minHomeDiskFree.$dirty}">
<label class="col-xs-12" for="minHomeDiskFree"><span translate>Minimum Free Disk Space</span></label><br/>
<div class="col-xs-9"><input name="minHomeDiskFree" id="minHomeDiskFree" class="form-control" type="number" ng-model="tmpOptions.minHomeDiskFree.value" required min="0" step="0.01"></div>
<div class="col-xs-9"><input name="minHomeDiskFree" id="minHomeDiskFree" class="form-control" type="number" ng-model="tmpOptions.minHomeDiskFree.value" required="" aria-required="true" min="0" step="0.01"/></div>
<div class="col-xs-3"><select class="col-sm-3 form-control" ng-model="tmpOptions.minHomeDiskFree.unit">
<option value="%">%</option>
<option value="kB">kB</option>
@@ -102,30 +102,30 @@
<div class="col-md-6">
<div class="form-group" ng-class="{'has-error': settingsEditor.Address.$invalid && settingsEditor.Address.$dirty}">
<label translate for="Address">GUI Listen Address</label>&emsp;<a href="https://docs.syncthing.net/users/guilisten.html" target="_blank"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Help</span></a>
<input id="Address" name="Address" class="form-control" type="text" ng-model="tmpGUI.address" ng-pattern="/.*:0*((102[4-9])|(10[3-9][0-9])|(1[1-9][0-9][0-9])|([2-9][0-9][0-9][0-9])|([1-6]\d{4}))$/">
<input id="Address" name="Address" class="form-control" type="text" ng-model="tmpGUI.address" ng-pattern="/.*:0*((102[4-9])|(10[3-9][0-9])|(1[1-9][0-9][0-9])|([2-9][0-9][0-9][0-9])|([1-6]\d{4}))$/"/>
<p class="help-block" ng-show="settingsEditor.Address.$invalid" translate>
Enter a non-privileged port number (1024 - 65535).
</p>
</div>
<div class="form-group">
<label translate for="User">GUI Authentication User</label>
<input id="User" class="form-control" type="text" ng-model="tmpGUI.user">
<input id="User" class="form-control" type="text" ng-model="tmpGUI.user"/>
</div>
<div class="form-group">
<label translate for="Password">GUI Authentication Password</label>
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.password" ng-trim="false">
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.password" ng-trim="false"/>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="UseTLS" type="checkbox" ng-model="tmpGUI.useTLS"> <span translate>Use HTTPS for GUI</span>
<input id="UseTLS" type="checkbox" ng-model="tmpGUI.useTLS"/> <span translate>Use HTTPS for GUI</span>
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="StartBrowser" type="checkbox" ng-model="tmpOptions.startBrowser"> <span translate>Start Browser</span>
<input id="StartBrowser" type="checkbox" ng-model="tmpOptions.startBrowser"/> <span translate>Start Browser</span>
</label>
</div>
</div>
@@ -141,7 +141,7 @@
<div class="form-group">
<div class="checkbox" ng-if="tmpOptions.upgrades != 'candidate'">
<label>
<input id="UREnabled" type="checkbox" ng-model="tmpOptions.urEnabled"> <span translate>Anonymous Usage Reporting</span> (<a href="" translate data-toggle="modal" data-target="#urPreview">Preview</a>)
<input id="UREnabled" type="checkbox" ng-model="tmpOptions.urEnabled"/> <span translate>Anonymous Usage Reporting</span> (<a href="" translate data-toggle="modal" data-target="#urPreview">Preview</a>)
</label>
</div>
<p class="help-block" ng-if="tmpOptions.upgrades == 'candidate'">

View File

@@ -14,7 +14,7 @@
<ul class="pagination pull-right">
<li ng-repeat="option in [10, 25, 50]" ng-class="{ active: failedPageSize == option }">
<a href="#" ng-click="failedChangePageSize(option)">{{option}}</a>
<li>
</li>
</ul>
<div class="clearfix"></div>
</div>

View File

@@ -60,7 +60,7 @@
<ul class="pagination pull-right">
<li ng-repeat="option in [10, 25, 50]" ng-class="{ active: neededPageSize == option }">
<a href="#" ng-click="neededChangePageSize(option)">{{option}}</a>
<li>
</li>
</ul>
<div class="clearfix"></div>
</div>

View File

@@ -17,6 +17,8 @@ import (
"net/url"
"os"
"path"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
@@ -29,7 +31,7 @@ import (
const (
OldestHandledVersion = 10
CurrentVersion = 20
CurrentVersion = 21
MaxRescanIntervalS = 365 * 24 * 60 * 60
)
@@ -268,6 +270,14 @@ func (cfg *Configuration) clean() error {
seenFolders[folder.ID] = struct{}{}
}
// Remove ignored folders that are anyway part of the configuration.
for i := 0; i < len(cfg.IgnoredFolders); i++ {
if _, ok := seenFolders[cfg.IgnoredFolders[i]]; ok {
cfg.IgnoredFolders = append(cfg.IgnoredFolders[:i], cfg.IgnoredFolders[i+1:]...)
i-- // IgnoredFolders[i] now points to something else, so needs to be rechecked
}
}
cfg.Options.ListenAddresses = util.UniqueStrings(cfg.Options.ListenAddresses)
cfg.Options.GlobalAnnServers = util.UniqueStrings(cfg.Options.GlobalAnnServers)
@@ -306,6 +316,9 @@ func (cfg *Configuration) clean() error {
if cfg.Version == 19 {
convertV19V20(cfg)
}
if cfg.Version == 20 {
convertV20V21(cfg)
}
// Build a list of available devices
existingDevices := make(map[protocol.DeviceID]bool)
@@ -355,6 +368,30 @@ func (cfg *Configuration) clean() error {
return nil
}
func convertV20V21(cfg *Configuration) {
for _, folder := range cfg.Folders {
switch folder.Versioning.Type {
case "simple", "trashcan":
// Clean out symlinks in the known place
cleanSymlinks(filepath.Join(folder.Path(), ".stversions"))
case "staggered":
versionDir := folder.Versioning.Params["versionsPath"]
if versionDir == "" {
// default place
cleanSymlinks(filepath.Join(folder.Path(), ".stversions"))
} else if filepath.IsAbs(versionDir) {
// absolute
cleanSymlinks(versionDir)
} else {
// relative to folder
cleanSymlinks(filepath.Join(folder.Path(), versionDir))
}
}
}
cfg.Version = 21
}
func convertV19V20(cfg *Configuration) {
cfg.Options.MinHomeDiskFree = Size{Value: cfg.Options.DeprecatedMinHomeDiskFreePct, Unit: "%"}
cfg.Options.DeprecatedMinHomeDiskFreePct = 0
@@ -632,3 +669,23 @@ loop:
}
return devices[0:count]
}
func cleanSymlinks(dir string) {
if runtime.GOOS == "windows" {
// We don't do symlinks on Windows. Additionally, there may
// be things that look like symlinks that are not, which we
// should leave alone. Deduplicated files, for example.
return
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
l.Infoln("Removing incorrectly versioned symlink", path)
os.Remove(path)
return filepath.SkipDir
}
return nil
})
}

View File

@@ -73,6 +73,7 @@ func TestDefaultValues(t *testing.T) {
KCPSendWindowSize: 128,
KCPUpdateIntervalMs: 25,
KCPFastResend: false,
DefaultFolderPath: "~",
}
cfg := New(device1)
@@ -221,6 +222,7 @@ func TestOverriddenValues(t *testing.T) {
KCPSendWindowSize: 1280,
KCPUpdateIntervalMs: 1000,
KCPFastResend: true,
DefaultFolderPath: "/media/syncthing",
}
os.Unsetenv("STNOUPGRADE")
@@ -807,3 +809,34 @@ func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
t.Error("Unexpected extra device")
}
}
func TestIssue4219(t *testing.T) {
// Adding a folder that was previously ignored should make it unignored.
r := bytes.NewReader([]byte(`{
"folders": [
{"id": "abcd123"}
],
"ignoredFolders": ["t1", "abcd123", "t2"]
}`))
cfg, err := ReadJSON(r, protocol.LocalDeviceID)
if err != nil {
t.Fatal(err)
}
if len(cfg.IgnoredFolders) != 2 {
t.Errorf("There should be two ignored folders, not %d", len(cfg.IgnoredFolders))
}
w := Wrap("/tmp/cfg", cfg)
if !w.IgnoredFolder("t1") {
t.Error("Folder t1 should be ignored")
}
if !w.IgnoredFolder("t2") {
t.Error("Folder t2 should be ignored")
}
if w.IgnoredFolder("abcd123") {
t.Error("Folder abcd123 should not be ignored")
}
}

View File

@@ -140,6 +140,7 @@ type OptionsConfiguration struct {
KCPCongestionControl bool `xml:"kcpCongestionControl" json:"kcpCongestionControl" default:"true"`
KCPSendWindowSize int `xml:"kcpSendWindowSize" json:"kcpSendWindowSize" default:"128"`
KCPReceiveWindowSize int `xml:"kcpReceiveWindowSize" json:"kcpReceiveWindowSize" default:"128"`
DefaultFolderPath string `xml:"defaultFolderPath" json:"defaultFolderPath" default:"~"`
DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes,omitempty" json:"-"`

View File

@@ -38,11 +38,12 @@
<stunKeepaliveSeconds>10</stunKeepaliveSeconds>
<stunServer>a.stun.com</stunServer>
<stunServer>b.stun.com</stunServer>
<defaultKCPEnabled>true</defaultKCPEnabled>
<kcpCongestionControl>false</kcpCongestionControl>
<kcpReceiveWindowSize>1280</kcpReceiveWindowSize>
<kcpSendWindowSize>1280</kcpSendWindowSize>
<kcpUpdateIntervalMs>1000</kcpUpdateIntervalMs>
<kcpFastResend>true</kcpFastResend>
<defaultKCPEnabled>true</defaultKCPEnabled>
<kcpCongestionControl>false</kcpCongestionControl>
<kcpReceiveWindowSize>1280</kcpReceiveWindowSize>
<kcpSendWindowSize>1280</kcpSendWindowSize>
<kcpUpdateIntervalMs>1000</kcpUpdateIntervalMs>
<kcpFastResend>true</kcpFastResend>
<defaultFolderPath>/media/syncthing</defaultFolderPath>
</options>
</configuration>

15
lib/config/testdata/v21.xml vendored Normal file
View File

@@ -0,0 +1,15 @@
<configuration version="21">
<folder id="test" path="testdata" type="readonly" ignorePerms="false" rescanIntervalS="600" autoNormalize="true">
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR"></device>
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"></device>
<minDiskFree unit="%">1</minDiskFree>
<maxConflicts>-1</maxConflicts>
<fsync>true</fsync>
</folder>
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" name="node one" compression="metadata">
<address>tcp://a</address>
</device>
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" name="node two" compression="metadata">
<address>tcp://b</address>
</device>
</configuration>

View File

@@ -26,8 +26,8 @@ var (
type filterList []*pfilter.PacketFilter
// Sort connections by wether the are unspecified or not, as connections
// listenin on all addresses are more useful.
// Sort connections by whether they are unspecified or not, as connections
// listening on all addresses are more useful.
func (f filterList) Len() int { return len(f) }
func (f filterList) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f filterList) Less(i, j int) bool {
@@ -92,7 +92,7 @@ func (f *kcpConversationFilter) Outgoing(out []byte, addr net.Addr) {
}
func (kcpConversationFilter) isKCPConv(data []byte) bool {
// Need atleast 5 bytes
// Need at least 5 bytes
if len(data) < 5 {
return false
}
@@ -143,7 +143,7 @@ func (f *stunFilter) ClaimIncoming(in []byte, addr net.Addr) bool {
}
func (f *stunFilter) isStunPayload(data []byte) bool {
// Need atleast 20 bytes
// Need at least 20 bytes
if len(data) < 20 {
return false
}

View File

@@ -51,14 +51,14 @@ func TestNamespacedTime(t *testing.T) {
n1 := NewNamespacedKV(ldb, "foo")
if v, ok := n1.Time("test"); v != (time.Time{}) || ok {
if v, ok := n1.Time("test"); !v.IsZero() || ok {
t.Errorf("Incorrect return v %v != %v || ok %v != false", v, time.Time{}, ok)
}
now := time.Now()
n1.PutTime("test", now)
if v, ok := n1.Time("test"); v != now || !ok {
if v, ok := n1.Time("test"); !v.Equal(now) || !ok {
t.Errorf("Incorrect return v %v != %v || ok %v != true", v, now, ok)
}
}

View File

@@ -77,8 +77,8 @@ type ChangeDetector interface {
}
type Matcher struct {
lines []string
patterns []Pattern
lines []string // exact lines read from .stignore
patterns []Pattern // patterns including those from included files
withCache bool
matches *cache
curHash string
@@ -386,11 +386,10 @@ func parseIgnoreFile(fd io.Reader, currentFile string, cd ChangeDetector) ([]str
} else if strings.HasPrefix(line, "#include ") {
includeRel := line[len("#include "):]
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
includeLines, includePatterns, err := loadIgnoreFile(includeFile, cd)
_, includePatterns, err := loadIgnoreFile(includeFile, cd)
if err != nil {
return fmt.Errorf("include of %q: %v", includeRel, err)
}
lines = append(lines, includeLines...)
patterns = append(patterns, includePatterns...)
} else {
// Path name or pattern, add it so it matches files both in

View File

@@ -872,3 +872,38 @@ func TestRoot(t *testing.T) {
}
}
}
func TestLines(t *testing.T) {
stignore := `
#include testdata/excludes
!/a
/*
`
pats := New(WithCache(true))
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
}
expectedLines := []string{
"",
"#include testdata/excludes",
"",
"!/a",
"/*",
"",
}
lines := pats.Lines()
if len(lines) != len(expectedLines) {
t.Fatalf("len(Lines()) == %d, expected %d", len(lines), len(expectedLines))
}
for i := range lines {
if lines[i] != expectedLines[i] {
t.Fatalf("Lines()[%d] == %s, expected %s", i, lines[i], expectedLines[i])
}
}
}

View File

@@ -533,7 +533,7 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) FolderComple
// If the completion is 100% but there are deletes we need to handle,
// drop it down a notch. Hack for consumers that look only at the
// percentage (our own GUI does the same calculation as here on it's own
// percentage (our own GUI does the same calculation as here on its own
// and needs the same fixup).
if need == 0 && deletes > 0 {
completionPct = 95 // chosen by fair dice roll

View File

@@ -350,6 +350,24 @@ func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileI
f.fileData[name] = data
}
func (f *fakeConnection) deleteFile(name string) {
f.mut.Lock()
defer f.mut.Unlock()
for i, fi := range f.files {
if fi.Name == name {
fi.Deleted = true
fi.ModifiedS = time.Now().Unix()
fi.Version = fi.Version.Update(f.id.Short())
fi.Sequence = time.Now().UnixNano()
fi.Blocks = nil
f.files = append(append(f.files[:i], f.files[i+1:]...), fi)
return
}
}
}
func (f *fakeConnection) sendIndexUpdate() {
f.model.IndexUpdate(f.id, f.folder, f.files)
}

View File

@@ -10,6 +10,7 @@ import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
@@ -204,6 +205,86 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
}
}
func TestRequestVersioningSymlinkAttack(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("no symlink support on Windows")
}
// Sets up a folder with trashcan versioning and tries to use a
// deleted symlink to escape
cfg := defaultConfig.RawCopy()
cfg.Folders[0] = config.NewFolderConfiguration("default", "_tmpfolder")
cfg.Folders[0].PullerSleepS = 1
cfg.Folders[0].Devices = []config.FolderDeviceConfiguration{
{DeviceID: device1},
{DeviceID: device2},
}
cfg.Folders[0].Versioning = config.VersioningConfiguration{
Type: "trashcan",
}
w := config.Wrap("/tmp/cfg", cfg)
db := db.OpenMemory()
m := NewModel(w, device1, "syncthing", "dev", db, nil)
m.AddFolder(cfg.Folders[0])
m.ServeBackground()
m.StartFolder("default")
defer m.Stop()
defer os.RemoveAll("_tmpfolder")
fc := addFakeConn(m, device2)
fc.folder = "default"
// Create a temporary directory that we will use as target to see if
// we can escape to it
tmpdir, err := ioutil.TempDir("", "syncthing-test")
if err != nil {
t.Fatal(err)
}
// We listen for incoming index updates and trigger when we see one for
// the expected test file.
idx := make(chan int)
fc.mut.Lock()
fc.indexFn = func(folder string, fs []protocol.FileInfo) {
idx <- len(fs)
}
fc.mut.Unlock()
// Send an update for the test file, wait for it to sync and be reported back.
fc.addFile("foo", 0644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
fc.sendIndexUpdate()
for updates := 0; updates < 1; updates += <-idx {
}
// Delete the symlink, hoping for it to get versioned
fc.deleteFile("foo")
fc.sendIndexUpdate()
for updates := 0; updates < 1; updates += <-idx {
}
// Recreate foo and a file in it with some data
fc.addFile("foo", 0755, protocol.FileInfoTypeDirectory, nil)
fc.addFile("foo/test", 0644, protocol.FileInfoTypeFile, []byte("testtesttest"))
fc.sendIndexUpdate()
for updates := 0; updates < 1; updates += <-idx {
}
// Remove the test file and see if it escaped
fc.deleteFile("foo/test")
fc.sendIndexUpdate()
for updates := 0; updates < 1; updates += <-idx {
}
path := filepath.Join(tmpdir, "test")
if _, err := os.Lstat(path); !os.IsNotExist(err) {
t.Fatal("File escaped to", path)
}
}
func setupModelWithConnection() (*Model, *fakeConnection) {
cfg := defaultConfig.RawCopy()
cfg.Folders[0] = config.NewFolderConfiguration("default", "_tmpfolder")

View File

@@ -855,7 +855,7 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo) {
err = osutil.InWritableDir(func(name string) error {
return f.moveForConflict(name, file.ModifiedBy.String())
}, realName)
} else if f.versioner != nil {
} else if f.versioner != nil && !cur.IsSymlink() {
err = osutil.InWritableDir(f.versioner.Archive, realName)
} else {
err = osutil.InWritableDir(os.Remove, realName)
@@ -1463,7 +1463,7 @@ func (f *sendReceiveFolder) performFinish(state *sharedPullerState) error {
return err
}
case f.versioner != nil:
case f.versioner != nil && !state.file.IsSymlink():
// If we should use versioning, let the versioner archive the old
// file before we replace it. Archiving a non-existent file is not
// an error.

View File

@@ -104,20 +104,26 @@ func CompareVersions(a, b string) Relation {
for i := 0; i < minlen; i++ {
if arel[i] < brel[i] {
if i == 0 {
// major version difference
if arel[0] == 0 && brel[0] == 1 {
// special case, v0.x is equivalent in majorness to v1.x.
return Older
}
return MajorOlder
}
if i == 1 && arel[0] == 0 {
return MajorOlder
}
// minor or patch version difference
return Older
}
if arel[i] > brel[i] {
if i == 0 {
// major version difference
if arel[0] == 1 && brel[0] == 0 {
// special case, v0.x is equivalent in majorness to v1.x.
return Newer
}
return MajorNewer
}
if i == 1 && arel[0] == 0 {
return MajorNewer
}
// minor or patch version difference
return Newer
}
}

View File

@@ -20,19 +20,21 @@ var versions = []struct {
{"0.1.2", "0.1.2", Equal},
{"0.1.3", "0.1.2", Newer},
{"0.1.1", "0.1.2", Older},
{"0.3.0", "0.1.2", MajorNewer},
{"0.0.9", "0.1.2", MajorOlder},
{"0.3.0", "0.1.2", Newer},
{"0.0.9", "0.1.2", Older},
{"1.3.0", "1.1.2", Newer},
{"1.0.9", "1.1.2", Older},
{"2.3.0", "1.1.2", MajorNewer},
{"1.0.9", "2.1.2", MajorOlder},
{"1.1.2", "0.1.2", MajorNewer},
{"0.1.2", "1.1.2", MajorOlder},
{"1.1.2", "0.1.2", Newer},
{"0.1.2", "1.1.2", Older},
{"2.1.2", "0.1.2", MajorNewer},
{"0.1.2", "2.1.2", MajorOlder},
{"0.1.10", "0.1.9", Newer},
{"0.10.0", "0.2.0", MajorNewer},
{"0.10.0", "0.2.0", Newer},
{"30.10.0", "4.9.0", MajorNewer},
{"0.9.0-beta7", "0.9.0-beta6", Newer},
{"0.9.0-beta7", "1.0.0-alpha", MajorOlder},
{"0.9.0-beta7", "1.0.0-alpha", Older},
{"1.0.0-alpha", "1.0.0-alpha.1", Older},
{"1.0.0-alpha.1", "1.0.0-alpha.beta", Older},
{"1.0.0-alpha.beta", "1.0.0-beta", Older},
@@ -73,6 +75,8 @@ func TestSelectedRelease(t *testing.T) {
}{
// Within the same "major" (minor, in this case) select the newest
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.12.26"}, "v0.12.26"},
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.13.0"},
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v1.0.0"}, "v1.0.0"},
// Do no select beta versions when we are not allowed to
{"v0.12.24", false, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.26"},
{"v0.12.24-beta.0", false, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.26"},
@@ -80,7 +84,7 @@ func TestSelectedRelease(t *testing.T) {
{"v0.12.24", true, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.27-beta.42"},
{"v0.12.24-beta.0", true, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.27-beta.42"},
// Select the best within the current major when there is a minor upgrade available
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.12.25"},
{"v0.12.24", false, []string{"v1.12.23", "v1.12.24", "v1.14.2", "v2.0.0"}, "v1.14.2"},
{"v1.12.24", false, []string{"v1.12.23", "v1.12.24", "v1.14.2", "v2.0.0"}, "v1.14.2"},
// Select the next major when we are at the best minor
{"v0.12.25", true, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.13.0"},

View File

@@ -41,13 +41,16 @@ func NewExternal(folderID, folderPath string, params map[string]string) Versione
// Archive moves the named file away to a version archive. If this function
// returns nil, the named file does not exist any more (has been archived).
func (v External) Archive(filePath string) error {
_, err := osutil.Lstat(filePath)
info, err := osutil.Lstat(filePath)
if os.IsNotExist(err) {
l.Debugln("not archiving nonexistent file", filePath)
return nil
} else if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
panic("bug: attempting to version a symlink")
}
l.Debugln("archiving", filePath)

View File

@@ -50,6 +50,9 @@ func (v Simple) Archive(filePath string) error {
} else if err != nil {
return err
}
if fileInfo.Mode()&os.ModeSymlink != 0 {
panic("bug: attempting to version a symlink")
}
versionsDir := filepath.Join(v.folderPath, ".stversions")
_, err = os.Stat(versionsDir)

View File

@@ -51,11 +51,14 @@ func NewStaggered(folderID, folderPath string, params map[string]string) Version
// Use custom path if set, otherwise .stversions in folderPath
var versionsDir string
if params["versionsPath"] == "" {
l.Debugln("using default dir .stversions")
versionsDir = filepath.Join(folderPath, ".stversions")
} else {
l.Debugln("using default dir .stversions")
} else if filepath.IsAbs(params["versionsPath"]) {
l.Debugln("using dir", params["versionsPath"])
versionsDir = params["versionsPath"]
} else {
versionsDir = filepath.Join(folderPath, params["versionsPath"])
l.Debugln("using dir", versionsDir)
}
s := &Staggered{
@@ -241,13 +244,16 @@ func (v *Staggered) Archive(filePath string) error {
v.mutex.Lock()
defer v.mutex.Unlock()
_, err := osutil.Lstat(filePath)
info, err := osutil.Lstat(filePath)
if os.IsNotExist(err) {
l.Debugln("not archiving nonexistent file", filePath)
return nil
} else if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
panic("bug: attempting to version a symlink")
}
if _, err := os.Stat(v.versionsPath); err != nil {
if os.IsNotExist(err) {

View File

@@ -44,13 +44,16 @@ func NewTrashcan(folderID, folderPath string, params map[string]string) Versione
// Archive moves the named file away to a version archive. If this function
// returns nil, the named file does not exist any more (has been archived).
func (t *Trashcan) Archive(filePath string) error {
_, err := osutil.Lstat(filePath)
info, err := osutil.Lstat(filePath)
if os.IsNotExist(err) {
l.Debugln("not archiving nonexistent file", filePath)
return nil
} else if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
panic("bug: attempting to version a symlink")
}
versionsDir := filepath.Join(t.folderPath, ".stversions")
if _, err := os.Stat(versionsDir); err != nil {

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "STDISCOSRV" "1" "May 17, 2017" "v0.14" "Syncthing"
.TH "STDISCOSRV" "1" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "STRELAYSRV" "1" "May 17, 2017" "v0.14" "Syncthing"
.TH "STRELAYSRV" "1" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-BEP" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-BEP" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-CONFIG" "5" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.
@@ -84,7 +84,7 @@ The following shows an example of the default configuration file (IDs will diffe
<configuration version="14">
<folder id="zj2AA\-q55a7" label="Default Folder (zj2AA\-q55a7)" path="/Users/jb/Sync/" type="readwrite" rescanIntervalS="60" ignorePerms="false" autoNormalize="true">
<device id="3LT2GA5\-CQI4XJM\-WTZ264P\-MLOGMHL\-MCRLDNT\-MZV4RD3\-KA745CL\-OGAERQZ"></device>
<minDiskFreePct>1</minDiskFreePct>
<minDiskFree unit="%">1</minDiskFree>
<versioning></versioning>
<copiers>0</copiers>
<pullers>0</pullers>
@@ -135,7 +135,7 @@ The following shows an example of the default configuration file (IDs will diffe
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFreePct>1</minHomeDiskFreePct>
<minHomeDiskFree unit="%">1</minHomeDiskFree>
<releasesURL>https://api.github.com/repos/syncthing/syncthing/releases?per_page=30</releasesURL>
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
@@ -162,7 +162,7 @@ migration from previous formats.
.ft C
<folder id="zj2AA\-q55a7" label="Default Folder (zj2AA\-q55a7)" path="/Users/jb/Sync/" type="readwrite" rescanIntervalS="60" ignorePerms="false" autoNormalize="true" ro="false">
<device id="3LT2GA5\-CQI4XJM\-WTZ264P\-MLOGMHL\-MCRLDNT\-MZV4RD3\-KA745CL\-OGAERQZ" introducedBy="2CYF2WQ\-AKZO2QZ\-JAKWLYD\-AGHMQUM\-BGXUOIS\-GYILW34\-HJG3DUK\-LRRYQAR"></device>
<minDiskFreePct>1</minDiskFreePct>
<minDiskFree unit="%">1</minDiskFree>
<versioning></versioning>
<copiers>0</copiers>
<pullers>0</pullers>
@@ -236,10 +236,10 @@ It is customary that the local device ID is included in all folders.
Syncthing will currently add this automatically if it is not present in
the configuration file.
.TP
.B minDiskFreePct
The percentage of space that should be available on the disk this folder
resides. The folder will be stopped when the percentage of free space goes
below the threshold. Set to zero to disable.
.B minDiskFree
The minimum required free space that should be available on the disk this folder
resides. The folder will be stopped when the value drops below the threshold. Accepted units are
\fB%\fP, \fBkB\fP, \fBMB\fP, \fBGB\fP and \fBTB\fP\&. Set to zero to disable.
.TP
.B versioning
Specifies a versioning configuration.
@@ -518,7 +518,7 @@ If set, this is the API key that enables usage of the REST interface.
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFreePct>1</minHomeDiskFreePct>
<minHomeDiskFree unit="%">1</minHomeDiskFree>
<releasesURL>https://api.github.com/repos/syncthing/syncthing/releases?per_page=30</releasesURL>
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
@@ -654,9 +654,10 @@ slow response time (slow connection/cpu) and large index exchanges.
.B pingIdleTimeS
Ping interval in seconds. Don\(aqt change it unless you feel it\(aqs necessary.
.TP
.B minHomeDiskFreePct
The percentage of space that should be available on the partition holding
the configuration and index.
.B minHomeDiskFree
The minimum required free space that should be available on the the
partition holding the configuration and index. Accepted units are \fB%\fP, \fBkB\fP,
\fBMB\fP, \fBGB\fP and \fBTB\fP\&.
.TP
.B releasesURL
The URL from which release information is loaded, for automatic upgrades.
@@ -675,6 +676,10 @@ into account files that have at least this many blocks.
The following address types are accepted in sync protocol listen addresses:
.INDENT 0.0
.TP
.B Default listen addresses (\fBdefault\fP)
This is equivalent to \fBtcp://0.0.0.0:22000\fP and
\fBdynamic+https://relays.syncthing.net/endpoint\fP\&.
.TP
.B TCP wildcard and port (\fBtcp://0.0.0.0:22000\fP, \fBtcp://:22000\fP)
These are equivalent and will result in Syncthing listening on all
interfaces, IPv4 and IPv6, on the specified port.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-DEVICE-IDS" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-EVENT-API" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.
@@ -37,7 +37,7 @@ core utility towards a GUI.
.sp
To receive events, perform a HTTP GET of \fB/rest/events\fP or
\fB/rest/events/disk\fP\&. The latter returns only local\-change\-detected and
local\-change\-detected events, the former all other events.
remote\-change\-detected events, the former all other events.
.sp
The optional parameter \fBsince=<lastSeenID>\fP sets the ID of the last event
you\(aqve already seen. Syncthing returns a JSON encoded array of event objects,

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-FAQ" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-GLOBALDISCO" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-LOCALDISCO" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-NETWORKING" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-RELAY" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-REST-API" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.
@@ -885,7 +885,7 @@ core utility towards a GUI.
.sp
To receive events, perform a HTTP GET of \fB/rest/events\fP or
\fB/rest/events/disk\fP\&. The latter returns only local\-change\-detected and
local\-change\-detected events, the former all other events.
remote\-change\-detected events, the former all other events.
.sp
The optional parameter \fBsince=<lastSeenID>\fP sets the ID of the last event
you\(aqve already seen. Syncthing returns a JSON encoded array of event objects,

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-SECURITY" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-STIGNORE" "5" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING-VERSIONING" "7" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SYNCTHING" "1" "May 17, 2017" "v0.14" "Syncthing"
.TH "SYNCTHING" "1" "July 03, 2017" "v0.14" "Syncthing"
.SH NAME
syncthing \- Syncthing
.

3
meta/README.txt Normal file
View File

@@ -0,0 +1,3 @@
The files in this directory contain metadata tests - that is, tests on the
shape and colour of the code in the rest of the repository. This code is not
compiled into the final product.

View File

@@ -4,19 +4,16 @@
// 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/.
// +build ignore
// Checks for authors that are not mentioned in AUTHORS
package main
package meta
import (
"bytes"
"io/ioutil"
"log"
"os"
"os/exec"
"regexp"
"strings"
"testing"
)
// list of commits that we don't include in our checks; because they are
@@ -37,34 +34,36 @@ var excludeCommits = stringSetFromStrings([]string{
"bcc5d7c00f52552303b463d43a636f27b7f7e19b",
})
func init() {
log.SetOutput(os.Stdout)
log.SetFlags(0)
}
func TestCheckAuthors(t *testing.T) {
if testing.Short() {
t.Skip("skipping slow test")
}
func main() {
actual := actualAuthorEmails("cmd/", "lib/", "gui/", "test/", "script/")
listed := listedAuthorEmails()
actual, hashes := actualAuthorEmails(t, ".", "../cmd/", "../lib/", "../gui/", "../test/", "../script/")
listed := listedAuthorEmails(t)
missing := actual.except(listed)
if len(missing) > 0 {
log.Println("Missing authors:")
for author := range missing {
log.Println(" ", author)
for author := range missing {
t.Logf("Missing author: %s", author)
for _, hash := range hashes[author] {
t.Logf(" in hash: %s", hash)
}
os.Exit(1)
}
if len(missing) > 0 {
t.Errorf("Missing %d author(s)", len(missing))
}
}
// actualAuthorEmails returns the set of author emails found in the actual git
// commit log, except those in excluded commits.
func actualAuthorEmails(paths ...string) stringSet {
func actualAuthorEmails(t *testing.T, paths ...string) (stringSet, map[string][]string) {
args := append([]string{"log", "--format=%H %ae"}, paths...)
cmd := exec.Command("git", args...)
bs, err := cmd.Output()
if err != nil {
log.Fatal("authorEmails:", err)
t.Fatal("authorEmails:", err)
}
hashes := make(map[string][]string)
authors := newStringSet()
for _, line := range bytes.Split(bs, []byte{'\n'}) {
fields := strings.Fields(string(line))
@@ -77,21 +76,22 @@ func actualAuthorEmails(paths ...string) stringSet {
continue
}
if strings.Contains(strings.ToLower(body(hash)), "skip-check: authors") {
if strings.Contains(strings.ToLower(body(t, hash)), "skip-check: authors") {
continue
}
authors.add(author)
hashes[author] = append(hashes[author], hash)
}
return authors
return authors, hashes
}
// listedAuthorEmails returns the set of author emails mentioned in AUTHORS
func listedAuthorEmails() stringSet {
bs, err := ioutil.ReadFile("AUTHORS")
func listedAuthorEmails(t *testing.T) stringSet {
bs, err := ioutil.ReadFile("../AUTHORS")
if err != nil {
log.Fatal("listedAuthorEmails:", err)
t.Fatal("listedAuthorEmails:", err)
}
emailRe := regexp.MustCompile(`<([^>]+)>`)
@@ -104,11 +104,11 @@ func listedAuthorEmails() stringSet {
return authors
}
func body(hash string) string {
func body(t *testing.T, hash string) string {
cmd := exec.Command("git", "show", "--pretty=format:%b", "-s", hash)
bs, err := cmd.Output()
if err != nil {
log.Fatal("body:", err)
t.Fatal("body:", err)
}
return string(bs)
}

View File

@@ -4,26 +4,27 @@
// 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/.
// +build ignore
// Checks for files missing copyright notice
package main
package meta
import (
"bufio"
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
// File extensions to check
var checkExts = map[string]bool{
var copyrightCheckExts = map[string]bool{
".go": true,
}
// Directories to search
var copyrightCheckDirs = []string{".", "../cmd", "../lib", "../test", "../script"}
// Valid copyright headers, searched for in the top five lines in each file.
var copyrightRegexps = []string{
`Copyright`,
@@ -34,13 +35,11 @@ var copyrightRegexps = []string{
var copyrightRe = regexp.MustCompile(strings.Join(copyrightRegexps, "|"))
func main() {
flag.Parse()
for _, dir := range flag.Args() {
func TestCheckCopyright(t *testing.T) {
for _, dir := range copyrightCheckDirs {
err := filepath.Walk(dir, checkCopyright)
if err != nil {
fmt.Println(err)
os.Exit(1)
t.Error(err)
}
}
}
@@ -52,7 +51,7 @@ func checkCopyright(path string, info os.FileInfo, err error) error {
if !info.Mode().IsRegular() {
return nil
}
if !checkExts[filepath.Ext(path)] {
if !copyrightCheckExts[filepath.Ext(path)] {
return nil
}

45
meta/gofmt_test.go Normal file
View File

@@ -0,0 +1,45 @@
// Copyright (C) 2015 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/.
// Checks for authors that are not mentioned in AUTHORS
package meta
import (
"os"
"os/exec"
"path/filepath"
"testing"
)
var gofmtCheckDirs = []string{".", "../cmd", "../lib", "../test", "../script"}
func TestCheckGoFmt(t *testing.T) {
for _, dir := range gofmtCheckDirs {
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == ".git" {
return filepath.SkipDir
}
if filepath.Ext(path) != ".go" {
return nil
}
cmd := exec.Command("gofmt", "-s", "-d", path)
bs, err := cmd.CombinedOutput()
if err != nil {
return err
}
if len(bs) != 0 {
t.Errorf("File %s is not formatted correctly:\n\n%s", path, string(bs))
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
}

118
meta/metalint_test.go Normal file
View File

@@ -0,0 +1,118 @@
// Copyright (C) 2017 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package meta
import (
"bytes"
"log"
"os/exec"
"strings"
"testing"
)
var (
// fast linters complete in a fraction of a second and might as well be
// run always as part of the build
fastLinters = []string{
"deadcode",
"golint",
"ineffassign",
"vet",
}
// slow linters take several seconds and are run only as part of the
// "metalint" command.
slowLinters = []string{
"gosimple",
"staticcheck",
"structcheck",
"unused",
"varcheck",
}
// Which parts of the tree to lint
lintDirs = []string{
".",
"../cmd/...",
"../lib/...",
"../script/...",
}
// Messages to ignore
lintExcludes = []string{
".pb.go",
"should have comment",
"protocol.Vector composite literal uses unkeyed fields",
"cli.Requires composite literal uses unkeyed fields",
"Use DialContext instead", // Go 1.7
"os.SEEK_SET is deprecated", // Go 1.7
"SA4017", // staticcheck "is a pure function but its return value is ignored"
}
)
func TestCheckMetalint(t *testing.T) {
if !isGometalinterInstalled() {
return
}
gometalinter(t, lintDirs, lintExcludes...)
}
func isGometalinterInstalled() bool {
if _, err := runError("gometalinter", "--disable-all"); err != nil {
log.Println("gometalinter is not installed")
return false
}
return true
}
func gometalinter(t *testing.T, dirs []string, excludes ...string) bool {
params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
for _, linter := range fastLinters {
params = append(params, "--enable="+linter)
}
if !testing.Short() {
for _, linter := range slowLinters {
params = append(params, "--enable="+linter)
}
}
for _, exclude := range excludes {
params = append(params, "--exclude="+exclude)
}
params = append(params, dirs...)
bs, _ := runError("gometalinter", params...)
nerr := 0
lines := make(map[string]struct{})
for _, line := range strings.Split(string(bs), "\n") {
if line == "" {
continue
}
if _, ok := lines[line]; ok {
continue
}
log.Println(line)
if strings.Contains(line, "executable file not found") {
log.Println(` - Try "go run build.go setup" to install missing tools`)
}
lines[line] = struct{}{}
nerr++
}
return nerr == 0
}
func runError(cmd string, args ...string) ([]byte, error) {
ecmd := exec.Command(cmd, args...)
bs, err := ecmd.CombinedOutput()
return bytes.TrimSpace(bs), err
}

View File

@@ -108,6 +108,9 @@ func runError(cmd string, args ...string) ([]byte, error) {
func githubIssueTitle(n int) (string, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/syncthing/syncthing/issues/%d", n), nil)
if err != nil {
return "", err
}
user, token := os.Getenv("GITHUB_USERNAME"), os.Getenv("GITHUB_TOKEN")
if user != "" && token != "" {

View File

@@ -33,7 +33,7 @@ func TestReset(t *testing.T) {
size := createFiles(t)
p := startInstance(t, 1)
defer p.Stop() // Not checkedStop, because Syncthing will exit on it's own
defer p.Stop() // Not checkedStop, because Syncthing will exit on its own
m, err := p.Model("default")
if err != nil {
@@ -76,7 +76,7 @@ func TestReset(t *testing.T) {
// ---- Syncthing exits here ----
p = startInstance(t, 1)
defer p.Stop() // Not checkedStop, because Syncthing will exit on it's own
defer p.Stop() // Not checkedStop, because Syncthing will exit on its own
m, err = p.Model("default")
if err != nil {