Compare commits

..

54 Commits

Author SHA1 Message Date
Jakob Borg
1be4b8bb5d Merge pull request #486 from AudriusButkevicius/windows
Add Windows upgrade support
2014-08-07 23:20:26 +02:00
Jakob Borg
c832fc9917 Merge pull request #485 from tojrobinson/world-writable-root
World writable root
2014-08-07 23:17:42 +02:00
Jakob Borg
4797a94689 Add explicit GC calls after expensive db ops (ref #468) 2014-08-07 23:09:50 +02:00
Audrius Butkevicius
6948903084 Add Windows upgrade support 2014-08-07 21:07:21 +01:00
treefingers
94164611ae Fix root being left world writable 2014-08-08 05:45:50 +10:00
treefingers
ae298e8902 Merge branch 'master' of https://github.com/syncthing/syncthing 2014-08-08 05:06:42 +10:00
Jakob Borg
3d8771ecb0 Woops, broke the build 2014-08-07 15:58:48 +02:00
Jakob Borg
28db264e90 Upgrade debugging, fix upgrade on ARM (fixes #482) 2014-08-07 15:57:20 +02:00
Jakob Borg
6af9fa4b81 Localize Close button in standard modals (fixes #481) 2014-08-07 12:35:38 +02:00
Jakob Borg
60b4d05860 Translation update, add Danish & Dutch 2014-08-07 10:49:29 +02:00
Jakob Borg
7b93839ed1 Woops, broke the build 2014-08-07 10:26:26 +02:00
Jakob Borg
fdb11d7c06 Correctly handle file updates in read only directories (fixes #470) 2014-08-07 08:31:22 +02:00
Jakob Borg
5651847877 Merge commit 'bc2bb22'
* commit 'bc2bb22':
  Add no-browser flag
2014-08-07 07:20:39 +02:00
Jakob Borg
e1442290b6 Add tojrobinson 2014-08-07 07:20:21 +02:00
Tully Robinson
c45b18cc75 Merge branch 'master' into browser-flag 2014-08-06 23:01:35 +10:00
Jakob Borg
bb2ad77987 Never remove currently valid languages when updating translations 2014-08-06 14:56:32 +02:00
Jakob Borg
68b1ffec19 Fix translation in upgrading/restarting dialogs 2014-08-06 14:41:46 +02:00
Tully Robinson
bc2bb22673 Add no-browser flag 2014-08-06 22:30:18 +10:00
Jakob Borg
83d707fc4b Add Transifex info to contribution guidelines 2014-08-06 11:03:39 +02:00
Jakob Borg
175b32e56c Forgot the favicon 2014-08-06 09:12:11 +02:00
Jakob Borg
97b4a6553b Logo update 2014-08-06 09:07:13 +02:00
Jakob Borg
4ade30e681 Merge branch 'pr/477'
* pr/477:
  Logo changed
2014-08-05 23:21:30 +02:00
Gilli Sigurdsson
4e03b4f191 Logo changed 2014-08-05 23:20:33 +02:00
Jakob Borg
bfe1d1d4ca Add Gilli 2014-08-05 23:19:11 +02:00
Jakob Borg
8918de85fd Correct memory usage in anonymous report 2014-08-05 23:13:55 +02:00
Jakob Borg
5e237aecae Reflect memory returned to OS in RAM Utilization 2014-08-05 22:14:11 +02:00
Jakob Borg
13291ad481 Tweak contribution guide 2014-08-05 20:54:53 +02:00
Jakob Borg
a47ee86bee Don't show 100 warnings for unknown repo at connect when once is enough 2014-08-05 20:26:05 +02:00
Jakob Borg
62d703f967 Show 100% complete status for nodes without any files to sync (fixes #453) 2014-08-05 20:16:25 +02:00
Jakob Borg
b2c196e5c7 Don't overwrite Node ID field with 'corrected' format 2014-08-05 19:47:29 +02:00
Jakob Borg
4be6a54bc0 Hide build version behind plus character (fixes #473) 2014-08-05 19:38:31 +02:00
Jakob Borg
8ce8476547 Exclude integration tests from normal go test 2014-08-05 15:50:05 +02:00
Jakob Borg
d82caf6bd4 Don't depend on a pretty printer just for testing 2014-08-05 15:43:29 +02:00
Jakob Borg
8ea1e302c3 Also expose ItemStarted events 2014-08-05 13:14:04 +02:00
Jakob Borg
a8799efa94 Don't reuse existing indexes, yet (fixes #463) 2014-08-05 12:20:50 +02:00
Jakob Borg
0cfac4e021 Start rewriting integration tests in Go instead of bash 2014-08-05 12:20:07 +02:00
Jakob Borg
f6c9642d72 Pull files in random-ish order again 2014-08-05 09:46:21 +02:00
Jakob Borg
5a07f9ddee Woops: don't consider all close()s to be failures... 2014-08-05 09:44:35 +02:00
Jakob Borg
9db75e91ac HTTP testing corrections 2014-08-05 09:38:38 +02:00
Jakob Borg
f288e00c37 Actually show Node ID in QR (fixes #471) 2014-08-04 22:53:37 +02:00
Jakob Borg
c9edd31993 Show pull errors, stop repo when not making progress (fixes #302) 2014-08-04 22:46:35 +02:00
Jakob Borg
5a7780ab5f Use Raleway font for headings 2014-08-04 22:46:29 +02:00
Jakob Borg
ac0fba99ad "52 or 56 characters" (fixes #466) 2014-08-04 22:11:44 +02:00
Jakob Borg
6f724a113c Use repo ID rather than path in header (fixes #425) 2014-08-03 21:58:36 +02:00
Jakob Borg
327cd4cb87 Fix statistics report preview (fixes #460) 2014-08-03 21:47:02 +02:00
Jakob Borg
25de3a2590 Also build for freebsd-386 (fixes #458) 2014-08-03 10:42:39 +02:00
Jakob Borg
06208a703a Implement -generate (fixes #459) 2014-08-03 09:41:08 +02:00
Jakob Borg
56afba6606 Only change the announce server when upgrading config version 2014-08-02 08:37:10 +02:00
Jakob Borg
d65bbf2113 Allow GET requests without CSRF 2014-08-02 08:19:10 +02:00
Jakob Borg
b8bfc9b732 Coveralls syncthing/syncthing 2014-08-02 08:18:55 +02:00
Jakob Borg
cec3bad373 Move calmh/syncthing -> syncthing/syncthing 2014-08-01 16:48:46 +02:00
Jakob Borg
9312e3c7de Config version 3: default to compression=true on nodes 2014-08-01 16:48:46 +02:00
Jakob Borg
43e7435c41 Call the darwin releases macosx instead 2014-08-01 16:30:28 +02:00
Jakob Borg
f34f5e41a4 Don't always run the tedious protocol tests 2014-08-01 16:30:13 +02:00
101 changed files with 3234 additions and 2228 deletions

View File

@@ -11,8 +11,8 @@ script:
- ./build.sh test-cov
after_success:
- goveralls -coverprofile=coverage.out -service=travis-ci -package=calmh/syncthing -repotoken="$COVERALS_TOKEN"
- goveralls -coverprofile=coverage.out -service=travis-ci -package=syncthing/syncthing -repotoken="$COVERALLS_TOKEN"
env:
global:
secure: "zEV2h2XtKHNLVdXJjM4LA/VjMfLVydm6goF+ARit+nOSGxGoH7f7jIdzJzhxgh7shKG93q61eLO1Tug+WBMYB2EpBuYnTB5AIMYhCDwNI8C4uBV6c3brHfcrie7MASNao8TID2QScASKNFFWvjv/i1Ccn5ztxdcQuhSsNjGZp8A="
secure: "TSPJDsokGCQhKLjgG3c58qHn8Qxhh4zEkWFf0XIOOY2nlDVzdgXDsC+Nq0YaP4106Ee4FgkSefsUTQV5lq/IyYW8elgqlgghjOtOi6RJa14eIS9Yy5Bkx6MXn0QfZX/lG+sy42pKSNk43y9GWx/qrt4nkfTtTvI5cXgwDGYdmX8="

View File

@@ -1,7 +1,40 @@
## Reporting Bugs
Please file bugs in the [Github Issue
Tracker](https://github.com/syncthing/syncthing/issues). Include at
least the following:
- What happened
- What did you expect to happen instead of what *did* happen, if it's
not crazy obvious
- What operating system, operating system version and version of
Syncthing you are running
- The same for other connected nodes, where relevant
- Screenshot if the issue concerns something visible in the GUI
- Console log entries, where possible and relevant
If you're not sure whether something is relevant, erring on the side of
too much information will never get you yelled at. :)
## Contributing Translations
All translations are done via
[Transifex](https://www.transifex.com/projects/p/syncthing/). If you
wish to contribute to a translation, just head over there and sign up.
Before every release, the language resources are updated from the
latest info on Transifex.
## Contributing Code
Please do contribute! If you want to contribute but are unsure where to
start, the [Contributions Needed
topic](http://discourse.syncthing.net/t/contributions-needed/49)
lists areas in need of attention.
topic](http://discourse.syncthing.net/t/49) lists areas in need of
attention. In general, any open issues are fair game!
## Licensing
@@ -16,8 +49,8 @@ to add yourself as a separate commit in your first pull request.
## Building
[See the
documentation](http://discourse.syncthing.net/t/building-syncthing/44)
[See the documentation](http://discourse.syncthing.net/t/44) on how to
get started with a build environment.
## Branches
@@ -44,7 +77,9 @@ Yes please!
## Style
`go fmt`
- `go fmt`
- Unix line breaks
## Documentation
@@ -53,4 +88,3 @@ Yes please!
## License
MIT

View File

@@ -4,8 +4,10 @@ Audrius Butkevicius <audrius.butkevicius@gmail.com>
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com>
Ben Sidhom <bsidhom@gmail.com>
Brandon Philips <brandon@ifup.org>
Gilli Sigurdsson <gilli@vx.is>
James Patterson <jamespatterson@operamail.com>
Jens Diemer <github.com@jensdiemer.de>
Philippe Schommers <philippe@schommers.be>
Ryan Sullivan <kayoticsully@gmail.com>
Tully Robinson <tully@tojr.org>
Veeti Paananen <veeti.paananen@rojekti.fi>

12
Godeps/Godeps.json generated
View File

@@ -1,5 +1,5 @@
{
"ImportPath": "github.com/calmh/syncthing",
"ImportPath": "github.com/syncthing/syncthing",
"GoVersion": "go1.3",
"Packages": [
"./cmd/..."
@@ -22,13 +22,13 @@
},
{
"ImportPath": "code.google.com/p/go.text/transform",
"Comment": "null-88",
"Rev": "1506dcc33592c369c3be7bd30b38f90445b86deb"
"Comment": "null-89",
"Rev": "df15baaf13e3f62b6b7a901e74caa3818a7c0a7c"
},
{
"ImportPath": "code.google.com/p/go.text/unicode/norm",
"Comment": "null-88",
"Rev": "1506dcc33592c369c3be7bd30b38f90445b86deb"
"Comment": "null-89",
"Rev": "df15baaf13e3f62b6b7a901e74caa3818a7c0a7c"
},
{
"ImportPath": "code.google.com/p/snappy-go/snappy",
@@ -41,7 +41,7 @@
},
{
"ImportPath": "github.com/calmh/xdr",
"Rev": "89d756f35ba26bcdd47ca25cf9795b3b8d1e1852"
"Rev": "694859acb207675085232438780db923ceb43e96"
},
{
"ImportPath": "github.com/juju/ratelimit",

View File

@@ -11,7 +11,6 @@
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
@@ -24,6 +23,8 @@ import (
"strconv"
"strings"
"unicode"
"code.google.com/p/go.text/internal/ucd"
)
func main() {
@@ -63,31 +64,7 @@ var localFiles = flag.Bool("local",
var logger = log.New(os.Stderr, "", log.Lshortfile)
// UnicodeData.txt has form:
// 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
// 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
// See http://unicode.org/reports/tr44/ for full explanation
// The fields:
const (
FCodePoint = iota
FName
FGeneralCategory
FCanonicalCombiningClass
FBidiClass
FDecompMapping
FDecimalValue
FDigitValue
FNumericValue
FBidiMirrored
FUnicode1Name
FISOComment
FSimpleUppercaseMapping
FSimpleLowercaseMapping
FSimpleTitlecaseMapping
NumField
MaxChar = 0x10FFFF // anything above this shouldn't exist
)
const MaxChar = 0x10FFFF // anything above this shouldn't exist
// Quick Check properties of runes allow us to quickly
// determine whether a rune may occur in a normal form.
@@ -232,7 +209,7 @@ func openReader(file string) (input io.ReadCloser) {
return
}
func parseDecomposition(s string, skipfirst bool) (a []rune, e error) {
func parseDecomposition(s string, skipfirst bool) (a []rune, err error) {
decomp := strings.Split(s, " ")
if len(decomp) > 0 && skipfirst {
decomp = decomp[1:]
@@ -247,56 +224,31 @@ func parseDecomposition(s string, skipfirst bool) (a []rune, e error) {
return a, nil
}
func parseCharacter(line string) {
field := strings.Split(line, ";")
if len(field) != NumField {
logger.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField)
}
x, err := strconv.ParseUint(field[FCodePoint], 16, 64)
point := int(x)
if err != nil {
logger.Fatalf("%.5s...: %s", line, err)
}
if point == 0 {
return // not interesting and we use 0 as unset
}
if point > MaxChar {
logger.Fatalf("%5s: Rune %X > MaxChar (%X)", line, point, MaxChar)
return
}
state := SNormal
switch {
case strings.Index(field[FName], ", First>") > 0:
state = SFirst
case strings.Index(field[FName], ", Last>") > 0:
state = SLast
}
firstChar := lastChar + 1
lastChar = rune(point)
if state != SLast {
firstChar = lastChar
}
x, err = strconv.ParseUint(field[FCanonicalCombiningClass], 10, 64)
if err != nil {
logger.Fatalf("%U: bad ccc field: %s", int(x), err)
}
ccc := uint8(x)
decmap := field[FDecompMapping]
exp, e := parseDecomposition(decmap, false)
isCompat := false
if e != nil {
if len(decmap) > 0 {
exp, e = parseDecomposition(decmap, true)
if e != nil {
logger.Fatalf(`%U: bad decomp |%v|: "%s"`, int(x), decmap, e)
func loadUnicodeData() {
f := openReader("UnicodeData.txt")
defer f.Close()
p := ucd.New(f)
for p.Next() {
r := p.Rune(ucd.CodePoint)
char := &chars[r]
char.ccc = uint8(p.Uint(ucd.CanonicalCombiningClass))
decmap := p.String(ucd.DecompMapping)
exp, err := parseDecomposition(decmap, false)
isCompat := false
if err != nil {
if len(decmap) > 0 {
exp, err = parseDecomposition(decmap, true)
if err != nil {
logger.Fatalf(`%U: bad decomp |%v|: "%s"`, r, decmap, err)
}
isCompat = true
}
isCompat = true
}
}
for i := firstChar; i <= lastChar; i++ {
char := &chars[i]
char.name = field[FName]
char.codePoint = i
char.name = p.String(ucd.Name)
char.codePoint = r
char.forms[FCompatibility].decomp = exp
if !isCompat {
char.forms[FCanonical].decomp = exp
@@ -306,24 +258,9 @@ func parseCharacter(line string) {
if len(decmap) > 0 {
char.forms[FCompatibility].decomp = exp
}
char.ccc = ccc
char.state = SMissing
if i == lastChar {
char.state = state
}
}
return
}
func loadUnicodeData() {
f := openReader("UnicodeData.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
parseCharacter(scanner.Text())
}
if scanner.Err() != nil {
logger.Fatal(scanner.Err())
if err := p.Err(); err != nil {
logger.Fatal(err)
}
}
@@ -354,47 +291,22 @@ func compactCCC() {
}
}
var singlePointRe = regexp.MustCompile(`^([0-9A-F]+) *$`)
// CompositionExclusions.txt has form:
// 0958 # ...
// See http://unicode.org/reports/tr44/ for full explanation
func parseExclusion(line string) int {
comment := strings.Index(line, "#")
if comment >= 0 {
line = line[0:comment]
}
if len(line) == 0 {
return 0
}
matches := singlePointRe.FindStringSubmatch(line)
if len(matches) != 2 {
logger.Fatalf("%s: %d matches (expected 1)\n", line, len(matches))
}
point, err := strconv.ParseUint(matches[1], 16, 64)
if err != nil {
logger.Fatalf("%.5s...: %s", line, err)
}
return int(point)
}
func loadCompositionExclusions() {
f := openReader("CompositionExclusions.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
point := parseExclusion(scanner.Text())
if point == 0 {
continue
}
c := &chars[point]
p := ucd.New(f)
for p.Next() {
c := &chars[p.Rune(0)]
if c.excludeInComp {
logger.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint)
}
c.excludeInComp = true
}
if scanner.Err() != nil {
log.Fatal(scanner.Err())
if e := p.Err(); e != nil {
logger.Fatal(e)
}
}
@@ -988,8 +900,6 @@ func verifyComputed() {
}
}
var qcRe = regexp.MustCompile(`([0-9A-F\.]+) *; (NF.*_QC); ([YNM]) #.*`)
// Use values in DerivedNormalizationProps.txt to compare against the
// values we computed.
// DerivedNormalizationProps.txt has form:
@@ -999,27 +909,13 @@ var qcRe = regexp.MustCompile(`([0-9A-F\.]+) *; (NF.*_QC); ([YNM]) #.*`)
func testDerived() {
f := openReader("DerivedNormalizationProps.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
qc := qcRe.FindStringSubmatch(line)
if qc == nil {
continue
}
rng := strings.Split(qc[1], "..")
i, err := strconv.ParseUint(rng[0], 16, 64)
if err != nil {
log.Fatal(err)
}
j := i
if len(rng) > 1 {
j, err = strconv.ParseUint(rng[1], 16, 64)
if err != nil {
log.Fatal(err)
}
}
p := ucd.New(f)
for p.Next() {
r := p.Rune(0)
c := &chars[r]
var ftype, mode int
qt := strings.TrimSpace(qc[2])
qt := p.String(1)
switch qt {
case "NFC_QC":
ftype, mode = FCanonical, MComposed
@@ -1030,10 +926,10 @@ func testDerived() {
case "NFKD_QC":
ftype, mode = FCompatibility, MDecomposed
default:
log.Fatalf(`Unexpected quick check type "%s"`, qt)
continue
}
var qr QCResult
switch qc[3] {
switch p.String(2) {
case "Y":
qr = QCYes
case "N":
@@ -1041,27 +937,15 @@ func testDerived() {
case "M":
qr = QCMaybe
default:
log.Fatalf(`Unexpected quick check value "%s"`, qc[3])
log.Fatalf(`Unexpected quick check value "%s"`, p.String(2))
}
var lastFailed bool
// Verify current
for ; i <= j; i++ {
c := &chars[int(i)]
c.forms[ftype].verified[mode] = true
curqr := c.forms[ftype].quickCheck[mode]
if curqr != qr {
if !lastFailed {
logger.Printf("%s: %.4X..%.4X -- %s\n",
qt, int(i), int(j), line[0:50])
}
logger.Printf("%U: FAILED %s (was %v need %v)\n",
int(i), qt, curqr, qr)
lastFailed = true
}
if got := c.forms[ftype].quickCheck[mode]; got != qr {
logger.Printf("%U: FAILED %s (was %v need %v)\n", r, qt, got, qr)
}
c.forms[ftype].verified[mode] = true
}
if scanner.Err() != nil {
logger.Fatal(scanner.Err())
if err := p.Err(); err != nil {
logger.Fatal(err)
}
// Any unspecified value must be QCYes. Verify this.
for i, c := range chars {

View File

@@ -0,0 +1 @@
coverage.out

19
Godeps/_workspace/src/github.com/calmh/xdr/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,19 @@
language: go
go:
- tip
install:
- export PATH=$PATH:$HOME/gopath/bin
- go get code.google.com/p/go.tools/cmd/cover
- go get github.com/mattn/goveralls
script:
- ./generate.sh
- go test -coverprofile=coverage.out
after_success:
- goveralls -coverprofile=coverage.out -service=travis-ci -package=calmh/xdr -repotoken="$COVERALLS_TOKEN"
env:
global:
secure: SmgnrGfp2zLrA44ChRMpjPeujubt9veZ8Fx/OseMWECmacyV5N/TuDhzIbwo6QwV4xB0sBacoPzvxQbJRVjNKsPiSu72UbcQmQ7flN4Tf7nW09tSh1iW8NgrpBCq/3UYLoBu2iPBEBKm93IK0aGNAKs6oEkB0fU27iTVBwiTXOY=

19
Godeps/_workspace/src/github.com/calmh/xdr/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (C) 2014 Jakob Borg.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

12
Godeps/_workspace/src/github.com/calmh/xdr/README.md generated vendored Normal file
View File

@@ -0,0 +1,12 @@
xdr
===
[![Build Status](https://img.shields.io/travis/calmh/xdr.svg?style=flat)](https://travis-ci.org/calmh/xdr)
[![Coverage Status](https://img.shields.io/coveralls/calmh/xdr.svg?style=flat)](https://coveralls.io/r/calmh/xdr?branch=master)
[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat)](http://godoc.org/github.com/calmh/xdr)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
This is an XDR encoding/decoding library. It uses code generation and
not reflection. It supports the IPDR bastardized XDR format when built
with `-tags ipdr`.

View File

@@ -5,8 +5,12 @@ package xdr_test
import (
"bytes"
"math/rand"
"reflect"
"testing"
"testing/quick"
"github.com/calmh/xdr"
)
// Contains all supported types
@@ -22,6 +26,25 @@ type TestStruct struct {
UI64 uint64
BS []byte
S string
C Opaque
}
type Opaque [32]byte
func (u *Opaque) encodeXDR(w *xdr.Writer) (int, error) {
return w.WriteRaw(u[:])
}
func (u *Opaque) decodeXDR(r *xdr.Reader) (int, error) {
return r.ReadRaw(u[:])
}
func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value {
var u Opaque
for i := range u[:] {
u[i] = byte(rand.Int())
}
return reflect.ValueOf(u)
}
func TestEncDec(t *testing.T) {
@@ -39,7 +62,7 @@ func TestEncDec(t *testing.T) {
t0.I32 != t1.I32 || t0.UI32 != t1.UI32 ||
t0.I64 != t1.I64 || t0.UI64 != t1.UI64 ||
bytes.Compare(t0.BS, t1.BS) != 0 ||
t0.S != t1.S {
t0.S != t1.S || t0.C != t1.C {
t.Logf("%#v", t0)
t.Logf("%#v", t1)
return false

View File

@@ -52,6 +52,8 @@ TestStruct Structure:
\ S (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opaque |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct TestStruct {
@@ -66,6 +68,7 @@ struct TestStruct {
unsigned hyper UI64;
opaque BS<>;
string S<>;
Opaque C;
}
*/
@@ -98,6 +101,10 @@ func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
xw.WriteUint64(o.UI64)
xw.WriteBytes(o.BS)
xw.WriteString(o.S)
_, err := o.C.encodeXDR(xw)
if err != nil {
return xw.Tot(), err
}
return xw.Tot(), xw.Error()
}
@@ -124,5 +131,6 @@ func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
o.UI64 = xr.ReadUint64()
o.BS = xr.ReadBytes()
o.S = xr.ReadString()
(&o.C).decodeXDR(xr)
return xr.Error()
}

View File

@@ -67,7 +67,7 @@ func TestReadBytesMaxInto(t *testing.T) {
}
}
func TestReadBytesMaxIntoNil(t *testing.T) {
func TestReadStringMax(t *testing.T) {
for tot := 42; tot < 72; tot++ {
for max := 0; max < 128; max++ {
var b = new(bytes.Buffer)
@@ -77,8 +77,8 @@ func TestReadBytesMaxIntoNil(t *testing.T) {
var toWrite = make([]byte, tot)
w.WriteBytes(toWrite)
var bs = r.ReadBytesMaxInto(max, nil)
var read = len(bs)
var str = r.ReadStringMax(max)
var read = len(str)
if max == 0 || tot <= max {
if read != tot {

View File

@@ -1,9 +1,9 @@
syncthing
=========
[![Build Status](https://img.shields.io/travis/calmh/syncthing.svg?style=flat)](https://travis-ci.org/calmh/syncthing)
[![Coverage Status](https://img.shields.io/coveralls/calmh/syncthing.svg?style=flat)](https://coveralls.io/r/calmh/syncthing?branch=master)
[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat)](http://godoc.org/github.com/calmh/syncthing)
[![Build Status](https://img.shields.io/travis/syncthing/syncthing.svg?style=flat)](https://travis-ci.org/syncthing/syncthing)
[![Coverage Status](https://img.shields.io/coveralls/syncthing/syncthing.svg?style=flat)](https://coveralls.io/r/syncthing/syncthing?branch=master)
[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat)](http://godoc.org/github.com/syncthing/syncthing)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
This is the `syncthing` project. The following are the project goals:
@@ -12,7 +12,7 @@ This is the `syncthing` project. The following are the project goals:
number of collaborating nodes. The protocol should be well defined,
unambiguous, easily understood, free to use, efficient, secure and
language neutral. This is the [Block Exchange
Protocol](https://github.com/calmh/syncthing/blob/master/protocol/PROTOCOL.md).
Protocol](https://github.com/syncthing/syncthing/blob/master/protocol/PROTOCOL.md).
2. Provide the reference implementation to demonstrate the usability of
said protocol. This is the `syncthing` utility. It is the hope that
@@ -50,4 +50,4 @@ under the [Creative Commons Attribution 4.0 International
License](http://creativecommons.org/licenses/by/4.0/).
All code is licensed under the [MIT
License](https://github.com/calmh/syncthing/blob/master/LICENSE).
License](https://github.com/syncthing/syncthing/blob/master/LICENSE).

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1008 B

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/logo-text-128.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/logo-text-256.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
assets/logo-text-64.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -4,7 +4,11 @@ export COPYFILE_DISABLE=true
export GO386=387 # Don't use SSE on 32 bit builds
distFiles=(README.md LICENSE CONTRIBUTORS) # apart from the binary itself
# replace "...-12-g123abc" with "...+12-g123abc" to remain semver compatible-ish
version=$(git describe --always --dirty)
version=$(echo "$version" | sed 's/-\([0-9]\{1,3\}-g[0-9a-f]\{5,10\}\)/+\1/')
date=$(git show -s --format=%ct)
user=$(whoami)
host=$(hostname)
@@ -47,7 +51,7 @@ test-cov() {
test() {
check
go vet ./...
godep go test -cpu=1,2,4 ./...
godep go test -cpu=1,2,4 $* ./...
}
sign() {
@@ -93,7 +97,6 @@ setup() {
go get -v code.google.com/p/go.tools/cmd/vet
go get -v github.com/mattn/goveralls
go get -v github.com/tools/godep
GOPATH="$GOPATH:$(godep path)" go get -v -t ./...
}
xdr() {
@@ -111,7 +114,7 @@ translate() {
transifex() {
pushd gui
go run ../cmd/transifexdl/main.go > valid-langs.js
go run ../cmd/transifexdl/main.go
popd
assets
}
@@ -133,7 +136,7 @@ case "$1" in
;;
test)
test
test -short
;;
test-cov)
@@ -142,28 +145,28 @@ case "$1" in
tar)
rm -f *.tar.gz *.zip
test || exit 1
test -short || exit 1
assets
build
eval $(go env)
name="syncthing-$GOOS-$GOARCH-$version"
name="syncthing-${GOOS/darwin/macosx}-$GOARCH-$version"
tarDist "$name"
;;
all)
rm -f *.tar.gz *.zip
test || exit 1
test -short || exit 1
assets
for os in darwin-amd64 linux-386 linux-amd64 freebsd-amd64 windows-amd64 windows-386 solaris-amd64 ; do
for os in darwin-amd64 freebsd-amd64 freebsd-386 linux-amd64 linux-386 windows-amd64 windows-386 solaris-amd64 ; do
export GOOS=${os%-*}
export GOARCH=${os#*-}
build
name="syncthing-$os-$version"
name="syncthing-${os/darwin/macosx}-$version"
case $GOOS in
windows)
zipDist "$name"
@@ -202,7 +205,7 @@ case "$1" in
tag=$(git describe)
shopt -s nullglob
for f in *.tar.gz *.zip *.asc ; do
relup calmh/syncthing "$tag" "$f"
relup syncthing/syncthing "$tag" "$f"
done
;;

View File

@@ -10,8 +10,8 @@ import (
"log"
"os"
"github.com/calmh/syncthing/files"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/files"
"github.com/syncthing/syncthing/protocol"
"github.com/syndtr/goleveldb/leveldb"
)

View File

@@ -26,13 +26,13 @@ import (
"crypto/tls"
"code.google.com/p/go.crypto/bcrypt"
"github.com/calmh/syncthing/auto"
"github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/events"
"github.com/calmh/syncthing/logger"
"github.com/calmh/syncthing/model"
"github.com/calmh/syncthing/protocol"
"github.com/calmh/syncthing/upgrade"
"github.com/syncthing/syncthing/auto"
"github.com/syncthing/syncthing/config"
"github.com/syncthing/syncthing/events"
"github.com/syncthing/syncthing/logger"
"github.com/syncthing/syncthing/model"
"github.com/syncthing/syncthing/protocol"
"github.com/syncthing/syncthing/upgrade"
"github.com/vitrun/qart/qr"
)
@@ -57,7 +57,7 @@ const (
func init() {
l.AddHandler(logger.LevelWarn, showGuiError)
sub := events.Default.Subscribe(^events.EventType(events.ItemStarted | events.ItemCompleted))
sub := events.Default.Subscribe(events.AllEvents)
eventSub = events.NewBufferedSubscription(sub, 1000)
}
@@ -384,7 +384,7 @@ func restGetSystem(w http.ResponseWriter, r *http.Request) {
res["myID"] = myID.String()
res["goroutines"] = runtime.NumGoroutine()
res["alloc"] = m.Alloc
res["sys"] = m.Sys
res["sys"] = m.Sys - m.HeapReleased
res["tilde"] = expandTilde("~")
if cfg.Options.GlobalAnnEnabled && discoverer != nil {
res["extAnnounceOK"] = discoverer.ExtAnnounceOK()
@@ -502,6 +502,7 @@ func restGetLang(w http.ResponseWriter, r *http.Request) {
langs = append(langs, l[:2])
}
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(langs)
}
@@ -514,7 +515,7 @@ func restPostUpgrade(w http.ResponseWriter, r *http.Request) {
}
if upgrade.CompareVersions(rel.Tag, Version) == 1 {
err = upgrade.UpgradeTo(rel)
err = upgrade.UpgradeTo(rel, GoArchExtra)
if err != nil {
l.Warnln(err)
http.Error(w, err.Error(), 500)
@@ -526,8 +527,8 @@ func restPostUpgrade(w http.ResponseWriter, r *http.Request) {
}
func getQR(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
text := r.FormValue("text")
var qs = r.URL.Query()
var text = qs.Get("text")
code, err := qr.Encode(text, qr.M)
if err != nil {
http.Error(w, "Invalid", 500)

View File

@@ -12,7 +12,7 @@ import (
"sync"
"time"
"github.com/calmh/syncthing/osutil"
"github.com/syncthing/syncthing/osutil"
)
var csrfTokens []string
@@ -43,6 +43,12 @@ func csrfMiddleware(prefix string, next http.Handler) http.Handler {
return
}
if r.Method == "GET" {
// Allow GET requests unconditionally
next.ServeHTTP(w, r)
return
}
// Verify the CSRF token
token := r.Header.Get("X-CSRF-Token")
if !validCsrfToken(token) {

View File

@@ -26,16 +26,16 @@ import (
"strings"
"time"
"github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/discover"
"github.com/calmh/syncthing/events"
"github.com/calmh/syncthing/logger"
"github.com/calmh/syncthing/model"
"github.com/calmh/syncthing/osutil"
"github.com/calmh/syncthing/protocol"
"github.com/calmh/syncthing/upgrade"
"github.com/calmh/syncthing/upnp"
"github.com/juju/ratelimit"
"github.com/syncthing/syncthing/config"
"github.com/syncthing/syncthing/discover"
"github.com/syncthing/syncthing/events"
"github.com/syncthing/syncthing/logger"
"github.com/syncthing/syncthing/model"
"github.com/syncthing/syncthing/osutil"
"github.com/syncthing/syncthing/protocol"
"github.com/syncthing/syncthing/upgrade"
"github.com/syncthing/syncthing/upnp"
"github.com/syndtr/goleveldb/leveldb"
)
@@ -47,6 +47,7 @@ var (
BuildHost = "unknown"
BuildUser = "unknown"
LongVersion string
GoArchExtra string // "", "v5", "v6", "v7"
)
var l = logger.DefaultLogger
@@ -54,7 +55,7 @@ var l = logger.DefaultLogger
func init() {
if Version != "unknown-dev" {
// If not a generic dev build, version string should come from git describe
exp := regexp.MustCompile(`^v\d+\.\d+\.\d+(-beta\d+)?(-\d+-g[0-9a-f]+)?(-dirty)?$`)
exp := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\+\d+-g[0-9a-f]+)?(-dirty)?$`)
if !exp.MatchString(Version) {
l.Fatalf("Invalid version string %q;\n\tdoes not match regexp %v", Version, exp)
}
@@ -135,12 +136,16 @@ func main() {
var showVersion bool
var doUpgrade bool
var doUpgradeCheck bool
var generateDir string
var noBrowser bool
flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster")
flag.BoolVar(&showVersion, "version", false, "Show version")
flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade")
flag.BoolVar(&doUpgradeCheck, "upgrade-check", false, "Check for available upgrade")
flag.BoolVar(&noBrowser, "no-browser", false, "Do not start browser")
flag.IntVar(&logFlags, "logflags", logFlags, "Set log flags")
flag.StringVar(&generateDir, "generate", "", "Generate key in specified dir")
flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
flag.Parse()
@@ -151,10 +156,29 @@ func main() {
l.SetFlags(logFlags)
var err error
lockPort, err = getLockPort()
if err != nil {
l.Fatalln("Opening lock port:", err)
if generateDir != "" {
dir := expandTilde(generateDir)
info, err := os.Stat(dir)
l.FatalErr(err)
if !info.IsDir() {
l.Fatalln(dir, "is not a directory")
}
cert, err := loadCert(dir, "")
if err == nil {
l.Warnln("Key exists; will not overwrite.")
l.Infoln("Node ID:", protocol.NewNodeID(cert.Certificate[0]))
return
}
newCertificate(dir, "")
cert, err = loadCert(dir, "")
l.FatalErr(err)
if err == nil {
l.Infoln("Node ID:", protocol.NewNodeID(cert.Certificate[0]))
}
return
}
if doUpgrade || doUpgradeCheck {
@@ -171,7 +195,7 @@ func main() {
l.Infof("Upgrade available (current %q < latest %q)", Version, rel.Tag)
if doUpgrade {
err = upgrade.UpgradeTo(rel)
err = upgrade.UpgradeTo(rel, GoArchExtra)
if err != nil {
l.Fatalln("Upgrade:", err) // exits 1
}
@@ -182,6 +206,12 @@ func main() {
}
}
var err error
lockPort, err = getLockPort()
if err != nil {
l.Fatalln("Opening lock port:", err)
}
if len(os.Getenv("GOGC")) == 0 {
debug.SetGCPercent(25)
}
@@ -393,12 +423,21 @@ nextRepo:
if err != nil {
l.Fatalln("Cannot start GUI:", err)
}
if cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 {
if !noBrowser && cfg.Options.StartBrowser && len(os.Getenv("STRESTART")) == 0 {
openURL(fmt.Sprintf("%s://%s:%d", proto, hostOpen, addr.Port))
}
}
}
// Clear out old indexes for other nodes. Otherwise we'll start up and
// start needing a bunch of files which are nowhere to be found. This
// needs to be changed when we correctly do persistent indexes.
for _, repoCfg := range cfg.Repositories {
for _, node := range repoCfg.NodeIDs() {
m.Index(node, repoCfg.ID, nil)
}
}
// Walk the repository and update the local model before establishing any
// connections to other nodes.

View File

@@ -39,7 +39,7 @@ func certSeed(bs []byte) int64 {
}
func newCertificate(dir string, prefix string) {
l.Infoln("Generating RSA certificate and key...")
l.Infoln("Generating RSA key and certificate...")
priv, err := rsa.GenerateKey(rand.Reader, tlsRSABits)
l.FatalErr(err)
@@ -67,11 +67,9 @@ func newCertificate(dir string, prefix string) {
l.FatalErr(err)
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
l.Okln("Created RSA certificate file")
keyOut, err := os.OpenFile(filepath.Join(dir, prefix+"key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
l.FatalErr(err)
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
keyOut.Close()
l.Okln("Created RSA key file")
}

View File

@@ -11,7 +11,7 @@ import (
"strings"
"time"
"github.com/calmh/syncthing/model"
"github.com/syncthing/syncthing/model"
)
// Current version number of the usage report, for acceptance purposes. If
@@ -51,7 +51,7 @@ func reportData(m *model.Model) map[string]interface{} {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
res["memoryUsageMiB"] = mem.Sys / 1024 / 1024
res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
var perf float64
for i := 0; i < 5; i++ {

View File

@@ -9,10 +9,13 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"sort"
"strings"
)
type stat struct {
@@ -31,6 +34,12 @@ func main() {
log.Fatal("Need environment variables TRANSIFEX_USER and TRANSIFEX_PASS")
}
curValidLangs := map[string]bool{}
for _, lang := range loadValidLangs() {
curValidLangs[lang] = true
}
log.Println(curValidLangs)
resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/stats")
var stats map[string]stat
@@ -43,10 +52,12 @@ func main() {
var langs []string
for code, stat := range stats {
shortCode := code[:2]
if pct := 100 * stat.Translated / (stat.Translated + stat.Untranslated); pct < 95 {
log.Printf("Skipping language %q (too low completion ratio %d%%)", shortCode, pct)
os.Remove("lang-" + shortCode + ".json")
continue
if !curValidLangs[shortCode] {
if pct := 100 * stat.Translated / (stat.Translated + stat.Untranslated); pct < 95 {
log.Printf("Skipping language %q (too low completion ratio %d%%)", shortCode, pct)
os.Remove("lang-" + shortCode + ".json")
continue
}
}
langs = append(langs, shortCode)
@@ -72,9 +83,18 @@ func main() {
fd.Close()
}
saveValidLangs(langs)
}
func saveValidLangs(langs []string) {
sort.Strings(langs)
fmt.Print("var validLangs = ")
json.NewEncoder(os.Stdout).Encode(langs)
fd, err := os.Create("valid-langs.js")
if err != nil {
log.Fatal(err)
}
fmt.Fprint(fd, "var validLangs = ")
json.NewEncoder(fd).Encode(langs)
fd.Close()
}
func userPass() (string, string) {
@@ -97,3 +117,27 @@ func req(url string) *http.Response {
}
return resp
}
func loadValidLangs() []string {
fd, err := os.Open("valid-langs.js")
if err != nil {
log.Fatal(err)
}
defer fd.Close()
bs, err := ioutil.ReadAll(fd)
if err != nil {
log.Fatal(err)
}
var langs []string
exp := regexp.MustCompile(`\[([a-z",]+)\]`)
if matches := exp.FindSubmatch(bs); len(matches) == 2 {
langs = strings.Split(string(matches[1]), ",")
for i := range langs {
// Remove quotes
langs[i] = langs[i][1 : len(langs[i])-1]
}
}
return langs
}

View File

@@ -10,12 +10,14 @@ import (
"encoding/json"
"log"
"os"
"regexp"
"strings"
"code.google.com/p/go.net/html"
)
var trans = make(map[string]string)
var attrRe = regexp.MustCompile(`\{\{'([^']+)'\s+\|\s+translate\}\}`)
func generalNode(n *html.Node) {
translate := false
@@ -24,6 +26,10 @@ func generalNode(n *html.Node) {
if a.Key == "translate" {
translate = true
break
} else {
if matches := attrRe.FindStringSubmatch(a.Val); len(matches) == 2 {
translation(matches[1])
}
}
}
} else if n.Type == html.TextNode {
@@ -44,12 +50,7 @@ func generalNode(n *html.Node) {
func inTranslate(n *html.Node) {
if n.Type == html.TextNode {
v := strings.TrimSpace(n.Data)
if _, ok := trans[v]; !ok {
av := strings.Replace(v, "{%", "{{", -1)
av = strings.Replace(av, "%}", "}}", -1)
trans[v] = av
}
translation(n.Data)
} else {
log.Println("translate node with non-text child <")
log.Println(n)
@@ -60,6 +61,15 @@ func inTranslate(n *html.Node) {
}
}
func translation(v string) {
v = strings.TrimSpace(v)
if _, ok := trans[v]; !ok {
av := strings.Replace(v, "{%", "{{", -1)
av = strings.Replace(av, "%}", "}}", -1)
trans[v] = av
}
}
func main() {
fd, err := os.Open(os.Args[1])
if err != nil {

View File

@@ -15,14 +15,14 @@ import (
"strconv"
"code.google.com/p/go.crypto/bcrypt"
"github.com/calmh/syncthing/logger"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/logger"
"github.com/syncthing/syncthing/protocol"
)
var l = logger.DefaultLogger
type Configuration struct {
Version int `xml:"version,attr" default:"2"`
Version int `xml:"version,attr" default:"3"`
Repositories []RepositoryConfiguration `xml:"repository"`
Nodes []NodeConfiguration `xml:"node"`
GUI GUIConfiguration `xml:"gui"`
@@ -296,6 +296,11 @@ func Load(rd io.Reader, myID protocol.NodeID) (Configuration, error) {
convertV1V2(&cfg)
}
// Upgrade to v3 configuration if appropriate
if cfg.Version == 2 {
convertV2V3(&cfg)
}
// Hash old cleartext passwords
if len(cfg.GUI.Password) > 0 && cfg.GUI.Password[0] != '$' {
hash, err := bcrypt.GenerateFromPassword([]byte(cfg.GUI.Password), 0)
@@ -333,13 +338,24 @@ func Load(rd io.Reader, myID protocol.NodeID) (Configuration, error) {
}
}
return cfg, err
}
func convertV2V3(cfg *Configuration) {
// In previous versions, compression was always on. When upgrading, enable
// compression on all existing new. New nodes will get compression on by
// default by the GUI.
for i := range cfg.Nodes {
cfg.Nodes[i].Compression = true
}
// The global discovery format and port number changed in v0.9. Having the
// default announce server but old port number is guaranteed to be legacy.
if cfg.Options.GlobalAnnServer == "announce.syncthing.net:22025" {
cfg.Options.GlobalAnnServer = "announce.syncthing.net:22026"
}
return cfg, err
cfg.Version = 3
}
func convertV1V2(cfg *Configuration) {

View File

@@ -11,7 +11,7 @@ import (
"reflect"
"testing"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/protocol"
)
var node1, node2, node3, node4 protocol.NodeID
@@ -91,7 +91,21 @@ func TestNodeConfig(t *testing.T) {
</configuration>
`)
for i, data := range [][]byte{v1data, v2data} {
v3data := []byte(`
<configuration version="3">
<repository id="test" directory="~/Sync" ro="true" ignorePerms="false">
<node id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" compression="false"></node>
<node id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" compression="false"></node>
</repository>
<node id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" name="node one" compression="true">
<address>a</address>
</node>
<node id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" name="node two" compression="true">
<address>b</address>
</node>
</configuration>`)
for i, data := range [][]byte{v1data, v2data, v3data} {
cfg, err := Load(bytes.NewReader(data), node1)
if err != nil {
t.Error(err)
@@ -107,20 +121,22 @@ func TestNodeConfig(t *testing.T) {
}
expectedNodes := []NodeConfiguration{
{
NodeID: node1,
Name: "node one",
Addresses: []string{"a"},
NodeID: node1,
Name: "node one",
Addresses: []string{"a"},
Compression: true,
},
{
NodeID: node4,
Name: "node two",
Addresses: []string{"b"},
NodeID: node4,
Name: "node two",
Addresses: []string{"b"},
Compression: true,
},
}
expectedNodeIDs := []protocol.NodeID{node1, node4}
if cfg.Version != 2 {
t.Errorf("%d: Incorrect version %d != 2", i, cfg.Version)
if cfg.Version != 3 {
t.Errorf("%d: Incorrect version %d != 3", i, cfg.Version)
}
if !reflect.DeepEqual(cfg.Repositories, expectedRepos) {
t.Errorf("%d: Incorrect Repositories\n A: %#v\n E: %#v", i, cfg.Repositories, expectedRepos)
@@ -222,16 +238,19 @@ func TestNodeAddressesDynamic(t *testing.T) {
name, _ := os.Hostname()
expected := []NodeConfiguration{
{
NodeID: node1,
Addresses: []string{"dynamic"},
NodeID: node1,
Addresses: []string{"dynamic"},
Compression: true,
},
{
NodeID: node2,
Addresses: []string{"dynamic"},
NodeID: node2,
Addresses: []string{"dynamic"},
Compression: true,
},
{
NodeID: node3,
Addresses: []string{"dynamic"},
NodeID: node3,
Addresses: []string{"dynamic"},
Compression: true,
},
{
NodeID: node4,
@@ -252,7 +271,7 @@ func TestNodeAddressesDynamic(t *testing.T) {
func TestNodeAddressesStatic(t *testing.T) {
data := []byte(`
<configuration version="2">
<configuration version="3">
<node id="AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ">
<address>192.0.2.1</address>
<address>192.0.2.2</address>

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -14,9 +14,9 @@ import (
"sync"
"time"
"github.com/calmh/syncthing/beacon"
"github.com/calmh/syncthing/events"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/beacon"
"github.com/syncthing/syncthing/events"
"github.com/syncthing/syncthing/protocol"
)
type Discoverer struct {

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -23,7 +23,6 @@ const (
LocalIndexUpdated
RemoteIndexUpdated
ItemStarted
ItemCompleted
StateChanged
AllEvents = ^EventType(0)

View File

@@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/calmh/syncthing/events"
"github.com/syncthing/syncthing/events"
)
var timeout = 100 * time.Millisecond

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -2,11 +2,12 @@ package files
import (
"bytes"
"runtime"
"sort"
"sync"
"github.com/calmh/syncthing/lamport"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/lamport"
"github.com/syncthing/syncthing/protocol"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
@@ -121,6 +122,8 @@ type deletionHandler func(db dbReader, batch dbWriter, repo, node, name []byte,
type fileIterator func(f protocol.FileInfo) bool
func ldbGenericReplace(db *leveldb.DB, repo, node []byte, fs []protocol.FileInfo, deleteFn deletionHandler) uint64 {
defer runtime.GC()
sort.Sort(fileList(fs)) // sort list on name, same as on disk
start := nodeKey(repo, node, nil) // before all repo/node files
@@ -246,6 +249,8 @@ func ldbReplaceWithDelete(db *leveldb.DB, repo, node []byte, fs []protocol.FileI
}
func ldbUpdate(db *leveldb.DB, repo, node []byte, fs []protocol.FileInfo) uint64 {
defer runtime.GC()
batch := new(leveldb.Batch)
snap, err := db.GetSnapshot()
if err != nil {
@@ -414,6 +419,8 @@ func ldbWithHave(db *leveldb.DB, repo, node []byte, fn fileIterator) {
}
func ldbWithAllRepo(db *leveldb.DB, repo []byte, fn func(node []byte, f protocol.FileInfo) bool) {
defer runtime.GC()
start := nodeKey(repo, nil, nil) // before all repo/node files
limit := nodeKey(repo, protocol.LocalNodeID[:], []byte{0xff, 0xff, 0xff, 0xff}) // after all repo/node files
snap, err := db.GetSnapshot()
@@ -530,6 +537,8 @@ func ldbGetGlobal(db *leveldb.DB, repo, file []byte) protocol.FileInfo {
}
func ldbWithGlobal(db *leveldb.DB, repo []byte, fn fileIterator) {
defer runtime.GC()
start := globalKey(repo, nil)
limit := globalKey(repo, []byte{0xff, 0xff, 0xff, 0xff})
snap, err := db.GetSnapshot()
@@ -597,6 +606,8 @@ func ldbAvailability(db *leveldb.DB, repo, file []byte) []protocol.NodeID {
}
func ldbWithNeed(db *leveldb.DB, repo, node []byte, fn fileIterator) {
defer runtime.GC()
start := globalKey(repo, nil)
limit := globalKey(repo, []byte{0xff, 0xff, 0xff, 0xff})
snap, err := db.GetSnapshot()

View File

@@ -8,8 +8,8 @@ package files
import (
"sync"
"github.com/calmh/syncthing/lamport"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/lamport"
"github.com/syncthing/syncthing/protocol"
"github.com/syndtr/goleveldb/leveldb"
)

View File

@@ -9,9 +9,9 @@ import (
"sort"
"testing"
"github.com/calmh/syncthing/files"
"github.com/calmh/syncthing/lamport"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/files"
"github.com/syncthing/syncthing/lamport"
"github.com/syncthing/syncthing/protocol"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/storage"
)

View File

@@ -168,6 +168,9 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
OutBytesTotal: 0,
Address: arg.data.addr,
};
$scope.completion[arg.data.id] = {
_total: 100,
};
}
});
@@ -284,6 +287,11 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
$scope.config.Options.ListenStr = $scope.config.Options.ListenAddress.join(', ');
$scope.nodes = $scope.config.Nodes;
$scope.nodes.forEach(function (nodeCfg) {
$scope.completion[nodeCfg.NodeID] = {
_total: 100,
};
});
$scope.nodes.sort(nodeCompare);
$scope.repos = repoMap($scope.config.Repositories);
@@ -506,8 +514,6 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
$scope.restart = function () {
restarting = true;
$scope.restartingTitle = "Restarting"
$scope.restartingBody = "Syncthing is restarting."
$('#restarting').modal({backdrop: 'static', keyboard: false});
$http.post(urlbase + '/restart');
$scope.configInSync = true;
@@ -529,14 +535,13 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
};
$scope.upgrade = function () {
$scope.restartingTitle = "Upgrading"
$scope.restartingBody = "Syncthing is upgrading."
$('#restarting').modal({backdrop: 'static', keyboard: false});
restarting = true;
$('#upgrading').modal({backdrop: 'static', keyboard: false});
$http.post(urlbase + '/upgrade').success(function () {
restarting = true;
$scope.restartingBody = "Syncthing is restarting into the new version."
$('#restarting').modal({backdrop: 'static', keyboard: false});
$('#upgrading').modal('hide');
}).error(function () {
$('#restarting').modal('hide');
$('#upgrading').modal('hide');
});
};
@@ -790,6 +795,10 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
$('#about').modal('show');
};
$scope.showReportPreview = function () {
$scope.reportPreview = true;
};
$scope.init();
setInterval($scope.refresh, 10000);
});
@@ -1035,7 +1044,6 @@ syncthing.directive('validNodeid', function($http) {
if (resp.error) {
ctrl.$setValidity('validNodeid', false);
} else {
scope.currentNode.NodeID = resp.id;
ctrl.$setValidity('validNodeid', true);
}
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -15,12 +15,17 @@
<title>Syncthing | {{thisNodeName()}}</title>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="raleway.css" rel="stylesheet">
<style type="text/css">
body {
padding-bottom: 70px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5 {
font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
ul+h5 {
margin-top: 1.5em;
}
@@ -95,7 +100,8 @@
<nav class="navbar navbar-top navbar-default" role="navigation">
<div class="container">
<span class="navbar-brand"><img class="logo" src="st-logo-128.png" width="32" height="32" /> Syncthing<small class="hidden-xs"> <span class="text-muted">|</span> {{thisNodeName()}}</small></span>
<span class="navbar-brand"><img class="logo" src="logo-text-64.png" height="32" width="117"/></span>
<p class="navbar-text hidden-xs">{{thisNodeName()}}</p>
<ul class="nav navbar-nav navbar-right">
<li ng-if="upgradeInfo.newer">
<button type="button" class="btn navbar-btn btn-default" href="" ng-click="upgrade()">
@@ -153,7 +159,7 @@
<div class="panel-heading">
<h3 class="panel-title">
<a data-toggle="collapse" data-parent="#repositories" href="#repo-{{$index}}">
<span class="glyphicon glyphicon-hdd"></span> {{repo.Directory | shortPath}}
<span class="glyphicon glyphicon-hdd"></span> {{repo.ID}}
<span class="pull-right hidden-xs" ng-switch="repoStatus(repo.ID)">
<span translate ng-switch-when="unknown">Unknown</span>
<span translate ng-switch-when="stopped">Stopped</span>
@@ -367,10 +373,10 @@
<div class="container">
<ul class="nav navbar-nav">
<li><a class="navbar-link" href="http://discourse.syncthing.net/"><span translate>Support / Forum</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/releases"><span translate>Latest Release</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases"><span translate>Latest Release</span></a></li>
<li><a class="navbar-link" href="http://discourse.syncthing.net/category/documentation"><span translate>Documentation</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/issues"><span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing"><span translate>Source Code</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues"><span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing"><span translate>Source Code</span></a></li>
</ul>
</div>
</nav>
@@ -385,8 +391,12 @@
<!-- Restarting modal -->
<modal id="restarting" icon="refresh" title="{{restartingTitle}}" status="info">
<p>{{restartingBody}} <span translate>Please wait</span>&hellip;</p>
<modal id="restarting" icon="refresh" title="{{'Restarting' | translate}}" status="info">
<p><span translate>Syncthing is restarting.</span> <span translate>Please wait</span>...</p>
</modal>
<modal id="upgrading" icon="refresh" title="{{'Upgrading' | translate}}" status="info">
<p><span translate>Syncthing is upgrading.</span> <span translate>Please wait</span>...</p>
</modal>
<!-- Shutdown modal -->
@@ -399,7 +409,7 @@
<modal id="idqr" large="yes" status="info" close="yes" icon="qrcode" title="{{'Node Identification' | translate}} &mdash; {{nodeName(thisNode())}}">
<div class="well well-sm text-monospace text-center">{{myID}}</div>
<img ng-if="myID" class="center-block img-thumbnail" src="qr/{{myID}}"/>
<img ng-if="myID" class="center-block img-thumbnail" src="qr/?text={{myID}}"/>
</modal>
<!-- Node editor modal -->
@@ -421,7 +431,7 @@
<span translate ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).</span>
<span translate ng-show="!editingExisting && (nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine)">When adding a new node, keep in mind that this node must be added on the other side too.</span>
<span translate ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
<span translate ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.</span>
<span translate ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node 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>
</p>
</div>
<div class="form-group">
@@ -682,8 +692,8 @@
</div>
<div class="modal-body">
<p translate>The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.</p>
<p translate translate-value-url="https://data.syncthing.net">The aggregated statistics are publicly available at {{url}}</p>
<button translate type="button" class="btn btn-default" ng-show="!reportPreview" ng-click="reportPreview = true">Preview Usage Report</button>
<p translate translate-value-url="https://data.syncthing.net">The aggregated statistics are publicly available at {%url%}.</p>
<button translate type="button" class="btn btn-default" ng-show="!reportPreview" ng-click="showReportPreview()">Preview Usage Report</button>
<pre ng-if="reportPreview"><small>{{reportData | json}}</small></pre>
</div>
<div class="modal-footer">
@@ -709,7 +719,7 @@
<!-- About modal -->
<modal id="about" large="yes" close="yes" status="info" title="About">
<h1 class="text-center"><img src="st-logo-128.png" style="vertical-align: -16px" width="64" height="64"/> Syncthing<br/><small>{{version}}</small></h1>
<h1 class="text-center"><img alt="Syncthing" title="Syncthing" src="logo-text-256.png" style="vertical-align: -16px" height="100" width="366"/><br/><small>{{version}}</small></h1>
<hr/>
<p translate>Copyright &copy; 2014 Jakob Borg and the following Contributors:</p>
@@ -722,6 +732,7 @@
<li>Audrius Butkevicius</li>
<li>Ben Sidhom</li>
<li>Brandon Philips</li>
<li>Gilli Sigurdsson</li>
</ul>
</div>
<div class="col-md-6">
@@ -730,6 +741,7 @@
<li>Jens Diemer</li>
<li>Philippe Schommers</li>
<li>Ryan Sullivan</li>
<li>Tully Robinson</li>
<li>Veeti Paananen</li>
</ul>
</div>

120
gui/lang-da.json Normal file
View File

@@ -0,0 +1,120 @@
{
"API Key": "API-nøgle",
"About": "Om",
"Add Node": "Tilføj node",
"Add Repository": "Tilføj lager",
"Address": "Adresse",
"Addresses": "Adresser",
"Allow Anonymous Usage Reporting?": "Tillad anonym brugerstatistik?",
"Announce Server": "Opslagsserver",
"Anonymous Usage Reporting": "Anonym brugerstatistik",
"Bugs": "Fejl",
"CPU Utilization": "CPU-forbrug",
"Close": "Luk",
"Connection Error": "Tilslutnings fejl",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg og følgende bidragsydere:",
"Delete": "Slet",
"Disconnected": "Ikke tilsluttet",
"Documentation": "Dokumentation",
"Download Rate": "Downloadhastighed",
"Edit": "Rediger",
"Edit Node": "Rediger node",
"Edit Repository": "Rediger lager",
"Enable UPnP": "Anvend UPnP",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Angiv kommaseparerat \"ip:port\"-adresser eller ordet \"dynamic\" for at benytte automatisk opslag.",
"Error": "Fejl",
"File Versioning": "Filversionering",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "Filrettigheder tages der ikke hensyn til ved synkronisering. Anvend på FAT-filsystemer.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Filer flyttes til et datostemplet versionsnavn i et .stversions-bibliotek, når de bliver opdateret eller slettet af syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Filer beskyttes mod ændringer fra andre noder, men ændringer på denne node bliver sendt til hele clusteret.",
"Folder": "Bibliotek",
"GUI Authentication Password": "GUI-kodeord",
"GUI Authentication User": "GUI-brugernavn",
"GUI Listen Addresses": "GUI-lytteadresse",
"Generate": "Opret",
"Global Discovery": "Globalt opslag",
"Global Discovery Server": "Global opslagsserver",
"Global Repository": "Global lagring",
"Idle": "Inaktiv",
"Ignore Permissions": "Ignorér filrettigheder",
"Keep Versions": "Behold versioner",
"Latest Release": "Seneste udgivelse",
"Local Discovery": "Lokal opslag",
"Local Discovery Port": "Lokal opslagsport",
"Local Repository": "Lokal lagring",
"Master Repo": "Hovedlagring",
"Max File Change Rate (KiB/s)": "Højeste filændringshastighed (KiB/s)",
"Max Outstanding Requests": "Parallelitet",
"No": "Nej",
"Node ID": "Node ID",
"Node Identification": "Node identifikation",
"Node Name": "Nodenavn",
"Notice": "OBS",
"OK": "OK",
"Offline": "Offline",
"Online": "Online",
"Out Of Sync": "Ude af sync",
"Outgoing Rate Limit (KiB/s)": "Udgående hastighedsbegrænsning (KiB/s)",
"Override Changes": "Overskriv ændringer",
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Sti til lagring på din lokale computer. Hvis biblioteket ikke findes vil det blive oprettet. Tegnet tilde (~) kan bruges som genvej til",
"Please wait": "Vent venligst",
"Preview Usage Report": "Forhåndsvisning af forbrugsrapport",
"RAM Utilization": "RAM-forbrug",
"Reconnect Interval (s)": "Gentilslutningsinterval (s)",
"Repository ID": "Lagrings-ID",
"Repository Master": "Hovedlagring",
"Repository Path": "Sti til lagring",
"Rescan Interval (s)": "Genscanningsinterval (s)",
"Restart": "Genstart",
"Restart Needed": "Programmet kræver genstart",
"Restarting": "Genstarter",
"Save": "Gem",
"Scanning": "Opdaterer",
"Select the nodes to share this repository with.": "Vælg hvilke noder denne lagring skal deles med.",
"Settings": "Indstillinger",
"Share With Nodes": "Del med noderne",
"Shared With": "Delt med",
"Short identifier for the repository. Must be the same on all cluster nodes.": "Kort identifikation for denne lagring. Skal være ens på alle noder i clusteret.",
"Show ID": "Vis ID",
"Shown instead of Node ID in the cluster status.": "Vises i stedet for node-ID under clusterstatus.",
"Shutdown": "Luk ned",
"Source Code": "Kildekode",
"Start Browser": "Start browser",
"Stopped": "Stoppet",
"Support / Forum": "Support / Forum",
"Sync Protocol Listen Addresses": "Lytteadresser for indgående forbindelser",
"Synchronization": "Synkronisering",
"Syncing": "Synkroniserer",
"Syncthing has been shut down.": "Syncthing er blevet lukket ned.",
"Syncthing includes the following software or portions thereof:": "Syncthing indeholder følgende software eller dele heraf:",
"Syncthing is restarting.": "Syncthing genstarter",
"Syncthing is upgrading.": "Syncthing opgradere",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing ser ud til at være stoppet eller oplever problemer med din internetforbindels. Prøver igen...",
"The aggregated statistics are publicly available at {%url%}.": "Samlet statistik er offentligt tilgængelig på {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurationen er gemt, men ikke aktiveret. Syncthing skal genstarte for at aktivere den nye konfiguration.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Den krypterede forbrugsrapport sendes dagligt. Den benyttes til at spore anvendte platforme, lagringsstørrelser og versioner. Hvis det typen af opsamlet data ændres på et senere tidspunkt, vil du blive spurgt om tilladelse igen.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det indtastede node ID ser ikke gyldigt ud. Det skal være en 52 tegn streng, bestående af tal og bogstaver, eventuelt med mellemrum og bindestreger.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det indtastede node ID ser ikke gyldigt ud. Det skal være en 52 eller 56 tegn streng, bestående af tal og bogstaver, eventuelt med mellemrum og bindestreger.",
"The node ID cannot be blank.": "Node-ID'et kan ikke være blankt.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Node-ID'et som skal bruges her, kan du finde i \"Rediger > Vis ID\"-dialogen på den anden node. Mellemrum og bindestreg er valgfri (ignoreres).",
"The number of old versions to keep, per file.": "Antallet af gamle versioner som gemmes, per fil.",
"The number of versions must be a number and cannot be blank.": "Antallet af versioner skal være et tal, og kan ikke være blankt.",
"The repository ID cannot be blank.": "Lagrings-ID kan ikke være blankt.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "Lagrings-ID'et skal være en kort identificierende streng (64 karaktere eller mindre) bestående af bogstav-, tal-, punktum- (.), bindestreg- (-) og understregskaraktere (_).",
"The repository ID must be unique.": "Lagrings-ID'et skal være unikt.",
"The repository path cannot be blank.": "Lagringsstien kan ikke være blank.",
"Unknown": "Ukendt",
"Up to Date": "Fuldt opdateret",
"Upgrade To {%version%}": "Opgradér til {{version}}",
"Upgrading": "Opgradere",
"Upload Rate": "Uploadhastighed",
"Usage": "Forbrug",
"Use Compression": "Anvend komprimering",
"Use HTTPS for GUI": "Anvend HTTPS til GUI adgang",
"Version": "Version",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Når du tilføjer en ny node skal du huske, at den også skal tilføjes på den anden side.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Når du tilføjer en ny node skal du huske, at lagrings-ID'et bliver brugt til at knytte noder sammen. De er følsomme for store og små bogstaver og skal matche på alle noder.",
"Yes": "Ja",
"You must keep at least one version.": "Du skal beholde mindst én version.",
"items": "poster"
}

View File

@@ -11,6 +11,7 @@
"Bugs": "Fehler",
"CPU Utilization": "Prozessorauslastung",
"Close": "Schließen",
"Connection Error": "Verbindungsfehler",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg und folgende Unterstützer:",
"Delete": "Löschen",
"Disconnected": "Verbindung getrennt",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Max. ausstehende Anfragen",
"No": "Nein",
"Node ID": "Knoten-ID",
"Node Identification": "Knoten Identifikation",
"Node Name": "Knoten-Name",
"Notice": "Benachrichtigung",
"OK": "Ok",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Suchintervall (s)",
"Restart": "Neustart",
"Restart Needed": "Neustart notwendig",
"Restarting": "Wird neu gestartet",
"Save": "Speichern",
"Scanning": "Überprüfe",
"Select the nodes to share this repository with.": "Wähle die Knoten aus, mit denen du dieses Verzeichnis teilen willst.",
@@ -84,11 +87,14 @@
"Syncing": "Synchronisiere",
"Syncthing has been shut down.": "Syncthing wurde heruntergefahren.",
"Syncthing includes the following software or portions thereof:": "Syncthing enthält die folgende Software oder Teile davon:",
"Syncthing is restarting.": "Syncthing wird neu gestartet",
"Syncthing is upgrading.": "Syncthing wird aktualisiert",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit Ihrer Internetverbindung. Versuche erneut...",
"The aggregated statistics are publicly available at {{url}}": "Die aggregierten Statistiken sind öffentlich verfügbar unter {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Die aggregierten Statistiken sind öffentlich verfügbar unter {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Die Konfiguration wurde gespeichert, aber nicht aktiviert. Syncthing muss neugestartet werden um die neue Konfiguration zu aktivieren.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Der verschlüsselte Benutzungsbericht wird täglich gesendet. Er wird benutzt um Statistiken über verwendete Betriebssysteme, Verzeichnis-Größen und Programm-Versionen zu erstellen. Sobald der Bericht in Zukunft weitere Daten erfasst, wird dir dieses Fenster erneut angezeigt.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Die eingegebene Knoten-ID scheint nicht gültig zu sein. Sie sollte eine 52 Stellen lange Zeichenkette aus Buchstaben und Zahlen sein. Leerzeichen und Striche sind optional (werden ignoriert).",
"The entered node 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.": "Die eingegebene Knoten-ID scheint nicht gültig zu sein. Es sollte eine 52 oder 56 stellige Zeichenkette aus Buchstaben und Nummern sein. Leerzeichen und Bindestriche sind optional.",
"The node ID cannot be blank.": "Die Knoten-ID darf nicht leer sein.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Die hier einzutragende Knoten-ID kann im \"Bearbeiten > Zeige ID\"-Dialog auf dem anderen Knoten gefunden werden. Leerzeichen und Striche sind optional (werden ignoriert).",
"The number of old versions to keep, per file.": "Anzahl der alten Versionen, die von jeder Datei gespeichert werden sollen.",
@@ -100,6 +106,7 @@
"Unknown": "Unbekannt",
"Up to Date": "Aktuell",
"Upgrade To {%version%}": "Upgrade auf {{version}}",
"Upgrading": "Wird aktualisiert",
"Upload Rate": "Uploadgeschwindigkeit",
"Usage": "Benutzung",
"Use Compression": "Benutze Komprimierung",

View File

@@ -11,6 +11,7 @@
"Bugs": "Bugs",
"CPU Utilization": "Χρήση CPU",
"Close": "Τέλος",
"Connection Error": "Σφάλμα Σύνδεσης",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg και οι παρακάτω Συνεισφορείς:",
"Delete": "Διαγραφή",
"Disconnected": "Αποσυνδεδεμένος",
@@ -32,7 +33,7 @@
"GUI Listen Addresses": "GUI Listen Addresses",
"Generate": "Δημιουργία",
"Global Discovery": "Global Discovery",
"Global Discovery Server": "Global Discovery Server",
"Global Discovery Server": "Διακομιστής Ανεύρεσης Κόμβου",
"Global Repository": "Global Repository",
"Idle": "Ανενεργός",
"Ignore Permissions": "Ignore Permissions",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Max Outstanding Requests",
"No": "Αριθμός",
"Node ID": "ID Κόμβου",
"Node Identification": "Ταυτοποίηση Κόμβου",
"Node Name": "Όνομα Κόμβου",
"Notice": "Notice",
"OK": "OK",
@@ -65,8 +67,9 @@
"Rescan Interval (s)": "Rescan Interval (s)",
"Restart": "Επανεκκίνηση",
"Restart Needed": "Απαιτείται Επανεκκίνηση",
"Restarting": "Επανεκκίνηση",
"Save": "Αποθήκευση",
"Scanning": "Scanning",
"Scanning": "Σάρρωση",
"Select the nodes to share this repository with.": "Επιλογή των κόμβων με τους οποίους θα διαμοιραστεί αυτό το αποθετήριο.",
"Settings": "Ρυθμίσεις",
"Share With Nodes": "Διαμοιρασμός με Κόμβους",
@@ -81,14 +84,17 @@
"Support / Forum": "Υποστήριξη / Forum",
"Sync Protocol Listen Addresses": "Sync Protocol Listen Addresses",
"Synchronization": "Συγχρονισμός",
"Syncing": "Syncing",
"Syncing": "Συγχρονισμός",
"Syncthing has been shut down.": "Syncthing έχει απενεργοποιηθεί.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is restarting.": "Το Syncthing επανεκκινεί.",
"Syncthing is upgrading.": "Το Syncthing αναβαθμίζεται.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
"The aggregated statistics are publicly available at {{url}}": "The aggregated statistics are publicly available at {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Τα στατιστικά που έχουν συλλεγεί είναι διαθέσιμα στο κοινό, στο {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The entered node 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 Κόμβου δεν είναι έγκυρο. Θα πρέπει να είναι αλφαριθμιτικό με 52 ή 56 χαρακτήρες και να αποτελείται από γράμματα και αριθμούς, που προαιρετικά χωρίζονται με κενά και παύλες.",
"The node ID cannot be blank.": "The node ID cannot be blank.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Το ID Κόμβου μπορείτε να βρείτε στο μενού \"Επεξεργασία > Εμφάνιση ID\" του άλλου κόμβου. Κενά και παύλες είναι προαιρετικά (αγνοούνται).",
"The number of old versions to keep, per file.": "The number of old versions to keep, per file.",
@@ -100,6 +106,7 @@
"Unknown": "Άγνωστο",
"Up to Date": "Ενημερώμενο",
"Upgrade To {%version%}": "Αναβάθμιση στην έκδοση {{version}}",
"Upgrading": "Αναβάθμιση",
"Upload Rate": "Upload Rate",
"Usage": "Usage",
"Use Compression": "Χρήση συμπίεσης",

View File

@@ -11,6 +11,7 @@
"Bugs": "Bugs",
"CPU Utilization": "CPU Utilization",
"Close": "Close",
"Connection Error": "Connection Error",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg and the following Contributors:",
"Delete": "Delete",
"Disconnected": "Disconnected",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Max Outstanding Requests",
"No": "No",
"Node ID": "Node ID",
"Node Identification": "Node Identification",
"Node Name": "Node Name",
"Notice": "Notice",
"OK": "OK",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Rescan Interval (s)",
"Restart": "Restart",
"Restart Needed": "Restart Needed",
"Restarting": "Restarting",
"Save": "Save",
"Scanning": "Scanning",
"Select the nodes to share this repository with.": "Select the nodes to share this repository with.",
@@ -84,11 +87,14 @@
"Syncing": "Syncing",
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
"The aggregated statistics are publicly available at {{url}}": "The aggregated statistics are publicly available at {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "The aggregated statistics are publicly available at {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The node ID cannot be blank.": "The node ID cannot be blank.",
"The node ID to enter here can be found in the \"Edit \u003e Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "The node ID to enter here can be found in the \"Edit \u003e Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).",
"The number of old versions to keep, per file.": "The number of old versions to keep, per file.",
@@ -100,6 +106,7 @@
"Unknown": "Unknown",
"Up to Date": "Up to Date",
"Upgrade To {%version%}": "Upgrade To {{version}}",
"Upgrading": "Upgrading",
"Upload Rate": "Upload Rate",
"Usage": "Usage",
"Use Compression": "Use Compression",

View File

@@ -11,6 +11,7 @@
"Bugs": "Errores",
"CPU Utilization": "Uso de la CPU",
"Close": "Cerrar",
"Connection Error": "Connection Error",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Derechos de autor © 2014 Jakob Borg y los siguientes colaboradores:",
"Delete": "Suprimir",
"Disconnected": "Desconectado",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Cantidad máxima de peticiones pendientes",
"No": "No",
"Node ID": "Nodo ID",
"Node Identification": "Node Identification",
"Node Name": "Nodo nombre",
"Notice": "Aviso",
"OK": "OK",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Intervalo de reescaneo (s)",
"Restart": "Reiniciar",
"Restart Needed": "Es necesario reiniciar",
"Restarting": "Restarting",
"Save": "Guardar",
"Scanning": "Actualización",
"Select the nodes to share this repository with.": "Seleccione los nodos con los cuales compartir el repositorio.",
@@ -84,11 +87,14 @@
"Syncing": "Sincronización",
"Syncthing has been shut down.": "La sincronización esta apagada",
"Syncthing includes the following software or portions thereof:": "Syncthing incluye los siguientes softwares o partes de ellos:",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing parece estar apagado, o hay un problema con su conexión de Internet. Reintentando...",
"The aggregated statistics are publicly available at {{url}}": "Las estadísticas acumuladas están públicamente disponibles en {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Las estadísticas acumuladas están públicamente disponibles en {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido guardada pero no activada.\nSyncthing debe reiniciarse para activar la nueva configuración.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "El reporte de uso se envía encriptado diariamente. Se utiliza para hacer un seguimiento de plataformas comunes, tamaño de repositorios y versión de aplicaciones. Si el conjunto de datos cambia sera notificado mediante este dialogo nuevamente.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "El ID de nodo ingresado no es valido. Debe ser una cadena de al menos 52 caracteres consistente en letras y números, con espacios y guiones opcionales.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "El ID de nodo ingresado no es valido. Debe ser una cadena de 52 o de 56 caracteres consistente en letras y números, con espacios y guiones opcionales.",
"The node ID cannot be blank.": "El ID de nodo no puede estar vacío.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "El ID de nodo a ingresar aquí puede verse en la opción de menú \"Edición > Mostrar ID\" del otro nodo. Espacios y guiones son opcionales (ignorados).",
"The number of old versions to keep, per file.": "El numero de versiones anteriores a conservar, por archivo.",
@@ -100,9 +106,10 @@
"Unknown": "Desconocido",
"Up to Date": "Actualizado",
"Upgrade To {%version%}": "Actualizar a {{version}}",
"Upgrading": "Upgrading",
"Upload Rate": "Tasa de subida",
"Usage": "Utilización",
"Use Compression": "Use Compression",
"Use Compression": "Usar compresn",
"Use HTTPS for GUI": "Usar HTTPS para la GUI",
"Version": "Versión",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Al agregar un nuevo nodo, recuerde que este nodo debe ser agregado en el otro lado también.",

View File

@@ -5,17 +5,18 @@
"Add Repository": "Ajouter un répertoire",
"Address": "Adresse",
"Addresses": "Adresses",
"Allow Anonymous Usage Reporting?": "Autoriser le rapport anonyme de statistiques d'utilisation?",
"Allow Anonymous Usage Reporting?": "Autoriser le rapport anonyme de statistiques d'utilisation ?",
"Announce Server": "Serveur d'annonce",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Bugs": "Bugs",
"CPU Utilization": "Utilisation du CPU",
"Close": "Fermer",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg et les contributeurs suivants:",
"Connection Error": "Erreur de connexion",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg et les contributeurs suivants :",
"Delete": "Supprimer",
"Disconnected": "Déconnecté",
"Documentation": "Documentation",
"Download Rate": "Débit de téléchargement",
"Download Rate": "Débit de réception",
"Edit": "Éditer",
"Edit Node": "Éditer le nœud",
"Edit Repository": "Éditer le répertoire",
@@ -23,8 +24,8 @@
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Entrer les adresses \"ip:port\" séparées par une virgule ou \"dynamic\" afin d'activer la recherche automatique de l'adresse.",
"Error": "Erreur",
"File Versioning": "Versions de fichier",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "Les permissions de fichier sont ignorées lors de la recherche de changements. À utiliser sur les systèmes de fichiers en FAT.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Les fichiers sont datés et déplacés dans le dossier .stversions lors de leurs remplacements ou suppressions par syncthing.",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "Les permissions de fichier sont ignorées lors de la recherche de changements. À utiliser sur les systèmes de fichiers de type FAT.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Les fichiers sont datés et déplacés dans le dossier .stversions lors de leur remplacement ou suppression par syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Les fichiers sont protégés des changements réalisés sur les autres nœuds, mais les changements réalisés sur ce nœud seront transférés au reste du cluster.",
"Folder": "Dossier",
"GUI Authentication Password": "Mot de passe d'authentification GUI",
@@ -43,9 +44,10 @@
"Local Repository": "Dossier local",
"Master Repo": "Dossier maître",
"Max File Change Rate (KiB/s)": "Débit maximum de changement de fichier (KiB/s)",
"Max Outstanding Requests": "Nombre maximum de demandes conccurentes de blocs de fichier",
"Max Outstanding Requests": "Nombre maximum de demandes concurrentes de blocs de fichier",
"No": "Non",
"Node ID": "ID du nœud",
"Node Identification": "Identification du nœud",
"Node Name": "Nom du nœud",
"Notice": "Notification",
"OK": "OK",
@@ -65,8 +67,9 @@
"Rescan Interval (s)": "Intervalle de rescan (s)",
"Restart": "Redémarrer",
"Restart Needed": "Redémarrage nécessaire",
"Restarting": "Redémarrage",
"Save": "Sauver",
"Scanning": "Scanning",
"Scanning": "En cours de scan",
"Select the nodes to share this repository with.": "Sélectionner les nœuds qui partageront ce répertoire.",
"Settings": "Configuration",
"Share With Nodes": "Partager avec les nœuds",
@@ -84,29 +87,33 @@
"Syncing": "En cours de synchronisation",
"Syncthing has been shut down.": "Syncthing a été éteint.",
"Syncthing includes the following software or portions thereof:": "Syncthing inclut les logiciels, ou portion de ceux-ci, suivants:",
"Syncthing is restarting.": "Syncthing est cours de redémarrage.",
"Syncthing is upgrading.": "Syncthing est cours de mise à jour.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing semble être éteint, ou il y a un problème avec votre connexion Internet. Nouvelle tentative ...",
"The aggregated statistics are publicly available at {{url}}": "Les statistiques agrégées sont disponibles publiquement à l'adresse {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Les statistiques agrégées sont disponibles publiquement à l'adresse {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuration a été sauvée mais pas activée. Syncthing doit redémarrer afin d'activer la nouvelle configuration.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Le rapport d'utilisation chiffré est envoyé quotidiennement. Il sert à répertorier les plateformes utilisées, la taille des répertoires et les versions de l'application. Si le jeu de données rapportées devait être changé, il vous sera demandé de le valider de nouveau via ce dialogue.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID du nœud ne semble pas être valide. Il devrait ressembler à une chaine de 52 caractères comprenant lettres et chiffres, avec des espaces et des traits d'union optionnels.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID du nœud inséré ne semble pas être valide. Il devrait ressembler à une chaîne de 52 ou 56 comprenant lettres et chiffres, avec des espaces et des traits d'union optionnels.",
"The node ID cannot be blank.": "L'ID du nœud ne peut être vide.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "L'ID du nœud à insérer peut être trouvé à travers le menu \"Éditer > Montrer l'ID\" des autres nœuds. Les espaces et les traits d'union sont optionnels (ils seront ignorés).",
"The number of old versions to keep, per file.": "Le nombre d'anciennes versions à garder, par fichier.",
"The number of versions must be a number and cannot be blank.": "Le nombre de version doit être un nombre et ne peut pas être vide.",
"The repository ID cannot be blank.": "L'ID du répertoire ne peut être vide.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "L'ID du répertoire doit un identifiant court (64 caractères ou moins) comprenant des lettres, nombres, points (.), trait d'union (-) et tiret bas (_).",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "L'ID du répertoire doit être un identifiant court (64 caractères ou moins) comprenant des lettres, nombres, points (.), trait d'union (-) et tiret bas (_).",
"The repository ID must be unique.": "L'ID du répertoire doit être unique.",
"The repository path cannot be blank.": "Le chemin du répertoire ne peut pas être vide.",
"Unknown": "Inconnu",
"Up to Date": "Synchronistation à jour",
"Up to Date": "Synchronisation à jour",
"Upgrade To {%version%}": "Upgrader vers {{version}}",
"Upgrading": "Mise à jour de Syncthing",
"Upload Rate": "Débit d'envoi",
"Usage": "Utilisation",
"Use Compression": "Utiliser la compression",
"Use HTTPS for GUI": "Utiliser l'HTTPS pour le GUI",
"Version": "Version",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Lorsqu'un nœud est ajouté, gardez à l'esprit que ce nœud doit aussi être ajouté de l'autre coté.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Lorsqu'un nouveau répertoire est ajouté, gardez à l'esprit que l'ID du répertoire est utilisé pour lier les répertoires à travers les nœuds. Ils sont sensibles à la case et doivent être semblables à travers tous les nœuds.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Lorsqu'un nouveau répertoire est ajouté, gardez à l'esprit que l'ID du répertoire est utilisé pour lier les répertoires à travers les nœuds. Ils sont sensibles à la casse et doivent être identiques à travers tous les nœuds.",
"Yes": "Oui",
"You must keep at least one version.": "Vous devez garder au minimum une version.",
"items": "éléments"

View File

@@ -6,11 +6,12 @@
"Address": "Indirizzo",
"Addresses": "Indirizzi",
"Allow Anonymous Usage Reporting?": "Abilitare Statistiche Anonime di Utilizzo?",
"Announce Server": "Server di Ricerca Globale dei Nodi",
"Announce Server": "Server di Presenza Globale dei Nodi",
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
"Bugs": "Bug",
"CPU Utilization": "Utilizzo della CPU",
"Close": "Chiudi",
"Connection Error": "Connection Error",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg e i seguenti Collaboratori:",
"Delete": "Elimina",
"Disconnected": "Disconnesso",
@@ -32,7 +33,7 @@
"GUI Listen Addresses": "Indirizzi dell'Interfaccia Grafica",
"Generate": "Genera",
"Global Discovery": "Individuazione Globale",
"Global Discovery Server": "Global Discovery Server",
"Global Discovery Server": "Server di Ricerca Globale",
"Global Repository": "Deposito Globale",
"Idle": "Inattivo",
"Ignore Permissions": "Ignora Permessi",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Numero Massimo di Richieste Simultanee per i Blocchi di File",
"No": "No",
"Node ID": "ID Nodo",
"Node Identification": "Node Identification",
"Node Name": "Nome Nodo",
"Notice": "Avviso",
"OK": "OK",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Intervallo di Scansione dei File (s)",
"Restart": "Riavvia",
"Restart Needed": "Riavvio Necessario",
"Restarting": "Restarting",
"Save": "Salva",
"Scanning": "Scansione in corso",
"Select the nodes to share this repository with.": "Seleziona i nodi con i quali vuoi condividere questo deposito.",
@@ -84,11 +87,14 @@
"Syncing": "Sincronizzazione in corso",
"Syncthing has been shut down.": "Syncthing è stato arrestato.",
"Syncthing includes the following software or portions thereof:": "Syncthing include i seguenti software o porzioni di questi:",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing sembra inattivo, oppure c'è un problema con la tua connessione a Internet. Nuovo tentativo…",
"The aggregated statistics are publicly available at {{url}}": "Le statistiche aggregate sono disponibili pubblicamente su {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Le statistiche aggregate sono disponibili pubblicamente su {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configurazione è stata salvata ma non attivata. Devi riavviare Syncthing per attivare la nuova configurazione.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Quotidianamente il software invia le statistiche di utilizzo in forma criptata. Questi dati riguardano i sistemi operativi utilizzati, le dimensioni dei depositi e le versioni del software. Se i dati riportati cambiano verrà mostrata di nuovo questa finestra di dialogo.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID del nodo inserito non sembra valido. Dovrebbe essere una stringa di 52 caratteri costituita da lettere e numeri, con spazi e trattini opzionali.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID del nodo inserito non sembra valido. Dovrebbe essere una stringa di 52 o 56 caratteri costituita da lettere e numeri, con spazi e trattini opzionali.",
"The node ID cannot be blank.": "L'ID del nodo non può essere vuoto.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Trova l'ID del nodo nella finestra di dialogo \"Modifica > Mostra ID\" dell'altro nodo e poi inseriscilo qui. Gli spazi e i trattini sono opzionali (ignorati).",
"The number of old versions to keep, per file.": "Il numero di vecchie versioni da mantenere, per file.",
@@ -100,6 +106,7 @@
"Unknown": "Sconosciuto",
"Up to Date": "Sincronizzato",
"Upgrade To {%version%}": "Aggiorna Alla {{version}}",
"Upgrading": "Upgrading",
"Upload Rate": "Velocità Upload",
"Usage": "Utilizzo",
"Use Compression": "Utilizza Compressione",

120
gui/lang-nl.json Normal file
View File

@@ -0,0 +1,120 @@
{
"API Key": "API-sleutel",
"About": "Over",
"Add Node": "Voeg node toe",
"Add Repository": "Voeg repository toe",
"Address": "Adres",
"Addresses": "Adressen",
"Allow Anonymous Usage Reporting?": "Bijhouden van anonieme gebruikers statistieken toestaan?",
"Announce Server": "Aankondigings Server",
"Anonymous Usage Reporting": "Bijhouden anonieme gebruikers statistieken",
"Bugs": "Fouten",
"CPU Utilization": "CPU Gebruik",
"Close": "Sluiten",
"Connection Error": "Verbindingsfout",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg en de onderstaande bijdragers:",
"Delete": "Verwijderen",
"Disconnected": "Niet Verbonden",
"Documentation": "Documentatie",
"Download Rate": "Downloadsnelheid",
"Edit": "Bewerk",
"Edit Node": "Bewerk node",
"Edit Repository": "Repository Bijwerken",
"Enable UPnP": "UPnP aanzetten",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Geef, gescheiden door komma's, \"ip:port\" adressen of \"dynamic\" voor het automatische vinden van de addressen.",
"Error": "Fout",
"File Versioning": "Versiebeheer",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "Bestands permissiebits worden genegeerd wanneer naar veranderingen wordt gekeken. Gebruik dit op FAT bestandsystemen",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Bestanden worden naar de .stversions map verplaatst met een tijdsaanduiding, wanneer ze aangepast of verwijderd worden door syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Bestanden zijn beschermd tegen verandering gedaan op andere nodes, maar veranderingen op deze node worden naar de rest van het cluster gestuurd",
"Folder": "Map",
"GUI Authentication Password": "GUI Authentificatie Wachtwoord",
"GUI Authentication User": "GUI Authentificatie Gebruikersnaam",
"GUI Listen Addresses": "GUI Inkomend adres",
"Generate": "Genereer",
"Global Discovery": "Globaal zoeken",
"Global Discovery Server": "Globale zoekserver",
"Global Repository": "Globale repository",
"Idle": "Klaar",
"Ignore Permissions": "Rechten negeren",
"Keep Versions": "Versies behouden",
"Latest Release": "Laatste uitgave",
"Local Discovery": "Lokaal zoeken",
"Local Discovery Port": "Lokaal zoeken-poort",
"Local Repository": "Lokale repository",
"Master Repo": "Tegen veranderingen beschermen",
"Max File Change Rate (KiB/s)": "Maximale bestands uitwisselsnelheid (KiB/s)",
"Max Outstanding Requests": "Maximaal aantal openstaande aanvragen",
"No": "Nee",
"Node ID": "Node ID",
"Node Identification": "Node Identificatie",
"Node Name": "Node naam",
"Notice": "Notificatie",
"OK": "OK",
"Offline": "Offline",
"Online": "Online",
"Out Of Sync": "Niet gesynchroniseerd",
"Outgoing Rate Limit (KiB/s)": "Uitgaande snelheidslimiet (KiB/s)",
"Override Changes": "Veranderingen overschrijven",
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Pad naar de repository op de lokale computer. Word aangemaakt indien deze niet bestaat. Het tilde (~) karakter kan gebruikt worden als afkorting voor",
"Please wait": "Even geduld",
"Preview Usage Report": "Bekijk gebruikers statistieken",
"RAM Utilization": "RAM gebruik",
"Reconnect Interval (s)": "Herverbind-interval (s)",
"Repository ID": "Repository ID",
"Repository Master": "Hoofd repository",
"Repository Path": "Pad van repository",
"Rescan Interval (s)": "Herscan interval (s)",
"Restart": "Herstart",
"Restart Needed": "Herstart nodig",
"Restarting": "Herstarten",
"Save": "Bewaar",
"Scanning": "Aan het zoeken",
"Select the nodes to share this repository with.": "Selecteer de nodes om deze repository mee te delen",
"Settings": "Instellingen",
"Share With Nodes": "Deel met nodes",
"Shared With": "Gedeeld met",
"Short identifier for the repository. Must be the same on all cluster nodes.": "Korte naam voor de repository. Moet hetzelfde zijn op alle nodes in het cluster.",
"Show ID": "Toon ID",
"Shown instead of Node ID in the cluster status.": "Wordt weergegeven i.p.v. het node ID in de cluster status",
"Shutdown": "Sluit af",
"Source Code": "Broncode",
"Start Browser": "Start browser",
"Stopped": "Gestopt",
"Support / Forum": "Support / Forum",
"Sync Protocol Listen Addresses": "Synchronisatie protocol luister adres",
"Synchronization": "Synchronisatie",
"Syncing": "Aan het synchroniseren",
"Syncthing has been shut down.": "Syncthing is afgesloten",
"Syncthing includes the following software or portions thereof:": "De volgende software of delen daarvan zijn onderdeel van syncthing:",
"Syncthing is restarting.": "Syncthing is aan het herstarten.",
"Syncthing is upgrading.": "Syncthing is aan het upgraden.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt afgesloten te zijn, of er is een verbindingsprobleem met het internet. Nieuwe poging....",
"The aggregated statistics are publicly available at {%url%}.": "The verzamelde statistieken zijn publiek beschikbaar op {{url}}",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De configuratie is opslagen maar nog niet actief. Syncthing moet opnieuw opgestart worden om de nieuwe configuratie te activeren.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "De versleutelde gebruikers statistieken worden dagelijks verstuurd. Deze worden gebruikt om veelgebruikte platformen, repo groottes en app versies bij te houden. Als er nieuwe statistieken worden bijgehouden, wordt dit venster weer getoond.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Het ingevoerde node ID lijkt niet valide te zijn. Het moet een 52 karakter lange string zijn bestaande uit letters en cijfers, spaties en streepjes zijn optioneel.",
"The entered node 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.": "Het ingevoerde node ID lijkt niet valide te zijn. Het moet een 52 of 56 karakter lange string zijn, bestaande uit letters en cijfers, spaties en streepjes zijn optioneel.",
"The node ID cannot be blank.": "Er moet een node ID ingevoerd worden",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Het node ID dat ingevoerd moet worden kan op de andere node gevonden via \"Bewerk > Toon ID\". Spaties en streepjes zijn toegestaan (worden genegeerd).",
"The number of old versions to keep, per file.": "Het aantal versies dat bewaard moet worden per file.",
"The number of versions must be a number and cannot be blank.": "Het aantal nummers moet een getal zijn en mag niet leeg blijven.",
"The repository ID cannot be blank.": "Er moet een repository ID ingevoerd worden.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "Het repository ID moet een korte naam zijn (met 64 karakters of minder) en mag alleen bestaan uit cijfers, letters, punten (.), streepjes (-) en liggende streepjes (_). ",
"The repository ID must be unique.": "Het repository ID moet uniek zijn.",
"The repository path cannot be blank.": "Het repository ID moet ingevuld worden.",
"Unknown": "Onbekend",
"Up to Date": "Gesynchroniseerd",
"Upgrade To {%version%}": "Upgrade naar {{version}}",
"Upgrading": "Bezig met upgrade",
"Upload Rate": "Upload snelheid",
"Usage": "Gebruik",
"Use Compression": "Compressie gebruiken",
"Use HTTPS for GUI": "Gebruik HTTPS voor de GUI",
"Version": "Versie",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Bedenk bij het toevoegen van een nieuwe node dat deze node ook toegevoegd moet worden aan de kant van de nieuwe node.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Bedenk bij het toevoegen van een nieuwe repository dat het repository ID de repositores met elkaar verbindt tussen de nodes. Ze zijn hoofdlettergevoelig en moeten exact dezelfde naam hebben op alle nodes.",
"Yes": "Ja",
"You must keep at least one version.": "Minstens 1 versie moet bewaard blijven.",
"items": "objecten"
}

View File

@@ -1,112 +1,119 @@
{
"API Key": "Chave API",
"About": "Acerca de",
"Add Node": "Adicionar Nó",
"Add Repository": "Adicionar Repositório",
"API Key": "Chave da API",
"About": "Acerca da aplicação",
"Add Node": "Adicionar nó",
"Add Repository": "Adicionar repositório",
"Address": "Endereço",
"Addresses": "Endereços",
"Allow Anonymous Usage Reporting?": "Permitir Envio de Relatórios Anónimos?",
"Allow Anonymous Usage Reporting?": "Permitir envio de relatórios anónimos de utilização?",
"Announce Server": "Servidor de anúncios",
"Anonymous Usage Reporting": "Envio de Relatórios Anónimos",
"Anonymous Usage Reporting": "Enviar de relatórios anónimos de utilização",
"Bugs": "Erros",
"CPU Utilization": "Utilização da CPU",
"Close": "Fechar",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Direitos Reservados © 2014 Jakob Borg e os seguintes Contribuidores:",
"Delete": "Apagar",
"Connection Error": "Erro de ligação",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Direitos reservados © 2014 Jakob Borg e os seguintes contribuidores:",
"Delete": "Eliminar",
"Disconnected": "Desconectado",
"Documentation": "Documentação",
"Download Rate": "Taxa de transferência",
"Download Rate": "Velocidade de recepção",
"Edit": "Editar",
"Edit Node": "Editar Nó",
"Edit Repository": "Editar Repositório",
"Edit Node": "Editar nó",
"Edit Repository": "Editar repositório",
"Enable UPnP": "Activar UPnP",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduza endereços \"ip:porto\" separados por vírgulas ou \"dynamic\" para o descobrimento automático do endereço.",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduza endereços \"ip:porto\" separados por vírgulas ou \"dynamic\" para descobrir automaticamente o endereço.",
"Error": "Erro",
"File Versioning": "Gestão de versões",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "As permissões do ficheiro são ignoradas na pesquisa por mudanças. Utilize nos sistemas de ficheiros FAT.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Os ficheiros são movidos para versões carimbadas com o tempo numa pasta .stversions quando substituídos ou apagados por syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Os ficheiros são protegidos de mudanças feitas em outros nós, mas alterações feitas neste nó serão enviadas para o resto do agrupamento.",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "As permissões do ficheiro são ignoradas ao procurar alterações. Utilize nos sistemas de ficheiros FAT.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Os ficheiros são movidos para versões carimbadas com o tempo numa pasta .stversions quando substituídos ou apagados pelo syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Os ficheiros são protegidos das alterações feitas noutros nós, mas alterações feitas neste nó serão enviadas para o resto do agrupamento.",
"Folder": "Pasta",
"GUI Authentication Password": "Senha de Autenticação GUI",
"GUI Authentication User": "Utilizador de autenticação GUI",
"GUI Listen Addresses": "Endereço de escuta GUI",
"GUI Authentication Password": "Senha da autenticação na interface gráfica",
"GUI Authentication User": "Utilizador da autenticação na interface gráfica",
"GUI Listen Addresses": "Endereço de escuta da interface gráfica",
"Generate": "Gerar",
"Global Discovery": "Descoberta Global",
"Global Discovery Server": "Servidor de Descoberta Global",
"Global Repository": "Repositório Global",
"Global Discovery": "Busca global",
"Global Discovery Server": "Servidor da busca global",
"Global Repository": "Repositório global",
"Idle": "Em espera",
"Ignore Permissions": "Ignorar Permissões",
"Keep Versions": "Manter Versões",
"Ignore Permissions": "Ignorar permissões",
"Keep Versions": "Manter versões",
"Latest Release": "Última versão",
"Local Discovery": "Descoberta Local",
"Local Discovery Port": "Porto de Descoberta Local",
"Local Discovery": "Busca local",
"Local Discovery Port": "Porto da busca local",
"Local Repository": "Repositório local",
"Master Repo": "Repositório Mestre",
"Max File Change Rate (KiB/s)": "Taxa máxima de troca de ficheiros (KiB/s)",
"Master Repo": "Repositório mestre",
"Max File Change Rate (KiB/s)": "Velocidade máxima de alterações de ficheiros (KiB/s)",
"Max Outstanding Requests": "Número máximo de pedidos pendentes",
"No": "Não",
"Node ID": "ID do Nó",
"Node Name": "Nome do Nó",
"Node ID": "ID do nó",
"Node Identification": "Identificação do nó",
"Node Name": "Nome do nó",
"Notice": "Nota",
"OK": "OK",
"Offline": "Desconectado",
"Online": "Conectado",
"Out Of Sync": "Não sincronizado",
"Outgoing Rate Limit (KiB/s)": "Limite da taxa de envio (KiB/s)",
"Override Changes": "Sobrepor Mudanças",
"Outgoing Rate Limit (KiB/s)": "Limite da velocidade de envio (KiB/s)",
"Override Changes": "Sobrepor alterações",
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Caminho para o repositório no computador local. Será criado se não existir. O carácter (~) pode ser utilizado como atalho para",
"Please wait": "Aguarde",
"Preview Usage Report": "Visualização de Relatório",
"Preview Usage Report": "Pré-visualizar relatório de utilização",
"RAM Utilization": "Utilização da RAM",
"Reconnect Interval (s)": "Intervalo de reestabelecimento de ligação (s)",
"Repository ID": "ID do Repositório",
"Repository Master": "Repositório Mestre",
"Repository Path": "Caminho do Repositório",
"Rescan Interval (s)": "Intervalo de monitorização (s)",
"Repository ID": "ID do repositório",
"Repository Master": "Repositório mestre",
"Repository Path": "Caminho do repositório",
"Rescan Interval (s)": "Intervalo entre varrimentos (s)",
"Restart": "Reiniciar",
"Restart Needed": "É preciso reiniciar",
"Restarting": "Reiniciando",
"Save": "Gravar",
"Scanning": "Varrendo",
"Select the nodes to share this repository with.": "Seleccione os nós com os quais vai partilhar este repositório.",
"Settings": "Configurações",
"Share With Nodes": "Partilhar com Nós",
"Shared With": "Partilhado Com",
"Share With Nodes": "Partilhar com os nós",
"Shared With": "Partilhado com",
"Short identifier for the repository. Must be the same on all cluster nodes.": "Identificador curto para o repositório. Tem que ser igual em todos os nós do agrupamento.",
"Show ID": "Mostrar ID",
"Shown instead of Node ID in the cluster status.": "Mostrado ao invés do ID do Nó no estado do agrupamento.",
"Shown instead of Node ID in the cluster status.": "Apresentado ao invés do ID do nó no estado do agrupamento.",
"Shutdown": "Desligar",
"Source Code": "Código Fonte",
"Start Browser": "Iniciar Navegador",
"Source Code": "Código fonte",
"Start Browser": "Iniciar navegador",
"Stopped": "Parado",
"Support / Forum": "Suporte / Fórum",
"Sync Protocol Listen Addresses": "Endereços de escuta do protocolo de sincronização",
"Synchronization": "Sincronização",
"Syncing": "Sincronizando",
"Syncthing has been shut down.": "Syncthing foi desligado.",
"Syncthing includes the following software or portions thereof:": "Syncthing inclui as seguintes aplicações ou partes delas:",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing parece estar em baixo, ou então existe um problema com a sua ligação à Internet. Tentando novamente...",
"The aggregated statistics are publicly available at {{url}}": "As estatísticas agrupadas estão disponíveis publicamente em {{url}}",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "A configuração foi gravada mas não activada. Syncthing tem que reiniciar para activar a nova configuração.",
"Syncing": "A Sincronizar",
"Syncthing has been shut down.": "O Syncthing foi desligado.",
"Syncthing includes the following software or portions thereof:": "O Syncthing inclui as seguintes aplicações ou partes delas:",
"Syncthing is restarting.": "O Syncthing está a reiniciar.",
"Syncthing is upgrading.": "O Syncthing está a actualizar-se.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "O Syncthing parece estar em baixo, ou então existe um problema com a sua ligação à Internet. Tentando novamente...",
"The aggregated statistics are publicly available at {%url%}.": "As estatísticas agrupadas estão disponíveis publicamente em {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "A configuração foi gravada mas não activada. O Syncthing tem que reiniciar para activar a nova configuração.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "O relatório de utilização cifrado é enviado diariamente. É utilizado para seguir plataformas comuns, tamanhos de repositórios e versões da aplicação. Se o conjunto de dados do relatório for alterado, será notificado novamente através desta janela.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "O ID do nó indicado não parece ser válido. Deveria conter uma palavra de 52 caracteres constituída por letras e números, com espaços e traços opcionais. ",
"The node ID cannot be blank.": "O ID do nó não pode ser vazio.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "O ID do nó fornecido não parece ser válido. Deveria ser um texto com 52 ou 56 caracteres constituídos por letras e números, com espaços e traços opcionais.",
"The node ID cannot be blank.": "O ID do nó não pode estar vazio.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "O ID do nó a introduzir pode ser encontrado no diálogo \"Editar > Mostrar ID\" no outro nó. Espaços e traços são opcionais (ignorados).",
"The number of old versions to keep, per file.": "O número de versões antigas a manter, por ficheiro.",
"The number of versions must be a number and cannot be blank.": "O número de versões tem que ser um número e não pode ser vazio.",
"The repository ID cannot be blank.": "O ID do repositório não pode ser vazio.",
"The number of versions must be a number and cannot be blank.": "O número de versões tem que ser um número e não pode estar vazio.",
"The repository ID cannot be blank.": "O ID do repositório não pode estar vazio.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "O ID do repositório tem que ser um identificador curto (64 caracteres ou menos) e consiste em letras, números e os caracteres ponto (.), traço (-) e (_).",
"The repository ID must be unique.": "O ID do repositório tem que ser único.",
"The repository path cannot be blank.": "O caminho do repositório não pode ser vazio.",
"The repository path cannot be blank.": "O caminho do repositório não pode estar vazio.",
"Unknown": "Desconhecido",
"Up to Date": "Actualizado",
"Upgrade To {%version%}": "Atualizar para {{version}}",
"Upgrade To {%version%}": "Actualizar para {{version}}",
"Upgrading": "Actualizando",
"Upload Rate": "Taxa de envio",
"Usage": "Utilização",
"Use Compression": "Usar Compressão",
"Use HTTPS for GUI": "Utilizar HTTPS para GUI",
"Use Compression": "Usar compressão",
"Use HTTPS for GUI": "Utilizar HTTPS na interface gráfica",
"Version": "Versão",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Quando adicionar um novo nó, lembre-se que este nó tem que ser adicionado do outro lado também.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Quando adicionar um novo repositório, lembre-se que o ID do Repositório é utilizado para juntar os repositórios entre nós. São sensíveis às maiúsculas e minúsculas e tem que corresponder exactamente entre todos os nós.",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Quando adicionar um novo nó, lembre-se que este nó tem que ser adicionado no outro lado também.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Quando adicionar um novo repositório, lembre-se que o ID do repositório é utilizado para juntar os repositórios entre nós. Os ID's são sensíveis às maiúsculas e minúsculas e têm que corresponder exactamente entre todos os nós.",
"Yes": "Sim",
"You must keep at least one version.": "Tem que manter pelo menos uma versão.",
"items": "itens"

View File

@@ -11,6 +11,7 @@
"Bugs": "Ошибки",
"CPU Utilization": "Загрузка ЦПУ",
"Close": "Закрыть",
"Connection Error": "Connection Error",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Все права защищены © 2014 Jakob Borg и следующие участники:",
"Delete": "Удалить",
"Disconnected": "Нет соединения",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Максимальное количество исходящих запросов",
"No": "Нет",
"Node ID": "ID Узла",
"Node Identification": "Node Identification",
"Node Name": "Имя Узла",
"Notice": "Внимание",
"OK": "ОК",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Интервал между сканированием (сек)",
"Restart": "Перезапуск",
"Restart Needed": "Требуется перезапуск",
"Restarting": "Restarting",
"Save": "Сохранить",
"Scanning": "Сканирование",
"Select the nodes to share this repository with.": "Выберите узлы для которых будет доступен данный репозиторий.",
@@ -84,11 +87,14 @@
"Syncing": "Синхронизация",
"Syncthing has been shut down.": "Syncthing выключен.",
"Syncthing includes the following software or portions thereof:": "Syncthing включает в себя следующее ПО или его части:",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Кажется, Syncthing не запущен или есть проблемы с подключением к Интернету. Переподключаюсь...",
"The aggregated statistics are publicly available at {{url}}": "Суммарная статистика общедоступна на {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Суммарная статистика общедоступна на {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Конфигурация была сохранена но не активирована. Для активации новой конфигурации необходимо рестартовать Syncthing.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Зашифрованные отчёты об использовании отправляются ежедневно. Они используются для отслеживания проблем, размеров репозиториев и версий Syncthing. Если набор данных в отчёте будет изменён, то вы получите уведомление об этом в этом диалоге.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Введённый ID узла выглядит неправильно: ID должен быть строкой, длинной 52 символа, обязательно содержащей группы букв и цифр которые могут быть разделены пробелами или тире.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The node ID cannot be blank.": "ID узла не может быть пустым.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "ID узла можно узнать в окне \"Редактировать > Показать ID\" на требуемом узле. Пробелы и тире используются для удобства и не обязательны (игнорируются).",
"The number of old versions to keep, per file.": "Количество хранимых версий файла.",
@@ -100,6 +106,7 @@
"Unknown": "Неизвестно",
"Up to Date": "Обновлено",
"Upgrade To {%version%}": "Обновить до {{version}}",
"Upgrading": "Upgrading",
"Upload Rate": "Скорость отдачи",
"Usage": "Справка",
"Use Compression": "Использовать сжатие",

View File

@@ -9,13 +9,14 @@
"Announce Server": "Uppslagningsserver",
"Anonymous Usage Reporting": "Anonym användarstatistik",
"Bugs": "Buggar",
"CPU Utilization": "CPU-utnyttjande",
"CPU Utilization": "CPU-användning",
"Close": "Stäng",
"Connection Error": "Anslutningsproblem",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg och de följande medarbetarna:",
"Delete": "Radera",
"Disconnected": "Ej ansluten",
"Documentation": "Dokumentation",
"Download Rate": "Nerladdningshastighet",
"Download Rate": "Nedladdningshastighet",
"Edit": "Redigera",
"Edit Node": "Redigera nod",
"Edit Repository": "Redigera lagring",
@@ -29,7 +30,7 @@
"Folder": "Katalog",
"GUI Authentication Password": "GUI-lösenord",
"GUI Authentication User": "GUI-användare",
"GUI Listen Addresses": "GUI-address",
"GUI Listen Addresses": "GUI-adress",
"Generate": "Skapa",
"Global Discovery": "Global uppslagning",
"Global Discovery Server": "Global uppslagningsserver",
@@ -46,6 +47,7 @@
"Max Outstanding Requests": "Paralellitet",
"No": "Nej",
"Node ID": "Nod-ID",
"Node Identification": "Nod-identifiering",
"Node Name": "Nodnamn",
"Notice": "OBS",
"OK": "OK",
@@ -57,7 +59,7 @@
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Sökväg till katalogen på din dator. Kommer att skapas om det inte finns. Tecknet tilde (~) kan användas som en genväg för",
"Please wait": "Var god vänta",
"Preview Usage Report": "Förhandsgranska statistik",
"RAM Utilization": "Minnesutnyttjande",
"RAM Utilization": "Minnesanvändning",
"Reconnect Interval (s)": "Anslutningsintervall (s)",
"Repository ID": "Lagrings-ID",
"Repository Master": "Huvudlagring",
@@ -65,6 +67,7 @@
"Rescan Interval (s)": "Förnyelseintervall (s)",
"Restart": "Starta om",
"Restart Needed": "Omstart behövs",
"Restarting": "Startar om",
"Save": "Spara",
"Scanning": "Uppdaterar",
"Select the nodes to share this repository with.": "Välj vilka noder förvaringen ska delas med.",
@@ -80,15 +83,18 @@
"Stopped": "Stoppad",
"Support / Forum": "Support / Forum",
"Sync Protocol Listen Addresses": "Address för inkommande anslutningar",
"Synchronization": "Synkronisation",
"Synchronization": "Synkronisering",
"Syncing": "Synkroniserar",
"Syncthing has been shut down.": "Syncthing har stängts ner.",
"Syncthing includes the following software or portions thereof:": "Syncthing innehåller de följande mjukvarupaketen eller delar därav:",
"Syncthing is restarting.": "Syncthing startar om.",
"Syncthing is upgrading.": "Syncthing uppgraderas.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar avstängd, eller så är där ett problem med din Internetanslutning. Försöker igen...",
"The aggregated statistics are publicly available at {{url}}": "Aggregerad statistik finns publikt tillgänglig på {{url}}",
"The aggregated statistics are publicly available at {%url%}.": "Aggregerad statistik finns publikt tillgänglig på {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurationen har sparats men inte aktiverats. Syncthing måste startas om för att aktivera den nya konfigurationen.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Den krypterade användarstatistiken skickas dagligen. Den används för att spåra vanliga plattformar, lagringsstorlekar och versioner. Om datan som rapporteras ändras så kommer du att bli tillfrågad igen.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det inmatade nod-ID:t verkar inte korrekt. Det ska vara en 52 teckens sträng bestående av siffror och bokstäver, eventuellt med mellanrum och bindestreck.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det inmatade nod-ID:t verkar inte korrekt. Det ska vara en 52 eller 56 teckens sträng bestående av siffror och bokstäver, eventuellt med mellanrum och bindestreck.",
"The node ID cannot be blank.": "Nod-ID:t kan inte vara blankt.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Nod-ID:t som behövs här kan du hitta i \"Redigera > Visa ID\"-dialogen på den andra noden. Mellanrum och bindestreck är valfria (ignoreras).",
"The number of old versions to keep, per file.": "Antalet gamla versioner som ska behållas, per fil.",
@@ -100,6 +106,7 @@
"Unknown": "Okänt",
"Up to Date": "Helt uppdaterad",
"Upgrade To {%version%}": "Uppgradera till {{version}}",
"Upgrading": "Uppgraderar",
"Upload Rate": "Uppladdningshastighet",
"Usage": "Användande",
"Use Compression": "Använd komprimering",

120
gui/lang-tr.json Normal file
View File

@@ -0,0 +1,120 @@
{
"API Key": "API Anahtarı",
"About": "Hakkında",
"Add Node": "Düğüm ekle",
"Add Repository": "Depo ekle",
"Address": "Adres",
"Addresses": "Adresler",
"Allow Anonymous Usage Reporting?": "Anonim kullanım raporlarına izin ver ?",
"Announce Server": "Duyuru Sunucusu",
"Anonymous Usage Reporting": "Anonim Kullanım Raporlama",
"Bugs": "Hatalar",
"CPU Utilization": "İşlemci Kullanımı",
"Close": "Kapat",
"Connection Error": "Connection Error",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Telif Hakkı © 2014 Jakob Borg ve Katkıda Bulunanlar",
"Delete": "Sil",
"Disconnected": "Bağlantı Kesildi",
"Documentation": "Dökümanlar",
"Download Rate": "İndirme Hızı",
"Edit": "Düzenle",
"Edit Node": "Düğümü Düzenle",
"Edit Repository": "Depoyu düzenle",
"Enable UPnP": "UPnP Etkinleştir",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "IP adresleri eklemek için virgül ile ayırarak \"ip:port\" yazın, ya da \"dynamic\" yazarak otomatik bulma işlemini seçin.",
"Error": "Hata",
"File Versioning": "Dosya Sürümlendirme",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "Değişimleri yoklarken dosya izin bilgilerini ihmal et. FAT dosya sistemlerinde kullanın.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Dosyalar syncthing tarafından değiştirildiğinde ya da silindiğinde, tarih damgalı sürümleri .stversions dizinine taşınır.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Dosyalar diğer düğümlerde yapılan değişikliklerden korunur, ancak bu düğümdeki değişiklikler kümedeki diğer düğümlere gönderilir.",
"Folder": "Dizin",
"GUI Authentication Password": "Kullanıcı arayüzü şifresi",
"GUI Authentication User": "Kullanıcı arayüzü kullanıcı ismi",
"GUI Listen Addresses": "Kullanıcı arayüzü bağlantı adresi",
"Generate": "Oluştur",
"Global Discovery": "Küresel Keşif",
"Global Discovery Server": "Küresel Keşif Sunucusu",
"Global Repository": "Global Depo",
"Idle": "Boşta",
"Ignore Permissions": "İzinleri yoksay",
"Keep Versions": "Sürüm tut",
"Latest Release": "En son sürüm",
"Local Discovery": "Yerel bulma",
"Local Discovery Port": "Yerel bulma portları",
"Local Repository": "Yerel Depo",
"Master Repo": "Master Depo",
"Max File Change Rate (KiB/s)": "Mak. Dosya değiştirme oranı (KB/sn)",
"Max Outstanding Requests": "Maks Öncellikli İstekler",
"No": "Hayır",
"Node ID": "Düğüm ID",
"Node Identification": "Node Identification",
"Node Name": "Düğüm İsmi",
"Notice": "Uyarı",
"OK": "Tamam",
"Offline": "Çevrim dışı",
"Online": "Çevrim içi",
"Out Of Sync": "Senkronize değil",
"Outgoing Rate Limit (KiB/s)": "Yükleme Oranı Limiti (KB/sn)",
"Override Changes": "Değişiklikleri Geçersiz kıl",
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Yerel bilgisayardaki depoya ulaşım yolu. Dizin yoksa yaratılacak. (~) karakterinin kısayol olarak kullanılabileceği yol",
"Please wait": "Lütfen Bekleyin",
"Preview Usage Report": "Kullanım raporunu gözden geçir",
"RAM Utilization": "RAM Kullanımı",
"Reconnect Interval (s)": "Yeniden bağlanma süresi (sn)",
"Repository ID": "Depo ID",
"Repository Master": "Master Depo",
"Repository Path": "Depo Yolu",
"Rescan Interval (s)": "Yeni tarama süresi (sn)",
"Restart": "Yeniden Başlat",
"Restart Needed": "Yeniden başlatma gereklidir",
"Restarting": "Restarting",
"Save": "Kaydet",
"Scanning": "Taranıyor",
"Select the nodes to share this repository with.": "Bu depo ile paylaşılacak olan düğümü seçin.",
"Settings": "Ayarlar",
"Share With Nodes": "Düğüm ile paylaş",
"Shared With": "ile paylaş",
"Short identifier for the repository. Must be the same on all cluster nodes.": "Depo için kısa tanımlayıcı. Tüm küme düğümlerinde aynı olmalı.",
"Show ID": "ID Göster",
"Shown instead of Node ID in the cluster status.": "Küme durumu yerine Düğüm ID göster.",
"Shutdown": "Kapat",
"Source Code": "Kaynak Kodu",
"Start Browser": "Tarayıcıyı Başlat",
"Stopped": "Durduruldu",
"Support / Forum": "Destek / Forum",
"Sync Protocol Listen Addresses": "Sync Protokol Dinleme Adresleri",
"Synchronization": "Senkronizasyon",
"Syncing": "Senkronize ediliyor",
"Syncthing has been shut down.": "Syncthing durduruldu",
"Syncthing includes the following software or portions thereof:": "Syncthing aşağıdaki yazılımları veya bunların bölümlerini içermektedir:",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing görünüşe durdu veya internetin bağlantınızda problem var. Tekrar deniyor....",
"The aggregated statistics are publicly available at {%url%}.": "The aggregated statistics are publicly available at {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Ayarlar kaydedildi ancak aktifleştirilmedi. Aktifleştirmek için Syncthing yeniden başlatılmalı.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Şifrelenmiş kullanım bilgisi günlük olarak gönderilir. Platform, depo büyüklüğü ve uygulama sürümü hakkında bilgi toplanır. Toplanan bilgi çeşidi değişecek olursa, sizden tekrar onay istenecek.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "Girilen düğüm ID'si geçerli gibi gözükmüyor. 52 karakter uzunluğunda, harf ve rakamlardan oluşmalı. Boşlukların ve kısa çizgilerin olup olmaması önemli değildir.",
"The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The node ID cannot be blank.": "Düğüm ID'si boş olamaz.",
"The node ID to enter here can be found in the \"Edit > Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "Buraya girilecek düğüm ID'si diğer düğümde \"Düzenle > ID Göster\" menüsünden bulunabilir. Boşluk ve kısa çizginin olup olmaması önemli değildir. (İhmal edilir)",
"The number of old versions to keep, per file.": "Dosya başına saklanacak eski sürüm.",
"The number of versions must be a number and cannot be blank.": "Sürümlerin sayısı sayı olmalı ve boş bırakılamaz.",
"The repository ID cannot be blank.": "Depo ID boş bırakılamaz.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "Depo ID uzun olmamalı (64 karakter ya da daha az). Sadece harf, rakam, nokta (.), kısa çizgi (-) ve alt çizgi (_) kullanabilirsiniz.",
"The repository ID must be unique.": "Depo ID'si benzersiz olmalıdır.",
"The repository path cannot be blank.": "Depo yolu boş bırakılamaz.",
"Unknown": "Bilinmiyor",
"Up to Date": "Güncel",
"Upgrade To {%version%}": "{{version}} sürümüne yükselt",
"Upgrading": "Upgrading",
"Upload Rate": "Yükleme Oranı",
"Usage": "Kullanım",
"Use Compression": "Sıkıştırma kullan",
"Use HTTPS for GUI": "GUI için HTTPS kullan",
"Version": "Sürüm",
"When adding a new node, keep in mind that this node must be added on the other side too.": "Yeni bir düğüm eklendiğinde unutmayın ki; bu düğüm diğer tarafa da eklenmelidir.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "Unutmayın ki; Depo ID, depoları düğümler arasında bağlamak için kullanılıyor. Büyük - küçük harf duyarlı, ve bütün düğümlerde aynı olmalı.",
"Yes": "Evet",
"You must keep at least one version.": "En az bir sürümü tutmalısınız.",
"items": "öğeler"
}

BIN
gui/logo-text-256.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
gui/logo-text-64.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -10,7 +10,7 @@
<div class="modal-body" ng-transclude>
</div>
<div ng-if="close" class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;Close</button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Close</span></button>
</div>
</div>
</div>

BIN
gui/raleway-400.ttf Normal file
View File

Binary file not shown.

BIN
gui/raleway-700.ttf Normal file
View File

Binary file not shown.

12
gui/raleway.css Normal file
View File

@@ -0,0 +1,12 @@
@font-face {
font-family: 'Raleway';
font-style: normal;
font-weight: 400;
src: local('Raleway'), url(raleway-400.ttf) format('truetype');
}
@font-face {
font-family: 'Raleway';
font-style: normal;
font-weight: 700;
src: local('Raleway Bold'), local('Raleway-Bold'), url(raleway-700.ttf) format('truetype');
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1 +1 @@
var validLangs = ["de","el","en","es","fr","it","pt","ru","sv"]
var validLangs = ["da","de","el","en","es","fr","it","nl","pt","ru","sv","tr"]

View File

@@ -3,5 +3,3 @@
./test-http.sh || exit
./test-merge.sh || exit
./test-delupd.sh || exit
# ./test-folders.sh || exit
./test-reconnect.sh || exit

264
integration/common_test.go Normal file
View File

@@ -0,0 +1,264 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build integration
package integration_test
import (
"crypto/md5"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"log"
mr "math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"time"
)
type syncthingProcess struct {
log string
argv []string
port int
cmd *exec.Cmd
logfd *os.File
}
func (p *syncthingProcess) start() error {
if p.logfd == nil {
logfd, err := os.Create(p.log)
if err != nil {
return err
}
p.logfd = logfd
}
cmd := exec.Command("../bin/syncthing", p.argv...)
cmd.Stdout = p.logfd
cmd.Stderr = p.logfd
cmd.Env = append(env, fmt.Sprintf("STPROFILER=:%d", p.port+1000))
err := cmd.Start()
if err != nil {
return err
}
p.cmd = cmd
return nil
}
func (p *syncthingProcess) stop() {
p.cmd.Process.Kill()
p.cmd.Wait()
}
func (p *syncthingProcess) peerCompletion() (map[string]int, error) {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/rest/debug/peerCompletion", p.port))
if err != nil {
return nil, err
}
defer resp.Body.Close()
comp := map[string]int{}
err = json.NewDecoder(resp.Body).Decode(&comp)
return comp, err
}
type fileGenerator struct {
files int
maxexp int
srcname string
}
func generateFiles(dir string, files, maxexp int, srcname string) error {
fd, err := os.Open(srcname)
if err != nil {
return err
}
for i := 0; i < files; i++ {
n := randomName()
p0 := filepath.Join(dir, string(n[0]), n[0:2])
err = os.MkdirAll(p0, 0755)
if err != nil {
log.Fatal(err)
}
s := 1 << uint(mr.Intn(maxexp))
a := 128 * 1024
if a > s {
a = s
}
s += mr.Intn(a)
src := io.LimitReader(&inifiteReader{fd}, int64(s))
p1 := filepath.Join(p0, n)
dst, err := os.Create(p1)
if err != nil {
return err
}
_, err = io.Copy(dst, src)
if err != nil {
return err
}
err = dst.Close()
if err != nil {
return err
}
err = os.Chmod(p1, os.FileMode(mr.Intn(0777)|0400))
if err != nil {
return err
}
t := time.Now().Add(-time.Duration(mr.Intn(30*86400)) * time.Second)
err = os.Chtimes(p1, t, t)
if err != nil {
return err
}
}
return nil
}
func randomName() string {
var b [16]byte
rand.Reader.Read(b[:])
return fmt.Sprintf("%x", b[:])
}
type inifiteReader struct {
rd io.ReadSeeker
}
func (i *inifiteReader) Read(bs []byte) (int, error) {
n, err := i.rd.Read(bs)
if err == io.EOF {
err = nil
i.rd.Seek(0, 0)
}
return n, err
}
// rm -rf
func removeAll(dirs ...string) error {
for _, dir := range dirs {
err := os.RemoveAll(dir)
if err != nil {
return err
}
}
return nil
}
// Compare a number of directories. Returns nil if the contents are identical,
// otherwise an error describing the first found difference.
func compareDirectories(dirs ...string) error {
chans := make([]chan fileInfo, len(dirs))
for i := range chans {
chans[i] = make(chan fileInfo)
}
abort := make(chan struct{})
for i := range dirs {
startWalker(dirs[i], chans[i], abort)
}
res := make([]fileInfo, len(dirs))
for {
numDone := 0
for i := range chans {
fi, ok := <-chans[i]
if !ok {
numDone++
}
res[i] = fi
}
for i := 1; i < len(res); i++ {
if res[i] != res[0] {
close(abort)
return fmt.Errorf("Mismatch; %#v (%s) != %#v (%s)", res[i], dirs[i], res[0], dirs[0])
}
}
if numDone == len(dirs) {
return nil
}
}
}
type fileInfo struct {
name string
mode os.FileMode
mod time.Time
hash [16]byte
}
func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) {
walker := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
rn, _ := filepath.Rel(dir, path)
if rn == "." {
return nil
}
var f fileInfo
if info.IsDir() {
f = fileInfo{
name: rn,
mode: info.Mode(),
// hash and modtime zero for directories
}
} else {
f = fileInfo{
name: rn,
mode: info.Mode(),
mod: info.ModTime(),
}
sum, err := md5file(path)
if err != nil {
return err
}
f.hash = sum
}
select {
case res <- f:
return nil
case <-abort:
return errors.New("abort")
}
}
go func() {
filepath.Walk(dir, walker)
close(res)
}()
}
func md5file(fname string) (hash [16]byte, err error) {
f, err := os.Open(fname)
if err != nil {
return
}
defer f.Close()
h := md5.New()
io.Copy(h, f)
hb := h.Sum(nil)
copy(hash[:], hb)
return
}

View File

@@ -26,7 +26,7 @@
<reconnectionIntervalS>5</reconnectionIntervalS>
<maxChangeKbps>10000</maxChangeKbps>
<startBrowser>false</startBrowser>
<upnpEnabled>true</upnpEnabled>
<upnpEnabled>false</upnpEnabled>
<urAccepted>-1</urAccepted>
</options>
</configuration>

View File

@@ -28,7 +28,7 @@
<reconnectionIntervalS>5</reconnectionIntervalS>
<maxChangeKbps>10000</maxChangeKbps>
<startBrowser>false</startBrowser>
<upnpEnabled>true</upnpEnabled>
<upnpEnabled>false</upnpEnabled>
<urAccepted>-1</urAccepted>
</options>
</configuration>

View File

@@ -9,6 +9,7 @@ package main
import (
"bufio"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
@@ -27,16 +28,19 @@ var (
)
var jsonEndpoints = []string{
"/rest/model?repo=default",
"/rest/model/version?repo=default",
"/rest/need",
"/rest/connections",
"/rest/completion?node=I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU&repo=default",
"/rest/config",
"/rest/config/sync",
"/rest/system",
"/rest/connections",
"/rest/errors",
// "/rest/discovery",
"/rest/events",
"/rest/lang",
"/rest/model/version?repo=default",
"/rest/model?repo=default",
"/rest/need",
"/rest/nodeid?id=I6KAH7666SLLLB5PFXSOAUFJCDZCYAOMLEKCP2GB32BV5RQST3PSROAU",
"/rest/report",
"/rest/system",
}
func main() {
@@ -61,17 +65,21 @@ func main() {
var tests []testing.InternalTest
tests = append(tests, testing.InternalTest{"TestGetIndex", TestGetIndex})
tests = append(tests, testing.InternalTest{"TestGetVersion", TestGetVersion})
tests = append(tests, testing.InternalTest{"TestGetVersionNoCSRF", TestGetVersion})
tests = append(tests, testing.InternalTest{"TestJSONEndpoints", TestJSONEndpoints})
if len(authUser) > 0 || len(apiKey) > 0 {
tests = append(tests, testing.InternalTest{"TestPOSTNoCSRF", TestPOSTNoCSRF})
if len(authUser) > 0 {
// If we expect authentication, verify that it fails with the wrong password and wrong API key
tests = append(tests, testing.InternalTest{"TestJSONEndpointsNoAuth", TestJSONEndpointsNoAuth})
tests = append(tests, testing.InternalTest{"TestJSONEndpointsIncorrectAuth", TestJSONEndpointsIncorrectAuth})
}
if len(csrfToken) > 0 {
tests = append(tests, testing.InternalTest{"TestJSONEndpointsNoCSRF", TestJSONEndpointsNoCSRF})
// If we have a CSRF token, verify that POST succeeds with it
tests = append(tests, testing.InternalTest{"TestPOSTWithCSRF", TestPOSTWithCSRF})
}
fmt.Printf("Testing HTTP: CSRF=%v, API=%v, Auth=%v\n", len(csrfToken) > 0, len(apiKey) > 0, len(authUser) > 0)
testing.Main(matcher, tests, nil, nil)
}
@@ -145,33 +153,54 @@ func TestJSONEndpoints(t *testing.T) {
for _, p := range jsonEndpoints {
res, err := get(p)
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
if res.StatusCode != 200 {
t.Errorf("Status %d != 200 for %q", res.StatusCode, p)
continue
}
if ct := res.Header.Get("Content-Type"); ct != "application/json; charset=utf-8" {
t.Errorf("Content-Type %q != \"application/json\" for %q", ct, p)
continue
}
}
}
func TestJSONEndpointsNoCSRF(t *testing.T) {
for _, p := range jsonEndpoints {
r, err := http.NewRequest("GET", "http://"+target+p, nil)
if err != nil {
t.Fatal(err)
}
if len(authUser) > 0 {
r.SetBasicAuth(authUser, authPass)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 403 && res.StatusCode != 401 {
t.Fatalf("Status %d != 403/401 for %q", res.StatusCode, p)
}
func TestPOSTNoCSRF(t *testing.T) {
r, err := http.NewRequest("POST", "http://"+target+"/rest/error/clear", nil)
if err != nil {
t.Fatal(err)
}
if len(authUser) > 0 {
r.SetBasicAuth(authUser, authPass)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 403 && res.StatusCode != 401 {
t.Fatalf("Status %d != 403/401 for POST", res.StatusCode)
}
}
func TestPOSTWithCSRF(t *testing.T) {
r, err := http.NewRequest("POST", "http://"+target+"/rest/error/clear", nil)
if err != nil {
t.Fatal(err)
}
if len(csrfToken) > 0 {
r.Header.Set("X-CSRF-Token", csrfToken)
}
if len(authUser) > 0 {
r.SetBasicAuth(authUser, authPass)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 {
t.Fatalf("Status %d != 200 for POST", res.StatusCode)
}
}
@@ -179,17 +208,20 @@ func TestJSONEndpointsNoAuth(t *testing.T) {
for _, p := range jsonEndpoints {
r, err := http.NewRequest("GET", "http://"+target+p, nil)
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
if len(csrfToken) > 0 {
r.Header.Set("X-CSRF-Token", csrfToken)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
if res.StatusCode != 403 && res.StatusCode != 401 {
t.Fatalf("Status %d != 403/401 for %q", res.StatusCode, p)
t.Errorf("Status %d != 403/401 for %q", res.StatusCode, p)
continue
}
}
}
@@ -198,7 +230,8 @@ func TestJSONEndpointsIncorrectAuth(t *testing.T) {
for _, p := range jsonEndpoints {
r, err := http.NewRequest("GET", "http://"+target+p, nil)
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
if len(csrfToken) > 0 {
r.Header.Set("X-CSRF-Token", csrfToken)
@@ -206,10 +239,12 @@ func TestJSONEndpointsIncorrectAuth(t *testing.T) {
r.SetBasicAuth("wronguser", "wrongpass")
res, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
if res.StatusCode != 403 && res.StatusCode != 401 {
t.Fatalf("Status %d != 403/401 for %q", res.StatusCode, p)
t.Errorf("Status %d != 403/401 for %q", res.StatusCode, p)
continue
}
}
}
@@ -222,9 +257,6 @@ func get(path string) (*http.Response, error) {
if len(authUser) > 0 {
r.SetBasicAuth(authUser, authPass)
}
if len(csrfToken) > 0 {
r.Header.Set("X-CSRF-Token", csrfToken)
}
if len(apiKey) > 0 {
r.Header.Set("X-API-Key", apiKey)
}

View File

@@ -0,0 +1,141 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build integration
package integration_test
import (
"sync"
"testing"
"time"
)
const (
apiKey = "abc123" // Used when talking to the processes under test
id1 = "I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"
id2 = "JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU"
)
var env = []string{
"HOME=.",
"STTRACE=model",
}
func TestRestartBothDuringTransfer(t *testing.T) {
// Give the receiver some time to rot with needed files but
// without any peer. This triggers
// https://github.com/syncthing/syncthing/issues/463
testRestartDuringTransfer(t, true, true, 10*time.Second, 0)
}
func TestRestartReceiverDuringTransfer(t *testing.T) {
testRestartDuringTransfer(t, false, true, 0, 0)
}
func TestRestartSenderDuringTransfer(t *testing.T) {
testRestartDuringTransfer(t, true, false, 0, 0)
}
func testRestartDuringTransfer(t *testing.T, restartSender, restartReceiver bool, senderDelay, receiverDelay time.Duration) {
t.Log("Cleaning...")
err := removeAll("s1", "s2", "f1/index", "f2/index")
if err != nil {
t.Fatal(err)
}
t.Log("Generating files...")
err = generateFiles("s1", 1000, 20, "../bin/syncthing")
if err != nil {
t.Fatal(err)
}
t.Log("Starting up...")
sender := syncthingProcess{ // id1
log: "1.out",
argv: []string{"-home", "f1"},
port: 8081,
}
err = sender.start()
if err != nil {
t.Fatal(err)
}
receiver := syncthingProcess{ // id2
log: "2.out",
argv: []string{"-home", "f2"},
port: 8082,
}
err = receiver.start()
if err != nil {
t.Fatal(err)
}
// Give them time to start up
time.Sleep(1 * time.Second)
var prevComp int
for {
comp, err := sender.peerCompletion()
if err != nil {
sender.stop()
receiver.stop()
t.Fatal(err)
}
curComp := comp[id2]
if curComp == 100 {
sender.stop()
receiver.stop()
break
}
if curComp > prevComp {
if restartReceiver {
t.Logf("Stopping receiver...")
receiver.stop()
}
if restartSender {
t.Logf("Stopping sender...")
sender.stop()
}
var wg sync.WaitGroup
if restartReceiver {
wg.Add(1)
go func() {
time.Sleep(receiverDelay)
t.Logf("Starting receiver...")
receiver.start()
wg.Done()
}()
}
if restartSender {
wg.Add(1)
go func() {
time.Sleep(senderDelay)
t.Logf("Starting sender...")
sender.start()
wg.Done()
}()
}
wg.Wait()
prevComp = curComp
}
time.Sleep(1 * time.Second)
}
t.Log("Comparing directories...")
err = compareDirectories("s1", "s2")
if err != nil {
t.Fatal(err)
}
}

View File

@@ -91,6 +91,7 @@ alterFiles() {
for i in 1 12-2 23-3 ; do
# Delete some files
pushd "s$i" >/dev/null
chmod 755 ro-test
nfiles=$(find . -type f | wc -l)
if [[ $nfiles -ge 300 ]] ; then
todelete=$(( $nfiles - 300 ))
@@ -107,6 +108,10 @@ alterFiles() {
../genfiles -maxexp 22 -files 200
echo " $i: append to large file"
dd if=large-$i bs=1024k count=4 >> large-$i 2>/dev/null
echo " $i: new files in ro directory"
uuidgen > ro-test/$(uuidgen)
chmod 500 ro-test
../md5r -l | sort | grep -v .stversions > ../md5-$i
popd >/dev/null
done
@@ -118,6 +123,7 @@ alterFiles() {
}
rm -rf h?/*.idx.gz h?/index
chmod -R u+w s? s??-?
rm -rf s? s??-?
mkdir s1 s2 s3 s12-1 s12-2 s23-2 s23-3
@@ -126,6 +132,10 @@ for i in 1 12-2 23-3; do
pushd "s$i" >/dev/null
echo " $i: random nonoverlapping"
../genfiles -maxexp 22 -files 400
echo " $i: ro directory"
mkdir ro-test
uuidgen > ro-test/$(uuidgen)
chmod 500 ro-test
popd >/dev/null
done

View File

@@ -1,87 +0,0 @@
#!/bin/bash
# Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
# Use of this source code is governed by an MIT-style license that can be
# found in the LICENSE file.
id1=I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU
id2=JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU
go build json.go
go build md5r.go
go build genfiles.go
start() {
echo "Starting..."
STTRACE=model,scanner STPROFILER=":9091" syncthing -home "f1" > 1.out 2>&1 &
STTRACE=model,scanner STPROFILER=":9092" syncthing -home "f2" > 2.out 2>&1 &
sleep 1
}
stop() {
echo "Stopping..."
for i in 1 2 ; do
curl -HX-API-Key:abc123 -X POST "http://localhost:808$i/rest/shutdown"
done
sleep 1
}
setup() {
echo "Setting up..."
rm -rf s? s??-?
rm -rf f?/*.idx.gz f?/index
mkdir -p s1
pushd s1 >/dev/null
../genfiles
../md5r > ../md5-1
popd >/dev/null
}
testConvergence() {
torestart="$1"
prevcomp=0
while true ; do
sleep 5
comp=$(curl -HX-API-Key:abc123 -s "http://localhost:8081/rest/debug/peerCompletion" | ./json "$id2")
comp=${comp:-0}
echo $comp / 100
if [[ $comp == 100 ]] ; then
echo Done
break
fi
# Restart if the destination has made some progress
if [[ $comp -gt $prevcomp ]] ; then
prevcomp=$comp
curl -HX-API-Key:abc123 -X POST "http://localhost:$torestart/rest/restart"
fi
done
echo "Verifying..."
pushd s2 >/dev/null
../md5r | grep -v .stversions > ../md5-2
popd >/dev/null
if ! cmp md5-1 md5-2 ; then
echo Repos differ
stop
exit 1
fi
}
echo Testing reconnects during pull where the source node restarts
setup
start
testConvergence 8081
stop
echo Testing reconnects during pull where the destination node restarts
setup
start
testConvergence 8082
stop
exit 0

View File

@@ -3,7 +3,7 @@ package luhn_test
import (
"testing"
"github.com/calmh/syncthing/luhn"
"github.com/syncthing/syncthing/luhn"
)
func TestGenerate(t *testing.T) {

View File

@@ -4,7 +4,7 @@
package model
import "github.com/calmh/syncthing/protocol"
import "github.com/syncthing/syncthing/protocol"
type bqAdd struct {
file protocol.FileInfo

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -15,12 +15,12 @@ import (
"sync"
"time"
"github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/events"
"github.com/calmh/syncthing/files"
"github.com/calmh/syncthing/lamport"
"github.com/calmh/syncthing/protocol"
"github.com/calmh/syncthing/scanner"
"github.com/syncthing/syncthing/config"
"github.com/syncthing/syncthing/events"
"github.com/syncthing/syncthing/files"
"github.com/syncthing/syncthing/lamport"
"github.com/syncthing/syncthing/protocol"
"github.com/syncthing/syncthing/scanner"
"github.com/syndtr/goleveldb/leveldb"
)
@@ -199,7 +199,15 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
// Returns the completion status, in percent, for the given node and repo.
func (m *Model) Completion(node protocol.NodeID, repo string) float64 {
var tot int64
m.repoFiles[repo].WithGlobal(func(f protocol.FileInfo) bool {
m.rmut.RLock()
rf, ok := m.repoFiles[repo]
m.rmut.RUnlock()
if !ok {
return 0 // Repo doesn't exist, so we hardly have any of it
}
rf.WithGlobal(func(f protocol.FileInfo) bool {
if !protocol.IsDeleted(f.Flags) {
var size int64
if protocol.IsDirectory(f.Flags) {
@@ -212,8 +220,12 @@ func (m *Model) Completion(node protocol.NodeID, repo string) float64 {
return true
})
if tot == 0 {
return 100 // Repo is empty, so we have all of it
}
var need int64
m.repoFiles[repo].WithNeed(node, func(f protocol.FileInfo) bool {
rf.WithNeed(node, func(f protocol.FileInfo) bool {
if !protocol.IsDeleted(f.Flags) {
var size int64
if protocol.IsDirectory(f.Flags) {
@@ -359,7 +371,7 @@ func (m *Model) IndexUpdate(nodeID protocol.NodeID, repo string, fs []protocol.F
}
if !m.repoSharedWith(repo, nodeID) {
l.Warnf("Unexpected repository ID %q sent from node %q; ensure that the repository exists and that this node is selected under \"Share With\" in the repository configuration.", repo, nodeID)
l.Infof("Update for unexpected repository ID %q sent from node %q; ensure that the repository exists and that this node is selected under \"Share With\" in the repository configuration.", repo, nodeID)
return
}

View File

@@ -11,8 +11,8 @@ import (
"testing"
"time"
"github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/config"
"github.com/syncthing/syncthing/protocol"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/storage"
)

View File

@@ -7,16 +7,17 @@ package model
import (
"bytes"
"errors"
"math/rand"
"os"
"path/filepath"
"time"
"github.com/calmh/syncthing/config"
"github.com/calmh/syncthing/events"
"github.com/calmh/syncthing/osutil"
"github.com/calmh/syncthing/protocol"
"github.com/calmh/syncthing/scanner"
"github.com/calmh/syncthing/versioner"
"github.com/syncthing/syncthing/config"
"github.com/syncthing/syncthing/events"
"github.com/syncthing/syncthing/osutil"
"github.com/syncthing/syncthing/protocol"
"github.com/syncthing/syncthing/scanner"
"github.com/syncthing/syncthing/versioner"
)
type requestResult struct {
@@ -72,6 +73,7 @@ type puller struct {
blocks chan bqBlock
requestResults chan requestResult
versioner versioner.Versioner
errors int
}
func newPuller(repoCfg config.RepositoryConfiguration, model *Model, slots int, cfg *config.Configuration) *puller {
@@ -128,6 +130,7 @@ func (p *puller) run() {
prevVer, queued = p.queueNeededBlocks(prevVer)
if queued > 0 {
p.errors = 0
pull:
for {
@@ -145,6 +148,11 @@ func (p *puller) run() {
if debug {
l.Debugf("%q: pulling loop needs more blocks", p.repoCfg.ID)
}
if p.errors > 0 && p.errors >= queued {
break pull
}
prevVer, _ = p.queueNeededBlocks(prevVer)
b, ok = p.bq.get()
}
@@ -179,6 +187,12 @@ func (p *puller) run() {
}
}
}
if p.errors > 0 && p.errors >= queued {
l.Warnf("All remaining files failed to sync. Stopping repo %q.", p.repoCfg.ID)
invalidateRepo(p.cfg, p.repoCfg.ID, errors.New("too many errors, check logs"))
return
}
}
if changed {
@@ -365,7 +379,8 @@ func (p *puller) handleBlock(b bqBlock) bool {
}
err = os.MkdirAll(path, os.FileMode(f.Flags&0777))
if err != nil {
l.Warnf("Create folder: %q: %v", path, err)
p.errors++
l.Infof("mkdir: error: %q: %v", path, err)
}
}
} else if debug {
@@ -384,13 +399,13 @@ func (p *puller) handleBlock(b bqBlock) bool {
fp := filepath.Join(p.repoCfg.Directory, f.Name)
t := time.Unix(f.Modified, 0)
err := os.Chtimes(fp, t, t)
if debug && err != nil {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
if err != nil {
l.Infof("chtimes: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
if !p.repoCfg.IgnorePerms && protocol.HasPermissionBits(f.Flags) {
err = os.Chmod(fp, os.FileMode(f.Flags&0777))
if debug && err != nil {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
if err != nil {
l.Infof("chmod: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
}
@@ -424,16 +439,20 @@ func (p *puller) handleBlock(b bqBlock) bool {
_, err := os.Stat(dirName)
if err != nil {
err = os.MkdirAll(dirName, 0777)
} else {
// We need to make sure the directory is writeable so we can create files in it
if (dirName != p.repoCfg.Directory) {
err = os.Chmod(dirName, 0777)
}
}
if err != nil {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
l.Infof("mkdir: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
of.file, of.err = os.Create(of.temp)
if of.err != nil {
if debug {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
}
p.errors++
l.Infof("create: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
if !b.last {
p.openFiles[f.Name] = of
}
@@ -482,9 +501,8 @@ func (p *puller) handleCopyBlock(b bqBlock) {
var exfd *os.File
exfd, of.err = os.Open(of.filepath)
if of.err != nil {
if debug {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
}
p.errors++
l.Infof("open: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
of.file.Close()
of.file = nil
@@ -500,9 +518,8 @@ func (p *puller) handleCopyBlock(b bqBlock) {
_, of.err = of.file.WriteAt(bs, b.Offset)
}
if of.err != nil {
if debug {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
}
p.errors++
l.Infof("write: error: %q / %q: %v", p.repoCfg.ID, f.Name, of.err)
exfd.Close()
of.file.Close()
of.file = nil
@@ -585,7 +602,13 @@ func (p *puller) handleEmptyBlock(b bqBlock) {
l.Debugf("pull: delete %q", f.Name)
}
os.Remove(of.temp)
// Ensure the file and the directory it is in is writeable so we can remove the file
dirName := filepath.Dir(of.filepath)
os.Chmod(of.filepath, 0666)
if (dirName != p.repoCfg.Directory) {
os.Chmod(dirName, 0777)
}
if p.versioner != nil {
if debug {
l.Debugln("pull: deleting with versioner")
@@ -630,10 +653,17 @@ func (p *puller) queueNeededBlocks(prevVer uint64) (uint64, int) {
}
queued := 0
files := make([]protocol.FileInfo, 0, indexBatchSize)
for _, f := range p.model.NeedFilesRepo(p.repoCfg.ID) {
if _, ok := p.openFiles[f.Name]; ok {
continue
}
files = append(files, f)
}
perm := rand.Perm(len(files))
for _, idx := range perm {
f := files[idx]
lf := p.model.CurrentRepoFile(p.repoCfg.ID, f.Name)
have, need := scanner.BlockDiff(lf.Blocks, f.Blocks)
if debug {
@@ -646,6 +676,7 @@ func (p *puller) queueNeededBlocks(prevVer uint64) (uint64, int) {
need: need,
})
}
if debug && queued > 0 {
l.Debugf("%q: queued %d items", p.repoCfg.ID, queued)
}
@@ -663,16 +694,19 @@ func (p *puller) closeFile(f protocol.FileInfo) {
}
of := p.openFiles[f.Name]
of.file.Close()
err := of.file.Close()
if err != nil {
p.errors++
l.Infof("close: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
defer os.Remove(of.temp)
delete(p.openFiles, f.Name)
fd, err := os.Open(of.temp)
if err != nil {
if debug {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
p.errors++
l.Infof("open: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
return
}
hb, _ := scanner.Blocks(fd, scanner.StandardBlockSize)
@@ -687,20 +721,22 @@ func (p *puller) closeFile(f protocol.FileInfo) {
for i := range hb {
if bytes.Compare(hb[i].Hash, f.Blocks[i].Hash) != 0 {
l.Debugf("pull: %q / %q: block %d hash mismatch\n\thave: %x\n\twant: %x", p.repoCfg.ID, f.Name, i, hb[i].Hash, f.Blocks[i].Hash)
if debug {
l.Debugf("pull: %q / %q: block %d hash mismatch\n have: %x\n want: %x", p.repoCfg.ID, f.Name, i, hb[i].Hash, f.Blocks[i].Hash)
}
return
}
}
t := time.Unix(f.Modified, 0)
err = os.Chtimes(of.temp, t, t)
if debug && err != nil {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
if err != nil {
l.Infof("chtimes: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
if !p.repoCfg.IgnorePerms && protocol.HasPermissionBits(f.Flags) {
err = os.Chmod(of.temp, os.FileMode(f.Flags&0777))
if debug && err != nil {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
if err != nil {
l.Infof("chmod: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
}
@@ -722,7 +758,8 @@ func (p *puller) closeFile(f protocol.FileInfo) {
if err := osutil.Rename(of.temp, of.filepath); err == nil {
p.model.updateLocal(p.repoCfg.ID, f)
} else {
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
p.errors++
l.Infof("rename: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
}
}

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -9,7 +9,7 @@ import (
"regexp"
"strings"
"github.com/calmh/syncthing/luhn"
"github.com/syncthing/syncthing/luhn"
)
type NodeID [32]byte

View File

@@ -7,6 +7,7 @@ package protocol
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
@@ -17,7 +18,6 @@ import (
"testing/quick"
"github.com/calmh/xdr"
pretty "github.com/tonnerre/golang-pretty"
)
var (
@@ -251,6 +251,11 @@ func TestElementSizeExceededNested(t *testing.T) {
}
func TestMarshalIndexMessage(t *testing.T) {
var quickCfg = &quick.Config{MaxCountScale: 10}
if testing.Short() {
quickCfg = nil
}
f := func(m1 IndexMessage) bool {
for _, f := range m1.Files {
for i := range f.Blocks {
@@ -264,22 +269,32 @@ func TestMarshalIndexMessage(t *testing.T) {
return testMarshal(t, "index", &m1, &IndexMessage{})
}
if err := quick.Check(f, &quick.Config{MaxCountScale: 10}); err != nil {
if err := quick.Check(f, quickCfg); err != nil {
t.Error(err)
}
}
func TestMarshalRequestMessage(t *testing.T) {
var quickCfg = &quick.Config{MaxCountScale: 10}
if testing.Short() {
quickCfg = nil
}
f := func(m1 RequestMessage) bool {
return testMarshal(t, "request", &m1, &RequestMessage{})
}
if err := quick.Check(f, &quick.Config{MaxCountScale: 10}); err != nil {
if err := quick.Check(f, quickCfg); err != nil {
t.Error(err)
}
}
func TestMarshalResponseMessage(t *testing.T) {
var quickCfg = &quick.Config{MaxCountScale: 10}
if testing.Short() {
quickCfg = nil
}
f := func(m1 ResponseMessage) bool {
if len(m1.Data) == 0 {
m1.Data = nil
@@ -287,27 +302,37 @@ func TestMarshalResponseMessage(t *testing.T) {
return testMarshal(t, "response", &m1, &ResponseMessage{})
}
if err := quick.Check(f, &quick.Config{MaxCountScale: 10}); err != nil {
if err := quick.Check(f, quickCfg); err != nil {
t.Error(err)
}
}
func TestMarshalClusterConfigMessage(t *testing.T) {
var quickCfg = &quick.Config{MaxCountScale: 10}
if testing.Short() {
quickCfg = nil
}
f := func(m1 ClusterConfigMessage) bool {
return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{})
}
if err := quick.Check(f, &quick.Config{MaxCountScale: 10}); err != nil {
if err := quick.Check(f, quickCfg); err != nil {
t.Error(err)
}
}
func TestMarshalCloseMessage(t *testing.T) {
var quickCfg = &quick.Config{MaxCountScale: 10}
if testing.Short() {
quickCfg = nil
}
f := func(m1 CloseMessage) bool {
return testMarshal(t, "close", &m1, &CloseMessage{})
}
if err := quick.Check(f, &quick.Config{MaxCountScale: 10}); err != nil {
if err := quick.Check(f, quickCfg); err != nil {
t.Error(err)
}
}
@@ -321,12 +346,10 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
var buf bytes.Buffer
failed := func(bc []byte) {
f, _ := os.Create(prefix + "-1.txt")
pretty.Fprintf(f, "%# v", m1)
f.Close()
f, _ = os.Create(prefix + "-2.txt")
pretty.Fprintf(f, "%# v", m2)
f.Close()
bs, _ := json.MarshalIndent(m1, "", " ")
ioutil.WriteFile(prefix+"-1.txt", bs, 0644)
bs, _ = json.MarshalIndent(m2, "", " ")
ioutil.WriteFile(prefix+"-2.txt", bs, 0644)
if len(bc) > 0 {
f, _ := os.Create(prefix + "-data.txt")
fmt.Fprint(f, hex.Dump(bc))

View File

@@ -9,7 +9,7 @@ import (
"path/filepath"
"sync"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/protocol"
)
// The parallell hasher reads FileInfo structures from the inbox, hashes the

View File

@@ -9,7 +9,7 @@ import (
"crypto/sha256"
"io"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/protocol"
)
const StandardBlockSize = 128 * 1024

View File

@@ -9,7 +9,7 @@ import (
"fmt"
"testing"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/protocol"
)
var blocksTestData = []struct {

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -15,8 +15,8 @@ import (
"strings"
"code.google.com/p/go.text/unicode/norm"
"github.com/calmh/syncthing/lamport"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/lamport"
"github.com/syncthing/syncthing/protocol"
)
type Walker struct {

View File

@@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/calmh/syncthing/protocol"
"github.com/syncthing/syncthing/protocol"
)
var testdata = []struct {

17
upgrade/debug.go Normal file
View File

@@ -0,0 +1,17 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package upgrade
import (
"os"
"strings"
"github.com/syncthing/syncthing/logger"
)
var (
debug = strings.Contains(os.Getenv("STTRACE"), "upgrade") || os.Getenv("STTRACE") == "all"
l = logger.DefaultLogger
)

View File

@@ -114,7 +114,8 @@ func CompareVersions(a, b string) int {
// Split a version into parts.
// "1.2.3-beta.2" -> []int{1, 2, 3}, []interface{}{"beta", 2}
func versionParts(v string) ([]int, []interface{}) {
parts := strings.SplitN(v, "-", 2)
parts := strings.SplitN(v, "+", 2)
parts = strings.SplitN(parts[0], "-", 2)
fields := strings.Split(parts[0], ".")
release := make([]int, len(fields))

View File

@@ -23,17 +23,27 @@ import (
"bitbucket.org/kardianos/osext"
)
var GoArchExtra string // "", "v5", "v6", "v7"
// Upgrade to the given release, saving the previous binary with a ".old" extension.
func UpgradeTo(rel Release) error {
func UpgradeTo(rel Release, archExtra string) error {
path, err := osext.Executable()
if err != nil {
return err
}
expectedRelease := fmt.Sprintf("syncthing-%s-%s%s-%s.", runtime.GOOS, runtime.GOARCH, GoArchExtra, rel.Tag)
osName := runtime.GOOS
if osName == "darwin" {
// We call the darwin release bundles macosx because that makes more
// sense for people downloading them
osName = "macosx"
}
expectedRelease := fmt.Sprintf("syncthing-%s-%s%s-%s.", osName, runtime.GOARCH, archExtra, rel.Tag)
if debug {
l.Debugf("expected release asset %q", expectedRelease)
}
for _, asset := range rel.Assets {
if debug {
l.Debugln("considering release", asset)
}
if strings.HasPrefix(asset.Name, expectedRelease) {
if strings.HasSuffix(asset.Name, ".tar.gz") {
fname, err := readTarGZ(asset.URL, filepath.Dir(path))
@@ -60,7 +70,7 @@ func UpgradeTo(rel Release) error {
// Returns the latest release, including prereleases or not depending on the argument
func LatestRelease(prerelease bool) (Release, error) {
resp, err := http.Get("https://api.github.com/repos/calmh/syncthing/releases?per_page=10")
resp, err := http.Get("https://api.github.com/repos/syncthing/syncthing/releases?per_page=10")
if err != nil {
return Release{}, err
}
@@ -91,6 +101,10 @@ func LatestRelease(prerelease bool) (Release, error) {
}
func readTarGZ(url string, dir string) (string, error) {
if debug {
l.Debugf("loading %q", url)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
@@ -123,6 +137,9 @@ func readTarGZ(url string, dir string) (string, error) {
if err != nil {
return "", err
}
if debug {
l.Debugf("considering file %q", hdr.Name)
}
if path.Base(hdr.Name) == "syncthing" {
of, err := ioutil.TempFile(dir, "syncthing")

View File

@@ -28,6 +28,9 @@ var testcases = []struct {
{"1.0.0-beta.2", "1.0.0-beta.11", -1},
{"1.0.0-beta.11", "1.0.0-rc.1", -1},
{"1.0.0-rc.1", "1.0.0", -1},
{"1.0.0+45", "1.0.0+23-dev-foo", 0},
{"1.0.0-beta.23+45", "1.0.0-beta.23+23-dev-foo", 0},
{"1.0.0-beta.3+99", "1.0.0-beta.24+0", -1},
}
func TestCompareVersions(t *testing.T) {

View File

@@ -2,11 +2,11 @@
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build windows solaris noupgrade
// +build solaris noupgrade
package upgrade
func UpgradeTo(rel Release) error {
func UpgradeTo(rel Release, extra string) error {
return ErrUpgradeUnsupported
}

166
upgrade/upgrade_windows.go Executable file
View File

@@ -0,0 +1,166 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build windows,!noupgrade
package upgrade
import (
"archive/zip"
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"bitbucket.org/kardianos/osext"
)
// Upgrade to the given release, saving the previous binary with a ".old" extension.
func UpgradeTo(rel Release, archExtra string) error {
path, err := osext.Executable()
if err != nil {
return err
}
expectedRelease := fmt.Sprintf("syncthing-%s-%s%s-%s.", runtime.GOOS, runtime.GOARCH, archExtra, rel.Tag)
if debug {
l.Debugf("expected release asset %q", expectedRelease)
}
for _, asset := range rel.Assets {
if debug {
l.Debugln("considering release", asset)
}
if strings.HasPrefix(asset.Name, expectedRelease) {
if strings.HasSuffix(asset.Name, ".zip") {
fname, err := readZip(asset.URL, filepath.Dir(path))
if err != nil {
return err
}
old := path + ".old"
os.Remove(old)
err = os.Rename(path, old)
if err != nil {
return err
}
err = os.Rename(fname, path)
if err != nil {
return err
}
return nil
}
}
}
return ErrVersionUnknown
}
// Returns the latest release, including prereleases or not depending on the argument
func LatestRelease(prerelease bool) (Release, error) {
resp, err := http.Get("https://api.github.com/repos/syncthing/syncthing/releases?per_page=10")
if err != nil {
return Release{}, err
}
if resp.StatusCode > 299 {
return Release{}, fmt.Errorf("API call returned HTTP error: %s", resp.Status)
}
var rels []Release
json.NewDecoder(resp.Body).Decode(&rels)
resp.Body.Close()
if len(rels) == 0 {
return Release{}, ErrVersionUnknown
}
if prerelease {
// We are a beta version. Use the latest.
return rels[0], nil
} else {
// We are a regular release. Only consider non-prerelease versions for upgrade.
for _, rel := range rels {
if !rel.Prerelease {
return rel, nil
}
}
return Release{}, ErrVersionUnknown
}
}
func readZip(url, dir string) (string, error) {
if debug {
l.Debugf("loading %q", url)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
req.Header.Add("Accept", "application/octet-stream")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
archive, err := zip.NewReader(bytes.NewReader(body), resp.ContentLength)
if err != nil {
return "", err
}
// Iterate through the files in the archive.
for _, file := range archive.File {
if debug {
l.Debugf("considering file %q", file.Name)
}
if path.Base(file.Name) == "syncthing.exe" {
infile, err := file.Open()
if err != nil {
return "", err
}
outfile, err := ioutil.TempFile(dir, "syncthing")
if err != nil {
return "", err
}
_, err = io.Copy(outfile, infile)
if err != nil {
return "", err
}
err = infile.Close()
if err != nil {
return "", err
}
err = outfile.Close()
if err != nil {
os.Remove(outfile.Name())
return "", err
}
os.Chmod(outfile.Name(), file.Mode())
return outfile.Name(), nil
}
}
return "", fmt.Errorf("No upgrade found")
}

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

View File

@@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/calmh/syncthing/logger"
"github.com/syncthing/syncthing/logger"
)
var (

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