mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-03 11:29:10 -05:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2e5643c3c | ||
|
|
956d212e99 | ||
|
|
60c218efdb | ||
|
|
8d290eb055 | ||
|
|
cd17aa2cab | ||
|
|
edaff81c0a | ||
|
|
dd7d6188f2 | ||
|
|
9be6f1a70e | ||
|
|
b0cce98648 | ||
|
|
e159f76ced | ||
|
|
07d3af21c2 | ||
|
|
35316e9142 | ||
|
|
2a0775da03 | ||
|
|
8bd67f72dd | ||
|
|
82f190285a | ||
|
|
f5590c3345 | ||
|
|
9be07de7c6 | ||
|
|
802b933778 | ||
|
|
f1ec7fe55b | ||
|
|
d842197931 | ||
|
|
4e7510dea9 | ||
|
|
c0f353c0e8 | ||
|
|
e95d005c21 | ||
|
|
11e9d575c8 | ||
|
|
46bbc78e82 | ||
|
|
50a621bc7b | ||
|
|
d0c84916c7 | ||
|
|
6db8dc33f2 | ||
|
|
194501c958 | ||
|
|
27a34609a1 | ||
|
|
ffc14a77c6 | ||
|
|
31119ed61a | ||
|
|
070bf3b776 | ||
|
|
ade8d79d42 | ||
|
|
42917d707d | ||
|
|
1522cf74bc | ||
|
|
3b7a57d108 | ||
|
|
a7d9268e4d | ||
|
|
052dc13487 | ||
|
|
249bcb3a01 | ||
|
|
fbe52faf49 | ||
|
|
8b86171642 | ||
|
|
e19d6e993d | ||
|
|
ef0473c091 | ||
|
|
3406a3ba95 | ||
|
|
7c1ed420a9 | ||
|
|
d7e86af6c6 | ||
|
|
76cf326290 | ||
|
|
6c3e187d1d | ||
|
|
e32a516b5f | ||
|
|
6da83ac9f5 | ||
|
|
adc07eddf6 | ||
|
|
9c88efd55f | ||
|
|
ffcb57580f | ||
|
|
abfbd13f17 | ||
|
|
f63cdbfcfa | ||
|
|
b2d82da20d | ||
|
|
70fddb6523 | ||
|
|
83cfd308b4 | ||
|
|
36acaf5e21 | ||
|
|
572ccfe3e2 | ||
|
|
253049a4cc | ||
|
|
8f199e12b3 |
4
AUTHORS
4
AUTHORS
@@ -61,9 +61,11 @@ Carsten Hagemann (carstenhag) <moter8@gmail.com> <carsten@chagemann.de>
|
||||
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
|
||||
Cedric Staniewski (xduugu) <cedric@gmx.ca>
|
||||
chenrui <rui@meetup.com>
|
||||
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
|
||||
Chris Howie (cdhowie) <me@chrishowie.com>
|
||||
Chris Joel (cdata) <chris@scriptolo.gy>
|
||||
Chris Tonkinson <chris@masterbran.ch>
|
||||
Christian Prescott <me@christianprescott.com>
|
||||
chucic <chucic@seznam.cz>
|
||||
Colin Kennedy (moshen) <moshen.colin@gmail.com>
|
||||
Cromefire_ <tim.l@nghorst.net> <26320625+cromefire@users.noreply.github.com>
|
||||
@@ -126,6 +128,7 @@ Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
||||
jelle van der Waa <jelle@vdwaa.nl>
|
||||
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
|
||||
Jerry Jacobs (xor-gate) <jerry.jacobs@xor-gate.org> <xor-gate@users.noreply.github.com>
|
||||
Jesse Lucas <jesse@jesselucas.com>
|
||||
Jochen Voss (seehuhn) <voss@seehuhn.de>
|
||||
Johan Andersson <j@i19.se>
|
||||
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
||||
@@ -212,6 +215,7 @@ Phill Luby (pluby) <phill.luby@newredo.com>
|
||||
Pier Paolo Ramon <ramonpierre@gmail.com>
|
||||
Piotr Bejda (piobpl) <piotrb10@gmail.com>
|
||||
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
|
||||
Quentin Hibon <qh.public@yahoo.com>
|
||||
Rahmi Pruitt <rjpruitt16@gmail.com>
|
||||
Richard Hartmann <RichiH@users.noreply.github.com>
|
||||
Robert Carosi (nov1n) <robert@carosi.nl>
|
||||
|
||||
13
build.go
13
build.go
@@ -666,11 +666,14 @@ func shouldBuildSyso(dir string) (string, error) {
|
||||
},
|
||||
},
|
||||
"StringFileInfo": M{
|
||||
"FileDescription": "Open Source Continuous File Synchronization",
|
||||
"LegalCopyright": "The Syncthing Authors",
|
||||
"FileVersion": version,
|
||||
"ProductVersion": version,
|
||||
"ProductName": "Syncthing",
|
||||
"CompanyName": "The Syncthing Authors",
|
||||
"FileDescription": "Syncthing - Open Source Continuous File Synchronization",
|
||||
"FileVersion": version,
|
||||
"InternalName": "syncthing",
|
||||
"LegalCopyright": "The Syncthing Authors",
|
||||
"OriginalFilename": "syncthing",
|
||||
"ProductName": "Syncthing",
|
||||
"ProductVersion": version,
|
||||
},
|
||||
"IconPath": "assets/logo.ico",
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ func main() {
|
||||
mux.Handle("/", cr)
|
||||
|
||||
if *dsn != "" {
|
||||
mux.HandleFunc("/failure", handleFailureFn(*dsn))
|
||||
mux.HandleFunc("/newcrash/failure", handleFailureFn(*dsn))
|
||||
}
|
||||
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
@@ -186,10 +186,10 @@ func main() {
|
||||
}
|
||||
|
||||
wrapper := config.Wrap("config", config.New(id), id, events.NoopLogger)
|
||||
wrapper.SetOptions(config.OptionsConfiguration{
|
||||
NATLeaseM: natLease,
|
||||
NATRenewalM: natRenewal,
|
||||
NATTimeoutS: natTimeout,
|
||||
wrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.Options.NATLeaseM = natLease
|
||||
cfg.Options.NATRenewalM = natRenewal
|
||||
cfg.Options.NATTimeoutS = natTimeout
|
||||
})
|
||||
natSvc := nat.NewService(id, wrapper)
|
||||
mapping := mapping{natSvc.NewMapping(nat.TCP, addr.IP, addr.Port)}
|
||||
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/upgrade"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/thejerf/suture/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -602,36 +603,46 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
evLogger := events.NewLogger()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go evLogger.Serve(ctx)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
|
||||
// earlyService is a supervisor that runs the services needed for or
|
||||
// before app startup; the event logger, and the config service.
|
||||
spec := svcutil.SpecWithDebugLogger(l)
|
||||
earlyService := suture.New("early", spec)
|
||||
earlyService.ServeBackground(ctx)
|
||||
|
||||
evLogger := events.NewLogger()
|
||||
earlyService.Add(evLogger)
|
||||
|
||||
cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
|
||||
if err != nil {
|
||||
l.Warnln("Failed to initialize config:", err)
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
if cfgService, ok := cfgWrapper.(suture.Service); ok {
|
||||
earlyService.Add(cfgService)
|
||||
}
|
||||
|
||||
// Candidate builds should auto upgrade. Make sure the option is set,
|
||||
// unless we are in a build where it's disabled or the STNOUPGRADE
|
||||
// environment variable is set.
|
||||
|
||||
if build.IsCandidate && !upgrade.DisabledByCompilation && !runtimeOptions.NoUpgrade {
|
||||
l.Infoln("Automatic upgrade is always enabled for candidate releases.")
|
||||
if opts := cfg.Options(); opts.AutoUpgradeIntervalH == 0 || opts.AutoUpgradeIntervalH > 24 {
|
||||
opts.AutoUpgradeIntervalH = 12
|
||||
// Set the option into the config as well, as the auto upgrade
|
||||
// loop expects to read a valid interval from there.
|
||||
cfg.SetOptions(opts)
|
||||
cfg.Save()
|
||||
}
|
||||
// We don't tweak the user's choice of upgrading to pre-releases or
|
||||
// not, as otherwise they cannot step off the candidate channel.
|
||||
cfgWrapper.Modify(func(cfg *config.Configuration) {
|
||||
l.Infoln("Automatic upgrade is always enabled for candidate releases.")
|
||||
if cfg.Options.AutoUpgradeIntervalH == 0 || cfg.Options.AutoUpgradeIntervalH > 24 {
|
||||
cfg.Options.AutoUpgradeIntervalH = 12
|
||||
// Set the option into the config as well, as the auto upgrade
|
||||
// loop expects to read a valid interval from there.
|
||||
}
|
||||
// We don't tweak the user's choice of upgrading to pre-releases or
|
||||
// not, as otherwise they cannot step off the candidate channel.
|
||||
})
|
||||
}
|
||||
|
||||
dbFile := locations.Get(locations.Database)
|
||||
ldb, err := syncthing.OpenDBBackend(dbFile, cfg.Options().DatabaseTuning)
|
||||
ldb, err := syncthing.OpenDBBackend(dbFile, cfgWrapper.Options().DatabaseTuning)
|
||||
if err != nil {
|
||||
l.Warnln("Error opening database:", err)
|
||||
os.Exit(1)
|
||||
@@ -642,7 +653,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
// later after App is initialised.
|
||||
|
||||
autoUpgradePossible := autoUpgradePossible(runtimeOptions)
|
||||
if autoUpgradePossible && cfg.Options().AutoUpgradeEnabled() {
|
||||
if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() {
|
||||
// try to do upgrade directly and log the error if relevant.
|
||||
release, err := initialAutoUpgradeCheck(db.NewMiscDataNamespace(ldb))
|
||||
if err == nil {
|
||||
@@ -661,9 +672,9 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
}
|
||||
|
||||
if runtimeOptions.unpaused {
|
||||
setPauseState(cfg, false)
|
||||
setPauseState(cfgWrapper, false)
|
||||
} else if runtimeOptions.paused {
|
||||
setPauseState(cfg, true)
|
||||
setPauseState(cfgWrapper, true)
|
||||
}
|
||||
|
||||
appOpts := runtimeOptions.Options
|
||||
@@ -681,14 +692,14 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
appOpts.DBIndirectGCInterval = dur
|
||||
}
|
||||
|
||||
app, err := syncthing.New(cfg, ldb, evLogger, cert, appOpts)
|
||||
app, err := syncthing.New(cfgWrapper, ldb, evLogger, cert, appOpts)
|
||||
if err != nil {
|
||||
l.Warnln("Failed to start Syncthing:", err)
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
|
||||
if autoUpgradePossible {
|
||||
go autoUpgrade(cfg, app, evLogger)
|
||||
go autoUpgrade(cfgWrapper, app, evLogger)
|
||||
}
|
||||
|
||||
setupSignalHandling(app)
|
||||
@@ -709,7 +720,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
go standbyMonitor(app, cfg)
|
||||
go standbyMonitor(app, cfgWrapper)
|
||||
|
||||
if err := app.Start(); err != nil {
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
@@ -717,10 +728,10 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
|
||||
cleanConfigDirectory()
|
||||
|
||||
if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
|
||||
if cfgWrapper.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
|
||||
// Can potentially block if the utility we are invoking doesn't
|
||||
// fork, and just execs, hence keep it in its own routine.
|
||||
go func() { _ = openURL(cfg.GUI().URL()) }()
|
||||
go func() { _ = openURL(cfgWrapper.GUI().URL()) }()
|
||||
}
|
||||
|
||||
status := app.Wait()
|
||||
@@ -988,15 +999,16 @@ func showPaths(options RuntimeOptions) {
|
||||
fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
|
||||
}
|
||||
|
||||
func setPauseState(cfg config.Wrapper, paused bool) {
|
||||
raw := cfg.RawCopy()
|
||||
for i := range raw.Devices {
|
||||
raw.Devices[i].Paused = paused
|
||||
}
|
||||
for i := range raw.Folders {
|
||||
raw.Folders[i].Paused = paused
|
||||
}
|
||||
if _, err := cfg.Replace(raw); err != nil {
|
||||
func setPauseState(cfgWrapper config.Wrapper, paused bool) {
|
||||
_, err := cfgWrapper.Modify(func(cfg *config.Configuration) {
|
||||
for i := range cfg.Devices {
|
||||
cfg.Devices[i].Paused = paused
|
||||
}
|
||||
for i := range cfg.Folders {
|
||||
cfg.Folders[i].Paused = paused
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
l.Warnln("Cannot adjust paused state:", err)
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,7 +1,7 @@
|
||||
module github.com/syncthing/syncthing
|
||||
|
||||
require (
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a
|
||||
github.com/AudriusButkevicius/recli v0.0.5
|
||||
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
|
||||
github.com/calmh/xdr v1.1.0
|
||||
@@ -17,7 +17,9 @@ require (
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/greatroar/blobloom v0.5.0
|
||||
github.com/hashicorp/golang-lru v0.5.1
|
||||
github.com/jackpal/gateway v1.0.6
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
|
||||
5
go.sum
5
go.sum
@@ -7,8 +7,8 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6 h1:Apvc4kyfdrOxG+F5dn8osz+45kwGJa6CySQn0tB38SU=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a h1:dUzIAgRmHLIUU0PT+OJ0X1WI5j1hlLbf+G420gUjIQg=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q=
|
||||
github.com/AudriusButkevicius/recli v0.0.5 h1:xUa55PvWTHBm17T6RvjElRO3y5tALpdceH86vhzQ5wg=
|
||||
github.com/AudriusButkevicius/recli v0.0.5/go.mod h1:Q2E26yc6RvWWEz/TJ/goUp6yXvipYdJI096hpoaqsNs=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
@@ -212,6 +212,7 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
|
||||
@@ -41,6 +41,12 @@ ul+h5 {
|
||||
float: none; /* issue #1197 */
|
||||
}
|
||||
|
||||
#advancedAccordion input.form-control[type="checkbox"] {
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.popover {
|
||||
max-width: none;
|
||||
min-width: 250px;
|
||||
@@ -114,7 +120,8 @@ table.table-dynamic {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
table.table-condensed td {
|
||||
table.table-condensed td,
|
||||
table.table-condensed th {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -357,7 +364,8 @@ ul.three-columns li, ul.two-columns li {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
table.table-condensed td {
|
||||
table.table-condensed td,
|
||||
table.table-condensed th {
|
||||
/* for mobile phones to allow linebreaks in long repro folder/shared with
|
||||
* columns. */
|
||||
white-space: normal;
|
||||
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Опасност!",
|
||||
"Debugging Facilities": "Дебъг функционалност",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Път до папка по подразбиране",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Изтрито",
|
||||
"Deselect All": "Никое",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Изтегляне",
|
||||
"Edit": "Промени",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Променяне на {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Разреши NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "елемента",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} желае да сподели папката \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} желае да сподели папката \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} желае да сподели папката \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Perill!",
|
||||
"Debugging Facilities": "Utilitats de Depuració",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Carpeta de la Ruta per Defecte",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Esborrat",
|
||||
"Deselect All": "Anul·lar tota la selecció",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Descarregant",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Editant {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Permetre NAT transversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "Elements",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartit la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vol compartir la carpeta \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vol compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Aktuálně sdíleno se zařízeními",
|
||||
"Danger!": "Nebezpečí!",
|
||||
"Debugging Facilities": "Nástroje pro ladění",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Popis umístění výchozí složky",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Smazáno",
|
||||
"Deselect All": "Zrušit výběr všeho",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Stahuje se",
|
||||
"Edit": "Upravit",
|
||||
"Edit Device": "Upravit zařízení",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Upravit složku",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Upravuje se {{path}}.",
|
||||
"Enable Crash Reporting": "Povolit hlášení pádů",
|
||||
"Enable NAT traversal": "Povolit průchod skrze NAT překlad",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "položky",
|
||||
"seconds": "sekund",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet složku „{{folder}}“.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce sdílet složku „{{folderlabel}}“ ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce sdílet složku „{{folderlabel}}“ ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "i øjeblikket delt med enheder",
|
||||
"Danger!": "Fare!",
|
||||
"Debugging Facilities": "Faciliteter til fejlretning",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Standardmappesti",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Slet ikke forventede elementer ",
|
||||
"Deleted": "Slettet",
|
||||
"Deselect All": "Fravælg alle",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Downloader",
|
||||
"Edit": "Redigér",
|
||||
"Edit Device": "Redigere enhed",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Redigere mappe",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Redigerer {{path}}.",
|
||||
"Enable Crash Reporting": "Aktivere nedbrud rapportering",
|
||||
"Enable NAT traversal": "Aktivér NAT-traversering",
|
||||
@@ -326,7 +332,7 @@
|
||||
"The following items were changed locally.": "De følgende filer er ændret lokalt.",
|
||||
"The following unexpected items were found.": "Følgende ikke-forventet emner blev fundet.",
|
||||
"The interval must be a positive number of seconds.": "Intervallet skal være et positivt antal sekunder.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval i sekunder for kørsel af oprydning i versionskatalog. Nul vil deaktivere periodisk rengøring.",
|
||||
"The maximum age must be a number and cannot be blank.": "Maksimal alder skal være et tal og feltet må ikke være tomt.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Den maksimale tid, en version skal gemmes (i dage; sæt lig med 0 for at beholde gamle versioner for altid).",
|
||||
"The number of days must be a number and cannot be blank.": "Antallet af dage skal være et tal og feltet må ikke være tomt.",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "filer",
|
||||
"seconds": "sekunder",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker at dele mappen “{{folder}}”.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ønsker at dele mappen “{{folderlabel}}” ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ønsker at dele mappen “{{folderlabel}}” ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ein externer Befehl behandelt die Versionierung. Die Datei aus dem freigegebenen Ordner muss entfernen werden. Wenn der Pfad der Anwendung Leerzeichen enthält, sollte dieser in Anführungszeichen stehen.",
|
||||
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
|
||||
"Are you sure you want to permanently delete all these files?": "Sind Sie sicher, dass Sie all diese Dateien permanent löschen wollen?",
|
||||
"Are you sure you want to permanently delete all these files?": "Sind Sie sicher, dass Sie all diese Dateien dauerhaft löschen möchten?",
|
||||
"Are you sure you want to remove device {%name%}?": "Sind Sie sicher, dass sie das Gerät {{name}} entfernen möchten?",
|
||||
"Are you sure you want to remove folder {%label%}?": "Sind Sie sicher, dass sie den Ordner {{label}} entfernen möchten?",
|
||||
"Are you sure you want to restore {%count%} files?": "Sind Sie sicher, dass Sie {{count}} Dateien wiederherstellen möchten?",
|
||||
@@ -61,8 +61,12 @@
|
||||
"Currently Shared With Devices": "Derzeit mit Geräten geteilt",
|
||||
"Danger!": "Achtung!",
|
||||
"Debugging Facilities": "Debugging-Möglichkeiten",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Standardmäßiger Ordnerpfad",
|
||||
"Delete Unexpected Items": "Lösche unerwartete Elemente",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Unerwartete Elemente löschen",
|
||||
"Deleted": "Gelöscht",
|
||||
"Deselect All": "Alle abwählen",
|
||||
"Deselect devices to stop sharing this folder with.": "Die Geräte abwählen, für die dieser Ordner nicht mehr freigegeben werden soll.",
|
||||
@@ -72,7 +76,7 @@
|
||||
"Device ID": "Gerätekennung",
|
||||
"Device Identification": "Geräteidentifikation",
|
||||
"Device Name": "Gerätename",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "Gerät wird nicht vertraut, Verschlüsselungspasswort eingeben",
|
||||
"Device rate limits": "Gerät Datenratelimit",
|
||||
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
|
||||
"Devices": "Geräte",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Lädt herunter",
|
||||
"Edit": "Bearbeiten",
|
||||
"Edit Device": "Gerät bearbeiten",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Ordner bearbeiten",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Bearbeite {{path}}.",
|
||||
"Enable Crash Reporting": "Absturzmeldung aktivieren",
|
||||
"Enable NAT traversal": "NAT-Durchdringung aktivieren",
|
||||
@@ -337,7 +343,7 @@
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Das Datenratelimit muss eine nicht negative Zahl sein (0 = kein Limit).",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht negative Anzahl (in Sekunden) sein.",
|
||||
"There are no devices to share this folder with.": "Es gibt keine Geräte, mit denen dieser Ordner geteilt werden kann.",
|
||||
"There are no folders to share with this device.": "There are no folders to share with this device.",
|
||||
"There are no folders to share with this device.": "Es gibt keine Ordner, die mit diesem Gerät geteilt werden können.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Sie werden automatisch heruntergeladen und werden synchronisiert, wenn der Fehler behoben wurde.",
|
||||
"This Device": "Dieses Gerät",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Dies kann dazu führen, dass Unberechtigte relativ einfach auf Ihre Dateien zugreifen und diese ändern können.",
|
||||
@@ -352,11 +358,11 @@
|
||||
"Unavailable/Disabled by administrator or maintainer": "Nicht verfügbar/durch Administrator oder Betreuer deaktiviert",
|
||||
"Undecided (will prompt)": "Unentschlossen (wird nachgefragt)",
|
||||
"Unexpected Items": "Unerwartete Elemente",
|
||||
"Unexpected items have been found in this folder.": "Unerwartete Elemente wurden in diesem Ordner gefunden.",
|
||||
"Unexpected items have been found in this folder.": "In diesem Ordner wurden unerwartete Elemente gefunden.",
|
||||
"Unignore": "Beachten",
|
||||
"Unknown": "Unbekannt",
|
||||
"Unshared": "Ungeteilt",
|
||||
"Unshared Devices": "Ungeteilte Geräte",
|
||||
"Unshared Devices": "Nicht geteilte Geräte",
|
||||
"Unshared Folders": "Nicht geteilte Ordner",
|
||||
"Untrusted": "Nicht vertraut",
|
||||
"Up to Date": "Aktuell",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "Elemente",
|
||||
"seconds": "Sekunden",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} möchte den Ordner \"{{folder}}\" teilen.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} möchte den Ordner \"{{folderlabel}}\" ({{folder}}) teilen."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} möchte den Ordner \"{{folderlabel}}\" ({{folder}}) teilen.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} könnte dieses Gerät wieder einführen."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Διαμοιράζεται με αυτές τις συσκευές",
|
||||
"Danger!": "Προσοχή!",
|
||||
"Debugging Facilities": "Εργαλεία αποσφαλμάτωσης",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Προκαθορισμένη διαδρομή φακέλων",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Διαγραμμένα",
|
||||
"Deselect All": "Αποεπιλογή όλων",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Λήψη",
|
||||
"Edit": "Επεξεργασία",
|
||||
"Edit Device": "Επεξεργασία συσκευής",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Επεξεργασία φακέλου",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Επεξεργασία του {{path}}.",
|
||||
"Enable Crash Reporting": "Ενεργοποίηση αναφοράς σφαλμάτων",
|
||||
"Enable NAT traversal": "Ενεργοποίηση διάσχισης NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "εγγραφές",
|
||||
"seconds": "δευτερόλεπτα",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "Η συσκευή {{device}} θέλει να μοιράσει τον φάκελο «{{folder}}».",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "Η συσκευή {{device}} επιθυμεί να διαμοιράσει τον φάκελο \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "Η συσκευή {{device}} επιθυμεί να διαμοιράσει τον φάκελο \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -53,7 +53,7 @@
|
||||
"Connection Error": "Connection Error",
|
||||
"Connection Type": "Connection Type",
|
||||
"Connections": "Connections",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that fewer full scans are required.",
|
||||
"Copied from elsewhere": "Copied from elsewhere",
|
||||
"Copied from original": "Copied from original",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Danger!",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Default Folder Path",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Deleted",
|
||||
"Deselect All": "Deselect All",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Downloading",
|
||||
"Edit": "Edit",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Enable NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "items",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Danger!",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Default Folder Path",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Deleted",
|
||||
"Deselect All": "Deselect All",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Downloading",
|
||||
"Edit": "Edit",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Enable NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "items",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Danger!",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Default Folder Path",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Deleted",
|
||||
"Deselect All": "Deselect All",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Downloading",
|
||||
"Edit": "Edit",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Enable NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "items",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Nune komunigita kun aparatoj",
|
||||
"Danger!": "Danĝero!",
|
||||
"Debugging Facilities": "Elpurigadiloj",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Defaŭlta Dosieruja Vojo",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Forigita",
|
||||
"Deselect All": "Malelekti Ĉiujn",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Elŝutado",
|
||||
"Edit": "Redakti",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Redaktado de {{path}}.",
|
||||
"Enable Crash Reporting": "Ŝalti raportadon de kraŝoj",
|
||||
"Enable NAT traversal": "Ŝaltu trairan NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "eroj",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} volas komunigi dosierujon \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} volas komunigi dosierujon \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} volas komunigi dosierujon \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -18,18 +18,18 @@
|
||||
"Advanced": "Avanzado",
|
||||
"Advanced Configuration": "Configuración Avanzada",
|
||||
"All Data": "Todos los datos",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Todos las carpetas compartidas con este equipo deben ser protegidas con una contraseña, de manera que todos los datos enviados sean ilegibles sin la contraseña dada.",
|
||||
"Allow Anonymous Usage Reporting?": "¿Deseas permitir el envío anónimo de informes de uso?",
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabético",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Un comando externo maneja el versionado. Tiene que eliminar el fichero de la carpeta compartida. Si la ruta a la aplicación contiene espacios, hay que escribirla entre comillas.",
|
||||
"Anonymous Usage Reporting": "Informe anónimo de uso",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe anónimo de uso ha cambiado. ¿Quieres cambiar al nuevo formato?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Are you sure you want to permanently delete all these files?": "¿Esta seguro(a) de que desea eliminar permanentemente todos estos archivos?",
|
||||
"Are you sure you want to remove device {%name%}?": "¿Estás seguro de que quieres quitar el dispositivo {{name}}?",
|
||||
"Are you sure you want to remove folder {%label%}?": "¿Estás seguro de que quieres quitar la carpeta {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "¿Estás seguro de que quieres restaurar {{count}} ficheros?",
|
||||
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
|
||||
"Are you sure you want to upgrade?": "¿Está seguro(a) de que desea actualizar?",
|
||||
"Auto Accept": "Auto aceptar",
|
||||
"Automatic Crash Reporting": "Informe automático de errores",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Ahora la actualización automática permite elegir entre versiones estables o versiones candidatas.",
|
||||
@@ -41,15 +41,15 @@
|
||||
"Bugs": "Errores",
|
||||
"Changelog": "Registro de cambios",
|
||||
"Clean out after": "Limpiar tras",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
"Cleanup Interval": "Cleanup Interval",
|
||||
"Cleaning Versions": "Limpiando Versiones",
|
||||
"Cleanup Interval": "Intervalo de Limpieza",
|
||||
"Click to see discovery failures": "Clica para ver fallos de descubrimiento.",
|
||||
"Close": "Cerrar",
|
||||
"Command": "Acción",
|
||||
"Comment, when used at the start of a line": "Comentar, cuando se usa al comienzo de una línea",
|
||||
"Compression": "Compresión",
|
||||
"Configured": "Configurado",
|
||||
"Connected (Unused)": "Connected (Unused)",
|
||||
"Connected (Unused)": "Conectado (Sin uso)",
|
||||
"Connection Error": "Error de conexión",
|
||||
"Connection Type": "Tipo de conexión",
|
||||
"Connections": "Conexiones",
|
||||
@@ -58,21 +58,25 @@
|
||||
"Copied from original": "Copiado del original",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 los siguientes Colaboradores:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Crear patrones a ignorar, sobreescribiendo un fichero existente en {{path}}.",
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Currently Shared With Devices": "Actualmente Compartida Con Los Equipos",
|
||||
"Danger!": "¡Peligro!",
|
||||
"Debugging Facilities": "Ayudas a la depuración",
|
||||
"Default Configuration": "Configuración Por Defecto",
|
||||
"Default Device": "Equipo Por Defecto",
|
||||
"Default Folder": "Carpeta Por Defecto",
|
||||
"Default Folder Path": "Ruta de la carpeta por defecto",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Defaults": "Valores Por Defecto",
|
||||
"Delete Unexpected Items": "Borrar Elementos Inesperados",
|
||||
"Deleted": "Eliminado",
|
||||
"Deselect All": "Deseleccionar Todo",
|
||||
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
|
||||
"Deselect devices to stop sharing this folder with.": "Deseleccione los equipos con los cuales dejar de compartir esta carpeta.",
|
||||
"Deselect folders to stop sharing with this device.": "Deseleccione las carpetas para dejar de compartir con este equipo.",
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "Se desconfía del equipo, ingrese la contraseña de cifrado.",
|
||||
"Device rate limits": "Límites de la tasa del dispositivo",
|
||||
"Device that last modified the item": "Último dispositivo que cambió el objeto",
|
||||
"Devices": "Dispositivos",
|
||||
@@ -81,10 +85,10 @@
|
||||
"Disabled periodic scanning and disabled watching for changes": "Desactivados el escaneo periódico y la vigilancia de cambios",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Desactivado el escaneo periódico y activada la vigilancia de cambios",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Desactivado el escaneo periódico y falló la activación de la vigilancia de cambios, reintentando cada 1 minuto:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desactiva la comparación y sincronización de los permisos de los archivos. Útil en sistemas con permisos inexistentes o personalizados (por ejemplo, FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Descartar",
|
||||
"Disconnected": "Desconectado",
|
||||
"Disconnected (Unused)": "Disconnected (Unused)",
|
||||
"Disconnected (Unused)": "Desconectado (Sin uso)",
|
||||
"Discovered": "Descubierto",
|
||||
"Discovery": "Descubrimiento",
|
||||
"Discovery Failures": "Fallos de Descubrimiento",
|
||||
@@ -96,8 +100,10 @@
|
||||
"Downloaded": "Descargado",
|
||||
"Downloading": "Descargando",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Device": "Editar Equipo",
|
||||
"Edit Device Defaults": "Editar Valores Por Defecto del Equipo",
|
||||
"Edit Folder": "Editar Carpeta",
|
||||
"Edit Folder Defaults": "Editar Valores Por Defecto de la Carpeta",
|
||||
"Editing {%path%}.": "Editando {{path}}.",
|
||||
"Enable Crash Reporting": "Permitir informe de errores",
|
||||
"Enable NAT traversal": "Permitir NAT transversal",
|
||||
@@ -107,7 +113,7 @@
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Introduce un puerto sin privilegios (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introducir direcciones separadas por coma (\"tcp://ip:port\", \"tcp://host:port\") o dinámicas para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introducir patrones a ignorar, uno por línea.",
|
||||
"Enter up to three octal digits.": "Enter up to three octal digits.",
|
||||
"Enter up to three octal digits.": "Ingrese hasta tres dígitos octales.",
|
||||
"Error": "Error",
|
||||
"External File Versioning": "Versionado externo de fichero",
|
||||
"Failed Items": "Elementos fallidos",
|
||||
@@ -127,14 +133,14 @@
|
||||
"Folder Label": "Etiqueta de la Carpeta",
|
||||
"Folder Path": "Ruta de la carpeta",
|
||||
"Folder Type": "Tipo de carpeta",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "El tipo de carpeta \"{{receiveEncrypted}}\" no se puede cambiar después de añadir la carpeta. Es necesario eliminar la carpeta, borrar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
"Folders": "Carpetas",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Para las siguientes carpetas ocurrió un error cuando se empezó a vigilar los cambios. Se reintentará cada minuto, así que puede ser que los errores desaparezcan pronto. Si persisten, intenta solucionar el problema subyacente y pide ayuda en el caso de que no puedas.",
|
||||
"Full Rescan Interval (s)": "Intervalo para el reescaneo completo (s)",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "Password de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication User": "Autentificación de usuario de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
|
||||
"GUI Authentication: Set User and Password": "Autenticación de la GUI: Establezca el Usuario y la Contraseña.",
|
||||
"GUI Listen Address": "Dirección de Escucha del GUI.",
|
||||
"GUI Theme": "Tema GUI",
|
||||
"General": "General",
|
||||
@@ -145,8 +151,8 @@
|
||||
"Help": "Ayuda",
|
||||
"Home page": "Página de inicio",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no querer habilitarlo. Hemos deshabilitado el informe automático de errores por usted.",
|
||||
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
|
||||
"If untrusted, enter encryption password": "Si no es de confianza, ingrese la contraseña de cifrado.",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si quiere impedirle a otros usuarios de esta computadora acceder a Syncthing y, a través de este, a sus archivos, considere establecer la autenticación.",
|
||||
"Ignore": "Ignorar",
|
||||
"Ignore Patterns": "Patrones a ignorar",
|
||||
"Ignore Permissions": "Permisos a ignorar",
|
||||
@@ -169,7 +175,7 @@
|
||||
"Listeners": "Oyentes",
|
||||
"Loading data...": "Cargando datos...",
|
||||
"Loading...": "Cargando...",
|
||||
"Local Additions": "Local Additions",
|
||||
"Local Additions": "Adiciones Locales",
|
||||
"Local Discovery": "Descubrimiento local",
|
||||
"Local State": "Estado local",
|
||||
"Local State (Total)": "Estado Local (Total)",
|
||||
@@ -194,7 +200,7 @@
|
||||
"No File Versioning": "Sin versionado de fichero",
|
||||
"No files will be deleted as a result of this operation.": "No se borraron ficheros como resultado de esta operación",
|
||||
"No upgrades": "Sin actualizaciones",
|
||||
"Not shared": "Not shared",
|
||||
"Not shared": "No compartida",
|
||||
"Notice": "Aviso",
|
||||
"OK": "OK",
|
||||
"Off": "Desconectar",
|
||||
@@ -212,7 +218,7 @@
|
||||
"Pause": "Pausar",
|
||||
"Pause All": "Pausar todo",
|
||||
"Paused": "Pausado",
|
||||
"Paused (Unused)": "Paused (Unused)",
|
||||
"Paused (Unused)": "Pausado(a) (Sin uso)",
|
||||
"Pending changes": "Cambios pendientes",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Escaneo periódico en un intervalo determinado y desactivada la vigilancia de cambios",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Escaneo periódico en un intervalo determinado y activada la vigilancia de cambios",
|
||||
@@ -223,20 +229,20 @@
|
||||
"Please wait": "Por favor, espere",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "El prefijo indica que el fichero puede ser borrado si se previene la eliminación de directorios",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "El prefijo indica que el patrón se comparará sin tener en cuenta las mayúsculas",
|
||||
"Preparing to Sync": "Preparing to Sync",
|
||||
"Preparing to Sync": "Preparándose para Sincronizar",
|
||||
"Preview": "Vista previa",
|
||||
"Preview Usage Report": "Informe de uso de vista previa",
|
||||
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
|
||||
"Random": "Aleatorio",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Encrypted": "Recibir Cifrado",
|
||||
"Receive Only": "Solo recibir",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Received data is already encrypted": "Los datos recibidos ya están cifrados",
|
||||
"Recent Changes": "Cambios recientes",
|
||||
"Reduced by ignore patterns": "Reducido por patrones de ignorar",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Las versiones candidatas contienen las últimas funcionalidades y correcciones. Son similares a las tradicionales versiones bisemanales de Syncthing.",
|
||||
"Remote Devices": "Otros dispositivos",
|
||||
"Remote GUI": "Remote GUI",
|
||||
"Remote GUI": "GUI Remota",
|
||||
"Remove": "Eliminar",
|
||||
"Remove Device": "Quitar dispositivo",
|
||||
"Remove Folder": "Quitar carpeta",
|
||||
@@ -259,8 +265,8 @@
|
||||
"See external versioning help for supported templated command line parameters.": "Consultar la ayuda externa del versionado para ver las plantillas de los parámetros de línea de comandos",
|
||||
"Select All": "Seleccionar Todo",
|
||||
"Select a version": "Selecciona una versión",
|
||||
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
|
||||
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
|
||||
"Select additional devices to share this folder with.": "Seleccione equipos adicionales con los cuales compartir esta carpeta",
|
||||
"Select additional folders to share with this device.": "Seleccione carpetas adicionales para compartir con este equipo.",
|
||||
"Select latest version": "Selecciona la última versión",
|
||||
"Select oldest version": "Selecciona la versión más antigua",
|
||||
"Select the folders to share with this device.": "Selecciona las carpetas para compartir con este dispositivo.",
|
||||
@@ -271,7 +277,7 @@
|
||||
"Share Folder": "Compartir carpeta",
|
||||
"Share Folders With Device": "Compartir carpetas con dispositivo",
|
||||
"Share this folder?": "¿Deseas compartir esta carpeta?",
|
||||
"Shared Folders": "Shared Folders",
|
||||
"Shared Folders": "Carpetas Compartidas",
|
||||
"Shared With": "Compartir con",
|
||||
"Sharing": "Compartiendo",
|
||||
"Show ID": "Mostrar ID",
|
||||
@@ -294,7 +300,7 @@
|
||||
"Start Browser": "Iniciar el navegador",
|
||||
"Statistics": "Estadísticas",
|
||||
"Stopped": "Detenido",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Solo almacena y sincroniza datos cifrados. Las carpetas de todos los equipos conectados tienen que ser configuradas con la misma contraseña o ser del tipo \"{{receiveEncrypted}}\" también.",
|
||||
"Support": "Forum",
|
||||
"Support Bundle": "Lote de Soporte",
|
||||
"Sync Protocol Listen Addresses": "Direcciones de escucha del protocolo de sincronización",
|
||||
@@ -309,10 +315,10 @@
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing tiene problemas para procesar tu solicitud. Por favor, actualiza la página o reinicia Syncthing si el problema persiste.",
|
||||
"Take me back": "Llévame atrás",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "La dirección del GUI es sobreescrita por las opciones de arranque. Los cambios de aquí no tendrán efecto mientras la sobreescritura esté activa.",
|
||||
"The Syncthing Authors": "The Syncthing Authors",
|
||||
"The Syncthing Authors": "Los Autores de Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "El panel de administración de Syncthing está configurado para permitir el acceso remoto sin contraseña.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Las estadísticas agragadas están disponibles públicamente en la URL de abajo.",
|
||||
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
|
||||
"The cleanup interval cannot be blank.": "El intervalo de limpieza no puede ser nulo.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido grabada pero no activada. Syncthing debe reiniciarse para activar la nueva configuración.",
|
||||
"The device ID cannot be blank.": "La ID del dispositivo no puede estar vacía.",
|
||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "El ID del dispositivo que hay que introducir aquí se puede encontrar en el diálogo \"Acciones > Mostrar ID\" en el otro dispositivo. Los espacios y las barras son opcionales (ignorados).",
|
||||
@@ -324,9 +330,9 @@
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
|
||||
"The following items could not be synchronized.": "Los siguientes elementos no pueden ser sincronizados.",
|
||||
"The following items were changed locally.": "Los siguientes ítems fueron cambiados localmente.",
|
||||
"The following unexpected items were found.": "The following unexpected items were found.",
|
||||
"The interval must be a positive number of seconds.": "The interval must be a positive number of seconds.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
|
||||
"The following unexpected items were found.": "Los siguientes elementos inesperados fueron encontrados.",
|
||||
"The interval must be a positive number of seconds.": "El intervalo debe ser un número positivo de segundos.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "El intervalo, en segundos, para realizar la limpieza del directorio de versiones. Cero para desactivar la limpieza periódica.",
|
||||
"The maximum age must be a number and cannot be blank.": "La edad máxima debe ser un número y no puede estar vacía.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "El tiempo máximo para mantener una versión en días (introducir 0 para mantener las versiones indefinidamente).",
|
||||
"The number of days must be a number and cannot be blank.": "El número de días debe ser un número y no puede estar en blanco.",
|
||||
@@ -336,8 +342,8 @@
|
||||
"The path cannot be blank.": "La ruta no puede estar vacía.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
|
||||
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
|
||||
"There are no folders to share with this device.": "There are no folders to share with this device.",
|
||||
"There are no devices to share this folder with.": "No hay equipos con los cuales compartir esta carpeta.",
|
||||
"There are no folders to share with this device.": "No hay carpetas para compartir con este equipo.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
|
||||
"This Device": "Este Dispositivo",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
|
||||
@@ -347,18 +353,18 @@
|
||||
"Time the item was last modified": "Tiempo en el que se modificó el ítem por última vez",
|
||||
"Trash Can File Versioning": "Versionado de archivos de la papelera",
|
||||
"Type": "Tipo",
|
||||
"UNIX Permissions": "UNIX Permissions",
|
||||
"UNIX Permissions": "Permisos de UNIX",
|
||||
"Unavailable": "No disponible",
|
||||
"Unavailable/Disabled by administrator or maintainer": "No disponible/Desactivado por el administrador o el mantenedor",
|
||||
"Undecided (will prompt)": "Aún no decidido (se preguntará al usuario)",
|
||||
"Unexpected Items": "Unexpected Items",
|
||||
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
|
||||
"Unexpected Items": "Elementos Inesperados",
|
||||
"Unexpected items have been found in this folder.": "Se han encontrado Elementos Inesperados en esta carpeta.",
|
||||
"Unignore": "Designorar",
|
||||
"Unknown": "Desconocido",
|
||||
"Unshared": "No compartido",
|
||||
"Unshared Devices": "Unshared Devices",
|
||||
"Unshared Folders": "Unshared Folders",
|
||||
"Untrusted": "Untrusted",
|
||||
"Unshared Devices": "Equipos no Compartidos",
|
||||
"Unshared Folders": "Carpetas no Compartidas",
|
||||
"Untrusted": "No Confiable",
|
||||
"Up to Date": "Actualizado",
|
||||
"Updated": "Actualizado",
|
||||
"Upgrade": "Actualizar",
|
||||
@@ -368,14 +374,14 @@
|
||||
"Uptime": "Tiempo de funcionamiento",
|
||||
"Usage reporting is always enabled for candidate releases.": "El informe de uso está siempre habilitado en las versiones candidatas.",
|
||||
"Use HTTPS for GUI": "Usar HTTPS para la Interfaz Gráfica de Usuario (GUI)",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Usuario/Contraseña no establecida para la autenticación de la GUI. Por favor, considere establecerla.",
|
||||
"Version": "Versión",
|
||||
"Versions": "Versiones",
|
||||
"Versions Path": "Ruta de las versiones",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se borran automáticamente si son más antiguas que la edad máxima o exceden el número de ficheros permitidos en un intervalo.",
|
||||
"Waiting to Clean": "Waiting to Clean",
|
||||
"Waiting to Scan": "Waiting to Scan",
|
||||
"Waiting to Sync": "Waiting to Sync",
|
||||
"Waiting to Clean": "Esperando para Limpiar",
|
||||
"Waiting to Scan": "Esperando para Escanear",
|
||||
"Waiting to Sync": "Esperando para Sincronizar",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "¡Peligro! Esta ruta es un directorio principal de la carpeta ya existente \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "'Peligro! Esta ruta es un subdirectorio de la carpeta ya existente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Peligro! Esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolder}}\".",
|
||||
@@ -394,13 +400,14 @@
|
||||
"You have no ignored folders.": "No tienes carpetas ignoradas.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Tienes cambios sin guardar. ¿Quieres descartarlos?",
|
||||
"You must keep at least one version.": "Debes mantener al menos una versión.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debe agregar o cambiar nada localmente en una carpeta \"{{receiveEncrypted}}\".",
|
||||
"days": "días",
|
||||
"directories": "directories",
|
||||
"directories": "directorios",
|
||||
"files": "archivos",
|
||||
"full documentation": "Documentación completa",
|
||||
"items": "Elementos",
|
||||
"seconds": "seconds",
|
||||
"seconds": "segundos",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} puede reintroducir este equipo."
|
||||
}
|
||||
@@ -18,73 +18,77 @@
|
||||
"Advanced": "Avanzado",
|
||||
"Advanced Configuration": "Configuración Avanzada",
|
||||
"All Data": "Todos los datos",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Todas las carpetas compartidas con este dispositivo deben estar protegidas por una contraseña, de forma que todos los datos enviados sean ilegibles sin la contraseña indicada.",
|
||||
"Allow Anonymous Usage Reporting?": "¿Deseas permitir el envío anónimo de informes de uso?",
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabético",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Un comando externo maneja las versiones. Tienes que eliminar el archivo de la carpeta compartida. Si la ruta a la aplicación contiene espacios, ésta debe estar entre comillas.",
|
||||
"Anonymous Usage Reporting": "Informe anónimo de uso",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe de uso anónimo a cambiado. ¿Desearía usar el nuevo formato?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Are you sure you want to permanently delete all these files?": "¿Está seguro de que desea eliminar permanente todos estos archivos?",
|
||||
"Are you sure you want to remove device {%name%}?": "¿Está seguro que desea eliminar el dispositivo {{name}}?",
|
||||
"Are you sure you want to remove folder {%label%}?": "¿Está seguro que desea eliminar la carpeta {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "¿Está seguro que desea restaurar {{count}} archivos?",
|
||||
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
|
||||
"Are you sure you want to upgrade?": "¿Está seguro(a) de que desea actualizar?",
|
||||
"Auto Accept": "Aceptar automáticamente",
|
||||
"Automatic Crash Reporting": "Automatic Crash Reporting",
|
||||
"Automatic Crash Reporting": "Informe Automático de Fallos",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Ahora la actualización automática permite elegir entre versiones estables o versiones candidatas.",
|
||||
"Automatic upgrades": "Actualizaciones automáticas",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Las actualizaciones automáticas siempre están activadas para las versiones candidatas.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Crear o compartir automáticamente carpetas que este dispositivo anuncia en la ruta por defecto.",
|
||||
"Available debug logging facilities:": "Funciones de registro de depuración disponibles:",
|
||||
"Be careful!": "¡Ten cuidado!",
|
||||
"Bugs": "Errores",
|
||||
"Changelog": "Registro de cambios",
|
||||
"Clean out after": "Limpiar tras",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
"Cleanup Interval": "Cleanup Interval",
|
||||
"Cleaning Versions": "Limpiando Versiones",
|
||||
"Cleanup Interval": "Intervalo de Limpieza",
|
||||
"Click to see discovery failures": "Clica para ver fallos de descubrimiento.",
|
||||
"Close": "Cerrar",
|
||||
"Command": "Acción",
|
||||
"Comment, when used at the start of a line": "Comentar, cuando se usa al comienzo de una línea",
|
||||
"Compression": "Compresión",
|
||||
"Configured": "Configurado",
|
||||
"Connected (Unused)": "Connected (Unused)",
|
||||
"Connected (Unused)": "Conectado (Sin Uso)",
|
||||
"Connection Error": "Error de conexión",
|
||||
"Connection Type": "Tipo de conexión",
|
||||
"Connections": "Conexiones",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Ahora está disponible en Syncthing la búsqueda continua de cambios. Se detectarán los cambios en disco y se hará un escaneado sólo en las rutas modificadas. Los beneficios son que los cambios se propagan más rápido y que se requieren menos escaneos completos.",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Ahora está disponible en Syncthing el control de cambios continuo. Se detectarán los cambios en disco y se hará un escaneado sólo en las rutas modificadas. Los beneficios son que los cambios se propagan más rápido y que se requieren menos escaneos completos.",
|
||||
"Copied from elsewhere": "Copiado de otro sitio",
|
||||
"Copied from original": "Copiado del original",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Derechos de Autor © 2014-2019 los siguientes colaboradores:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Crear patrones a ignorar, sobreescribiendo un fichero existente en {{path}}.",
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Currently Shared With Devices": "Actualmente Compartida con los Dispositivos",
|
||||
"Danger!": "¡Peligro!",
|
||||
"Debugging Facilities": "Servicios de depuración",
|
||||
"Default Configuration": "Configuración Predeterminada",
|
||||
"Default Device": "Dispositivo Predeterminado",
|
||||
"Default Folder": "Carpeta Predeterminada",
|
||||
"Default Folder Path": "Ruta de la carpeta por defecto",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Defaults": "Valores Predeterminados",
|
||||
"Delete Unexpected Items": "Borrar Elementos Inesperados",
|
||||
"Deleted": "Eliminado",
|
||||
"Deselect All": "Deselect All",
|
||||
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
|
||||
"Deselect All": "Deseleccionar Todo",
|
||||
"Deselect devices to stop sharing this folder with.": "Deseleccionar dispositivos con los cuales dejar de compartir esta carpeta.",
|
||||
"Deselect folders to stop sharing with this device.": "Deseleccionar carpetas para dejar de compartir con este dispositivo.",
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "El dispositivo no es de confianza, introduzca la contraseña de cifrado",
|
||||
"Device rate limits": "Límites de velocidad del dispositivo",
|
||||
"Device that last modified the item": "Dispositivo que modificó por última vez el ítem",
|
||||
"Devices": "Dispositivos",
|
||||
"Disable Crash Reporting": "Disable Crash Reporting",
|
||||
"Disable Crash Reporting": "Desactivar Informes de Fallos",
|
||||
"Disabled": "Deshabilitado",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Se desactivó el escaneo periódico y se desactivó el control de cambios",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Se desactivó el escaneo periódico y se activó el control de cambios",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Se desactivó el escaneo periódico y falló la configuración para detectar cambios, volviendo a intentarlo cada 1 m:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desactiva la comparación y sincronización de los permisos de los archivos. Útil en sistemas con permisos inexistentes o personalizados (por ejemplo, FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Descartar",
|
||||
"Disconnected": "Desconectado",
|
||||
"Disconnected (Unused)": "Disconnected (Unused)",
|
||||
"Disconnected (Unused)": "Desconectado (Sin Uso)",
|
||||
"Discovered": "Descubierto",
|
||||
"Discovery": "Descubrimiento",
|
||||
"Discovery Failures": "Fallos de Descubrimiento",
|
||||
@@ -96,18 +100,20 @@
|
||||
"Downloaded": "Descargado",
|
||||
"Downloading": "Descargando",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Device": "Editar Dispositivo",
|
||||
"Edit Device Defaults": "Editar Valores Predeterminados del Dispositivo",
|
||||
"Edit Folder": "Editar Carpeta",
|
||||
"Edit Folder Defaults": "Editar Valores Predeterminados de la Carpeta",
|
||||
"Editing {%path%}.": "Editando {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable Crash Reporting": "Activar Informes de Fallos",
|
||||
"Enable NAT traversal": "Permitir NAT transversal",
|
||||
"Enable Relaying": "Habilitar Retransmisión",
|
||||
"Enabled": "Activado",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduce un número no negativo (por ejemplo, \"2.35\") y selecciona una unidad. Los porcentajes son como parte del tamaño total del disco.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Introduce un puerto sin privilegios (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduzca direcciones separadas por comas (\"tcp://ip:port\", \"tcp://host:port\") o \"dynamic\" para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introducir patrones a ignorar, uno por línea.",
|
||||
"Enter up to three octal digits.": "Enter up to three octal digits.",
|
||||
"Enter up to three octal digits.": "Introduzca hasta tres dígitos octales.",
|
||||
"Error": "Error",
|
||||
"External File Versioning": "Versionado externo de fichero",
|
||||
"Failed Items": "Elementos fallidos",
|
||||
@@ -127,14 +133,14 @@
|
||||
"Folder Label": "Etiqueta de la Carpeta",
|
||||
"Folder Path": "Ruta de la carpeta",
|
||||
"Folder Type": "Tipo de carpeta",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "El tipo de carpeta \"{{receiveEncrypted}}\" no se puede cambiar después de añadir la carpeta. Es necesario eliminar la carpeta, borrar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
"Folders": "Carpetas",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "En las siguientes carpetas se ha producido un error al empezar a buscar cambios. Se volverá a intentar cada minuto, por lo que los errores podrían solucionarse pronto. Si persisten, trata de arreglar el problema subyacente y pide ayuda si no puedes.",
|
||||
"Full Rescan Interval (s)": "Intervalo de rescaneo completo (s)",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "Password de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication Password": "Contraseña de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication User": "Autentificación de usuario de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
|
||||
"GUI Authentication: Set User and Password": "Autenticación de la GUI: Establezca el Usuario y Contraseña",
|
||||
"GUI Listen Address": "Dirección de Escucha del GUI.",
|
||||
"GUI Theme": "Tema GUI",
|
||||
"General": "General",
|
||||
@@ -144,9 +150,9 @@
|
||||
"Global State": "Estado global",
|
||||
"Help": "Ayuda",
|
||||
"Home page": "Página de inicio",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.",
|
||||
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no quererla activa. Hemos desactivado los informes automáticos de fallos por usted.",
|
||||
"If untrusted, enter encryption password": "Si no es de confianza, introduzca la contraseña de cifrado",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si desea evitar que otros usuarios de esta computadora accedan a Syncthing y, a través de él, a sus archivos, considere establecer la autenticación.",
|
||||
"Ignore": "Ignorar",
|
||||
"Ignore Patterns": "Patrones a ignorar",
|
||||
"Ignore Permissions": "Permisos a ignorar",
|
||||
@@ -165,17 +171,17 @@
|
||||
"Last seen": "Visto por última vez",
|
||||
"Latest Change": "Último Cambio",
|
||||
"Learn more": "Saber más",
|
||||
"Limit": "Limit",
|
||||
"Limit": "Límite",
|
||||
"Listeners": "Oyentes",
|
||||
"Loading data...": "Cargando datos...",
|
||||
"Loading...": "Cargando...",
|
||||
"Local Additions": "Local Additions",
|
||||
"Local Additions": "Adiciones Locales",
|
||||
"Local Discovery": "Descubrimiento local",
|
||||
"Local State": "Estado local",
|
||||
"Local State (Total)": "Estado Local (Total)",
|
||||
"Locally Changed Items": "Locally Changed Items",
|
||||
"Locally Changed Items": "Elementos Cambiados Localmente",
|
||||
"Log": "Registro",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Seguimiento del registro pausado. Desplácese hasta el final para continuar.",
|
||||
"Logs": "Registros",
|
||||
"Major Upgrade": "Actualización importante",
|
||||
"Mass actions": "Acción masiva",
|
||||
@@ -194,7 +200,7 @@
|
||||
"No File Versioning": "Sin versionado de fichero",
|
||||
"No files will be deleted as a result of this operation.": "Ningún archivo será eliminado como resultado de esta operación.",
|
||||
"No upgrades": "Sin actualizaciones",
|
||||
"Not shared": "Not shared",
|
||||
"Not shared": "No Compartido(a)",
|
||||
"Notice": "Aviso",
|
||||
"OK": "OK",
|
||||
"Off": "Desconectar",
|
||||
@@ -212,7 +218,7 @@
|
||||
"Pause": "Pausar",
|
||||
"Pause All": "Pausar todo",
|
||||
"Paused": "Pausado",
|
||||
"Paused (Unused)": "Paused (Unused)",
|
||||
"Paused (Unused)": "Pausado(a) (Sin Uso)",
|
||||
"Pending changes": "Cambios pendientes",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios desactivada",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios activada",
|
||||
@@ -223,20 +229,20 @@
|
||||
"Please wait": "Por favor, espere",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
|
||||
"Preparing to Sync": "Preparing to Sync",
|
||||
"Preparing to Sync": "Preparándose para Sincronizar",
|
||||
"Preview": "Vista previa",
|
||||
"Preview Usage Report": "Informe de uso de vista previa",
|
||||
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
|
||||
"Random": "Aleatorio",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Encrypted": "Recibir Encriptado(s)",
|
||||
"Receive Only": "Solo Recibir",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Received data is already encrypted": "Los datos recibidos ya están cifrados",
|
||||
"Recent Changes": "Cambios recientes",
|
||||
"Reduced by ignore patterns": "Reducido por patrones de ignorar",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Las versiones candidatas contienen las últimas funcionalidades y correcciones. Son similares a las tradicionales versiones bisemanales de Syncthing.",
|
||||
"Remote Devices": "Otros dispositivos",
|
||||
"Remote GUI": "Remote GUI",
|
||||
"Remote GUI": "GUI Remota",
|
||||
"Remove": "Eliminar",
|
||||
"Remove Device": "Eliminar dispositivo",
|
||||
"Remove Folder": "Remover carpeta",
|
||||
@@ -257,10 +263,10 @@
|
||||
"Scan Time Remaining": "Tiempo Restante de Escaneo",
|
||||
"Scanning": "Analizando",
|
||||
"See external versioning help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
|
||||
"Select All": "Select All",
|
||||
"Select All": "Seleccionar Todo",
|
||||
"Select a version": "Seleccione una versión",
|
||||
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
|
||||
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
|
||||
"Select additional devices to share this folder with.": "Seleccionar dispositivos adicionales con los cuales compartir esta carpeta.",
|
||||
"Select additional folders to share with this device.": "Seleccionar carpetas adicionales para compartir con este dispositivo.",
|
||||
"Select latest version": "Seleccione la última versión",
|
||||
"Select oldest version": "Seleccione la versión más antigua",
|
||||
"Select the folders to share with this device.": "Selecciona las carpetas para compartir con este dispositivo.",
|
||||
@@ -271,7 +277,7 @@
|
||||
"Share Folder": "Compartir carpeta",
|
||||
"Share Folders With Device": "Compartir carpetas con dispositivo",
|
||||
"Share this folder?": "¿Deseas compartir esta carpeta?",
|
||||
"Shared Folders": "Shared Folders",
|
||||
"Shared Folders": "Carpetas Compartidas",
|
||||
"Shared With": "Compartir con",
|
||||
"Sharing": "Compartiendo",
|
||||
"Show ID": "Mostrar ID",
|
||||
@@ -294,25 +300,25 @@
|
||||
"Start Browser": "Iniciar el navegador",
|
||||
"Statistics": "Estadísticas",
|
||||
"Stopped": "Detenido",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Almacena y sincroniza sólo los datos cifrados. Las carpetas de todos los dispositivos conectados deben estar configuradas con la misma contraseña o ser también del tipo \"{{receiveEncrypted}}\".",
|
||||
"Support": "Forum",
|
||||
"Support Bundle": "Paquete de Soporte",
|
||||
"Sync Protocol Listen Addresses": "Direcciones de escucha del protocolo de sincronización",
|
||||
"Syncing": "Sincronizando",
|
||||
"Syncthing has been shut down.": "Syncthing se ha detenido.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing incluye el siguiente software o partes de él:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as MPL v2.0.",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es Software Libre y de Código Abierto con licencia MPL v2.0.",
|
||||
"Syncthing is restarting.": "Syncthing se está reiniciando.",
|
||||
"Syncthing is upgrading.": "Syncthing se está actualizando.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora soporta el reportar automáticamente las fallas a los desarrolladores. Esta característica está habilitada por defecto.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing parece no estar activo o hay un problema con tu conexión de internet. Reintentando...",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing tiene problemas para procesar tu solicitud. Por favor, actualiza la página o reinicia Syncthing si el problema persiste.",
|
||||
"Take me back": "Llévame de vuelta",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "La dirección de la Interfaz Gráfica de Ususario (GUI) está sobreescrita por las opciones de inicio. Los cambios aquí no tendrán efecto mientras la sobreescritura esté activa.",
|
||||
"The Syncthing Authors": "The Syncthing Authors",
|
||||
"The Syncthing Authors": "Los Autores de Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "El panel de administración de Syncthing está configurado para permitir el acceso remoto sin contraseña.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Las estadísticas agragadas están disponibles públicamente en la URL de abajo.",
|
||||
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
|
||||
"The cleanup interval cannot be blank.": "El intervalo de limpieza no puede ser nulo.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido grabada pero no activada. Syncthing debe reiniciarse para activar la nueva configuración.",
|
||||
"The device ID cannot be blank.": "La ID del dispositivo no puede estar vacía.",
|
||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "El ID del dispositivo que hay que introducir aquí se puede encontrar en el diálogo \"Acciones > Mostrar ID\" en el otro dispositivo. Los espacios y las barras son opcionales (ignorados).",
|
||||
@@ -323,10 +329,10 @@
|
||||
"The folder path cannot be blank.": "La ruta de la carpeta no puede estar en blanco.",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
|
||||
"The following items could not be synchronized.": "Los siguientes elementos no pueden ser sincronizados.",
|
||||
"The following items were changed locally.": "The following items were changed locally.",
|
||||
"The following unexpected items were found.": "The following unexpected items were found.",
|
||||
"The interval must be a positive number of seconds.": "The interval must be a positive number of seconds.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
|
||||
"The following items were changed locally.": "Los siguientes elementos fueron cambiados localmente.",
|
||||
"The following unexpected items were found.": "Los siguientes elementos inesperados fueron encontrados.",
|
||||
"The interval must be a positive number of seconds.": "El intervalo debe ser un número de segundos positivo.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "El intervalo, en segundos, para ejecutar la limpieza del directorio de versiones. Cero para desactivar la limpieza periódica.",
|
||||
"The maximum age must be a number and cannot be blank.": "La edad máxima debe ser un número y no puede estar vacía.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "El tiempo máximo para mantener una versión en días (introducir 0 para mantener las versiones indefinidamente).",
|
||||
"The number of days must be a number and cannot be blank.": "El número de días debe ser un número y no puede estar en blanco.",
|
||||
@@ -336,8 +342,8 @@
|
||||
"The path cannot be blank.": "La ruta no puede estar vacía.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
|
||||
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
|
||||
"There are no folders to share with this device.": "There are no folders to share with this device.",
|
||||
"There are no devices to share this folder with.": "No hay dispositivos con los cuales compartir esta carpeta.",
|
||||
"There are no folders to share with this device.": "No hay carpetas para compartir con este dispositivo.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
|
||||
"This Device": "Este Dispositivo",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
|
||||
@@ -347,18 +353,18 @@
|
||||
"Time the item was last modified": "Hora en que el ítem fue modificado por última vez",
|
||||
"Trash Can File Versioning": "Versionado de archivos de la papelera",
|
||||
"Type": "Tipo",
|
||||
"UNIX Permissions": "UNIX Permissions",
|
||||
"UNIX Permissions": "Permisos de UNIX",
|
||||
"Unavailable": "No disponible",
|
||||
"Unavailable/Disabled by administrator or maintainer": "No disponible/Deshabilitado por el administrador o mantenedor",
|
||||
"Undecided (will prompt)": "No decidido (se preguntará)",
|
||||
"Unexpected Items": "Unexpected Items",
|
||||
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
|
||||
"Unexpected Items": "Elementos Inesperados",
|
||||
"Unexpected items have been found in this folder.": "Se han encontrado Elementos Inesperados en esta carpeta.",
|
||||
"Unignore": "Dejar de ignorar",
|
||||
"Unknown": "Desconocido",
|
||||
"Unshared": "No compartido",
|
||||
"Unshared Devices": "Unshared Devices",
|
||||
"Unshared Folders": "Unshared Folders",
|
||||
"Untrusted": "Untrusted",
|
||||
"Unshared Devices": "Dispositivos no Enlazados",
|
||||
"Unshared Folders": "Carpetas no Compartidas",
|
||||
"Untrusted": "No Confiable",
|
||||
"Up to Date": "Actualizado",
|
||||
"Updated": "Actualizado",
|
||||
"Upgrade": "Actualizar",
|
||||
@@ -368,14 +374,14 @@
|
||||
"Uptime": "Tiempo de funcionamiento",
|
||||
"Usage reporting is always enabled for candidate releases.": "El informe de uso está siempre habilitado en las versiones candidatas.",
|
||||
"Use HTTPS for GUI": "Usar HTTPS para la Interfaz Gráfica de Usuario (GUI)",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "No se ha configurado el nombre de usuario/la contraseña para la autenticación de la GUI. Por favor, considere configurarlos.",
|
||||
"Version": "Versión",
|
||||
"Versions": "Versiones",
|
||||
"Versions Path": "Ruta de las versiones",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se borran automáticamente si son más antiguas que la edad máxima o exceden el número de ficheros permitidos en un intervalo.",
|
||||
"Waiting to Clean": "Waiting to Clean",
|
||||
"Waiting to Scan": "Waiting to Scan",
|
||||
"Waiting to Sync": "Waiting to Sync",
|
||||
"Waiting to Clean": "Esperando para Limpiar",
|
||||
"Waiting to Scan": "Esperando para Escanear",
|
||||
"Waiting to Sync": "Esperando para Sincronizar",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "¡Peligro! Esta ruta es un directorio principal de la carpeta ya existente \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "'Peligro! Esta ruta es un subdirectorio de la carpeta ya existente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Peligro! Esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolder}}\".",
|
||||
@@ -383,7 +389,7 @@
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Advertencia: Si estás utilizando un observador externo como {{syncthingInotify}}, debes asegurarte de que está desactivado.",
|
||||
"Watch for Changes": "Vigila los cambios",
|
||||
"Watching for Changes": "Vigilando los cambios",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "El control de cambios descubre la mayoría de cambios sin el escaneo periódico.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Cuando añada un nuevo dispositivo, tenga en cuenta que este debe añadirse también en el otro lado.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
|
||||
"Yes": "Si",
|
||||
@@ -394,13 +400,14 @@
|
||||
"You have no ignored folders.": "No tienes carpetas ignoradas",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Tienes cambios sin guardar. ¿Quieres descartarlos realmente?",
|
||||
"You must keep at least one version.": "Debes mantener al menos una versión.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debe agregar o cambiar nada localmente en una carpeta \"{{receiveEncrypted}}\".",
|
||||
"days": "días",
|
||||
"directories": "directories",
|
||||
"directories": "directorios",
|
||||
"files": "archivos",
|
||||
"full documentation": "Documentación completa",
|
||||
"items": "Elementos",
|
||||
"seconds": "seconds",
|
||||
"seconds": "segundos",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} puede reintroducir este dispositivo."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Gaur egun tresnekin partekatua",
|
||||
"Danger!": "Lanjera !",
|
||||
"Debugging Facilities": "Arazketa zerbitzuak",
|
||||
"Default Configuration": "Konfigurazio lehenetsia",
|
||||
"Default Device": "Gailu lehenetsia",
|
||||
"Default Folder": "Karpeta lehenetsia",
|
||||
"Default Folder Path": "Partekatzearen sustrai bide lehenetsia",
|
||||
"Defaults": "Lehenetsiak",
|
||||
"Delete Unexpected Items": "Ezabatu ustekabeko elementuak",
|
||||
"Deleted": "Kendua",
|
||||
"Deselect All": "Hautaketa guztia kendu",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Deskargatzea",
|
||||
"Edit": "Aldatu",
|
||||
"Edit Device": "Tresna aldatzea",
|
||||
"Edit Device Defaults": "Editatu gailu lehenetsiak",
|
||||
"Edit Folder": "Editatu karpeta",
|
||||
"Edit Folder Defaults": "Editatu karpeta lehenetsiak",
|
||||
"Editing {%path%}.": "Muntatzea {{path}}",
|
||||
"Enable Crash Reporting": "Aktibatu blokeatzeko txosten automatikoen bidalketa",
|
||||
"Enable NAT traversal": "NAT translazioa aktibatu",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "Elementuak",
|
||||
"seconds": "segunduak",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}}k \"{{folder}}\" partekatze hontan gomitatzen zaitu.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}k \"{{folderlabel}}\" ({{folder}}) hontan gomitatzen zaitu."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}k \"{{folderlabel}}\" ({{folder}}) hontan gomitatzen zaitu.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} -ek gailu hau birsar lezake."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Vaara!",
|
||||
"Debugging Facilities": "Debug -luokat",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Oletuspolku kansioille",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Poistettu",
|
||||
"Deselect All": "Poista valinnat",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Ladataan",
|
||||
"Edit": "Muokkaa",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Muokkaa {{path}}.",
|
||||
"Enable Crash Reporting": "Ota kaatumisraportointi käyttöön",
|
||||
"Enable NAT traversal": "Aktivoi osoitteenmuunnoksen kierto",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "kohteet",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} haluaa jakaa kansion \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} haluaa jakaa kansion \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} haluaa jakaa kansion \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Appareils membres actuels de ce partage :",
|
||||
"Danger!": "Attention !",
|
||||
"Debugging Facilities": "Outils de débogage",
|
||||
"Default Configuration": "Personnalisation des créations",
|
||||
"Default Device": "Définir comme référence pour les réglages",
|
||||
"Default Folder": "Définir comme référence pour les réglages",
|
||||
"Default Folder Path": "Chemin parent par défaut pour les nouveaux partages",
|
||||
"Defaults": "Personnalisation",
|
||||
"Delete Unexpected Items": "Supprimer les éléments inattendus",
|
||||
"Deleted": "Supprimé",
|
||||
"Deselect All": "Tout déselectionner",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Réception",
|
||||
"Edit": "Gérer...",
|
||||
"Edit Device": "Gérer l'appareil",
|
||||
"Edit Device Defaults": "Pour les nouveaux appareils",
|
||||
"Edit Folder": "Gérer le partage",
|
||||
"Edit Folder Defaults": "Pour les nouveaux partages",
|
||||
"Editing {%path%}.": "Modification de {{path}}.",
|
||||
"Enable Crash Reporting": "Activer l'envoi des rapports de plantage automatiques",
|
||||
"Enable NAT traversal": "Activer la translation d'adresses (NAT)",
|
||||
@@ -206,7 +212,7 @@
|
||||
"Outgoing Rate Limit (KiB/s)": "Limite du débit d'envoi (Kio/s)",
|
||||
"Override Changes": "Écraser les changements",
|
||||
"Path": "Chemin",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Chemin vers le répertoire à partager dans l'appareil local. Il sera créé s'il n'existe pas. Vous pouvez entrer un chemin absolu (p.ex \"/home/moi/Sync/Exemple\") ou relatif à celui du programme (p.ex \"..\\Partages\\Exemple\" - utile pour installation portable). Le caractère tilde (~, ou ~+Espace sous Windows XP+Azerty) peut être utilisé comme raccourci vers",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Chemin vers le répertoire à partager dans l'appareil local. Il sera créé s'il n'existe pas. Vous pouvez entrer un chemin absolu (p.ex \"/home/moi/Sync/Exemple\") ou relatif à celui du programme (p.ex \"..\\Partages\\Exemple\" - utile pour installation portable). Le caractère tilde (~, ou ~+Espace sous Windows) peut être utilisé comme raccourci vers\nDans l'écran de \"Personnalisation\" des valeurs par défaut, ce champ indique le chemin dans lequel les partages acceptés automatiquement seront créés, ainsi que chemin de base suggéré lors de l'enregistrement des nouveaux partages. Le caractère tilde (~) est un raccourci pour {{tilde}}.",
|
||||
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "Chemin dans lequel les partages acceptés automatiquement seront créés, ainsi que chemin suggéré lors de l'enregistrement des nouveaux partages via cette interface graphique. Le caractère tilde (~) est un raccourci pour {{tilde}}.",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Chemin où les versions seront conservées (laisser vide pour le chemin par défaut de .stversions (caché) dans le partage).\nChemin relatif ou absolu (recommandé), mais dans un répertoire non synchronisé (par masque ou hors du chemin du partage).\nSur la même partition ou système de fichiers (recommandé).",
|
||||
"Pause": "Pause",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "élément(s)",
|
||||
"seconds": "secondes",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vous invite au partage \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vous invite au partage \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vous invite au partage \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} pourrait ré-enrôler cet appareil."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Op dit stuit Dielt mei Apparaten",
|
||||
"Danger!": "Gefaar!",
|
||||
"Debugging Facilities": "Debug-foarsjennings",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Standert Map-paad",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Fuortsmiten",
|
||||
"Deselect All": "Alles Deselektearje",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Oan it downloaden",
|
||||
"Edit": "Bewurkje",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "{{path}} wurd bewurke.",
|
||||
"Enable Crash Reporting": "Automatyske Rapportaazje fan Fêstrinners Oansette",
|
||||
"Enable NAT traversal": "NAT-trochkruse ynskeakelje",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "items",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wol map \"{{folder}}\" diele.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wol map \"{{folderlabel}}\" ({{folder}}) diele."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wol map \"{{folderlabel}}\" ({{folder}}) diele.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Eszközök, melyekkel jelenleg meg van osztva",
|
||||
"Danger!": "Veszély!",
|
||||
"Debugging Facilities": "Hibakeresési képességek",
|
||||
"Default Configuration": "Alapértelmezett beállítások",
|
||||
"Default Device": "Alapértelmezett eszköz",
|
||||
"Default Folder": "Alapértelmezett mappa",
|
||||
"Default Folder Path": "Alapértelmezett mappa útvonala",
|
||||
"Defaults": "Alapértelmezések",
|
||||
"Delete Unexpected Items": "Váratlan elemek törlése",
|
||||
"Deleted": "Törölve",
|
||||
"Deselect All": "Kijelölés megszüntetése",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Letöltés",
|
||||
"Edit": "Szerkesztés",
|
||||
"Edit Device": "Eszköz szerkesztése",
|
||||
"Edit Device Defaults": "Eszköz alapértelmezéseinek szerkesztése",
|
||||
"Edit Folder": "Mappa szerkesztése",
|
||||
"Edit Folder Defaults": "Mappa alapértelmezéseinek szerkesztése",
|
||||
"Editing {%path%}.": "{{path}} szerkesztése.",
|
||||
"Enable Crash Reporting": "Összeomlás-jelentés engedélyezése",
|
||||
"Enable NAT traversal": "NAT bejárás engedélyezése",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "elem",
|
||||
"seconds": "másodperc",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} szeretné megosztani a mappát: „{{folder}}”.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} szeretné megosztani a mappát: „{{folderlabel}}” ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} szeretné megosztani a mappát: „{{folderlabel}}” ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} újra bevezetheti ezt az eszközt."
|
||||
}
|
||||
@@ -18,14 +18,14 @@
|
||||
"Advanced": "Avanzato",
|
||||
"Advanced Configuration": "Configurazione Avanzata",
|
||||
"All Data": "Tutti i Dati",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Tutte le cartelle condivise con questo dispositivo devono essere protette da una password, in modo tale che tutti i dati inviati siano illeggibili senza la password fornita.",
|
||||
"Allow Anonymous Usage Reporting?": "Abilitare Statistiche Anonime di Utilizzo?",
|
||||
"Allowed Networks": "Reti Consentite.",
|
||||
"Alphabetic": "Alfabetico",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
|
||||
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Are you sure you want to permanently delete all these files?": "Sei sicuro di voler eliminare definitivamente tutti questi file?",
|
||||
"Are you sure you want to remove device {%name%}?": "Sei sicuro di voler rimuovere il dispositivo {{name}}?",
|
||||
"Are you sure you want to remove folder {%label%}?": "Sei sicuro di voler rimuovere la cartella {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "Sei sicuro di voler ripristinare {{count}} file?",
|
||||
@@ -61,18 +61,22 @@
|
||||
"Currently Shared With Devices": "Attualmente Condiviso Con Dispositivi",
|
||||
"Danger!": "Pericolo!",
|
||||
"Debugging Facilities": "Servizi di Debug",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Percorso Cartella di Default",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Elimina elementi imprevisti",
|
||||
"Deleted": "Cancellato",
|
||||
"Deselect All": "Deseleziona tutto",
|
||||
"Deselect devices to stop sharing this folder with.": "Deseleziona i dispositivi con cui interrompere la condivisione di questa cartella.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
|
||||
"Deselect folders to stop sharing with this device.": "Deseleziona le cartelle per interromperne la condivisione con questo dispositivo.",
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Il dispositivo \"{{name}}\" ({{device}} - {{address}}) chiede di connettersi. Aggiungere il nuovo dispositivo?",
|
||||
"Device ID": "ID Dispositivo",
|
||||
"Device Identification": "Identificazione Dispositivo",
|
||||
"Device Name": "Nome Dispositivo",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "Il dispositivo non è attendibile, inserisci la password di crittografia",
|
||||
"Device rate limits": "Limiti di velocità del dispositivo",
|
||||
"Device that last modified the item": "Dispositivo che ha modificato l'elemento per ultimo",
|
||||
"Devices": "Dispositivi",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Scaricamento in corso",
|
||||
"Edit": "Modifica",
|
||||
"Edit Device": "Modifica Dispositivo",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Modifica Cartella",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Modifica di {{path}}.",
|
||||
"Enable Crash Reporting": "Attiva la Segnalazione degli arresti anomali",
|
||||
"Enable NAT traversal": "Abilita NAT traversal",
|
||||
@@ -127,7 +133,7 @@
|
||||
"Folder Label": "Etichetta per la Cartella",
|
||||
"Folder Path": "Percorso Cartella",
|
||||
"Folder Type": "Tipo di Cartella",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Il tipo di cartella \"{{receiveEncrypted}}\" non può essere modificato dopo aver aggiunto la cartella. È necessario rimuovere la cartella, eliminare o decrittografare i dati sul disco e aggiungere nuovamente la cartella.",
|
||||
"Folders": "Cartelle",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Per le seguenti cartelle si è verificato un errore durante l'avvio della ricerca delle modifiche. Sarà ripetuto ogni minuto, quindi gli errori potrebbero risolversi presto. Se persistono, prova a risolvere il problema sottostante e chiedi aiuto se non puoi.",
|
||||
"Full Rescan Interval (s)": "Intervallo di scansione completa (s)",
|
||||
@@ -145,7 +151,7 @@
|
||||
"Help": "Aiuto",
|
||||
"Home page": "Pagina home",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Tuttavia, le impostazioni correnti indicano che potresti non volerla attiva. Abbiamo disattivato la segnalazione automatica degli arresti anomali per te.",
|
||||
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
|
||||
"If untrusted, enter encryption password": "Se non attendibile, immettere la password di crittografia",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Se vuoi impedire ad altri utenti di questo computer di accedere a Syncthing e attraverso di esso ai tuoi file, prendi in considerazione la configurazione dell'autenticazione.",
|
||||
"Ignore": "Ignora",
|
||||
"Ignore Patterns": "Schemi Esclusione File",
|
||||
@@ -228,9 +234,9 @@
|
||||
"Preview Usage Report": "Anteprima Statistiche di Utilizzo",
|
||||
"Quick guide to supported patterns": "Guida veloce agli schemi supportati",
|
||||
"Random": "Casuale",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Encrypted": "Ricevi crittografato",
|
||||
"Receive Only": "Ricevi solo",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Received data is already encrypted": "I dati ricevuti sono già crittografati",
|
||||
"Recent Changes": "Cambiamenti Recenti",
|
||||
"Reduced by ignore patterns": "Ridotto da schemi di esclusione",
|
||||
"Release Notes": "Note di Rilascio",
|
||||
@@ -260,7 +266,7 @@
|
||||
"Select All": "Seleziona Tutto",
|
||||
"Select a version": "Seleziona una versione",
|
||||
"Select additional devices to share this folder with.": "Seleziona altri dispositivi con cui condividere questa cartella.",
|
||||
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
|
||||
"Select additional folders to share with this device.": "Seleziona altre cartelle da condividere con questo dispositivo.",
|
||||
"Select latest version": "Seleziona l'ultima versione",
|
||||
"Select oldest version": "Seleziona la versione più vecchia",
|
||||
"Select the folders to share with this device.": "Seleziona le cartelle da condividere con questo dispositivo.",
|
||||
@@ -294,7 +300,7 @@
|
||||
"Start Browser": "Avvia Browser",
|
||||
"Statistics": "Statistiche",
|
||||
"Stopped": "Fermato",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Memorizza e sincronizza solo i dati crittografati. Le cartelle su tutti i dispositivi collegati devono essere configurate con la stessa password o essere del tipo \"{{receiveEncrypted}}\".",
|
||||
"Support": "Supporto",
|
||||
"Support Bundle": "Pacchetto di supporto",
|
||||
"Sync Protocol Listen Addresses": "Indirizzi del Protocollo di Sincronizzazione",
|
||||
@@ -324,7 +330,7 @@
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Vengono utilizzati i seguenti intervalli temporali: per la prima ora viene mantenuta una versione ogni 30 secondi, per il primo giorno viene mantenuta una versione ogni ora, per i primi 30 giorni viene mantenuta una versione al giorno, successivamente viene mantenuta una versione ogni settimana fino al periodo massimo impostato.",
|
||||
"The following items could not be synchronized.": "Non è stato possibile sincronizzare i seguenti elementi.",
|
||||
"The following items were changed locally.": "I seguenti elementi sono stati modificati localmente.",
|
||||
"The following unexpected items were found.": "The following unexpected items were found.",
|
||||
"The following unexpected items were found.": "Sono stati trovati i seguenti elementi imprevisti.",
|
||||
"The interval must be a positive number of seconds.": "L'intervallo deve essere un numero positivo di secondi.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'intervallo, in secondi, per l'esecuzione della pulizia nella directory delle versioni. Zero per disabilitare la pulizia periodica.",
|
||||
"The maximum age must be a number and cannot be blank.": "La durata massima dev'essere un numero e non può essere vuoto.",
|
||||
@@ -337,7 +343,7 @@
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Il limite di banda deve essere un numero non negativo (0: nessun limite)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "L'intervallo di scansione deve essere un numero non negativo secondi.",
|
||||
"There are no devices to share this folder with.": "Non ci sono dispositivi con cui condividere questa cartella.",
|
||||
"There are no folders to share with this device.": "There are no folders to share with this device.",
|
||||
"There are no folders to share with this device.": "Non ci sono cartelle da condividere con questo dispositivo.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Verranno effettuati tentativi in automatico e verranno sincronizzati quando l'errore sarà risolto.",
|
||||
"This Device": "Questo Dispositivo",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Ciò potrebbe facilmente permettere agli hackers accesso alla lettura e modifica di qualunque file del tuo computer.",
|
||||
@@ -351,13 +357,13 @@
|
||||
"Unavailable": "Non disponibile",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Non disponibile/Disabilitato dall'amministratore o dal manutentore",
|
||||
"Undecided (will prompt)": "Non deciso (verrà richiesto)",
|
||||
"Unexpected Items": "Unexpected Items",
|
||||
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
|
||||
"Unexpected Items": "Elementi imprevisti",
|
||||
"Unexpected items have been found in this folder.": "Sono stati trovati elementi imprevisti in questa cartella.",
|
||||
"Unignore": "Non ignorare",
|
||||
"Unknown": "Sconosciuto",
|
||||
"Unshared": "Non Condiviso",
|
||||
"Unshared Devices": "Dispositivi non condivisi",
|
||||
"Unshared Folders": "Unshared Folders",
|
||||
"Unshared Folders": "Cartelle non condivise",
|
||||
"Untrusted": "Non attendibile",
|
||||
"Up to Date": "Sincronizzato",
|
||||
"Updated": "Aggiornato",
|
||||
@@ -394,7 +400,7 @@
|
||||
"You have no ignored folders.": "Non ci sono cartelle ignorate.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Hai modifiche non salvate. Vuoi davvero scartarle?",
|
||||
"You must keep at least one version.": "È necessario mantenere almeno una versione.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Non si dovrebbe mai aggiungere o modificare nulla localmente in una cartella \"{{receiveEncrypted}}\".",
|
||||
"days": "giorni",
|
||||
"directories": "directory",
|
||||
"files": "file",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "elementi",
|
||||
"seconds": "secondi",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vuole condividere la cartella \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vuole condividere la cartella \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vuole condividere la cartella \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} potrebbe reintrodurre questo dispositivo."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "現在共有中のデバイス",
|
||||
"Danger!": "危険!",
|
||||
"Debugging Facilities": "デバッグ機能",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "デフォルトのフォルダーパス",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "削除",
|
||||
"Deselect All": "すべて選択解除",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "ダウンロード中",
|
||||
"Edit": "編集",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "{{path}} を編集中",
|
||||
"Enable Crash Reporting": "クラッシュレポートを有効にする",
|
||||
"Enable NAT traversal": "NATトラバーサルを有効にする",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "項目",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} がフォルダー \"{{folder}}\" を共有するよう求めています。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} がフォルダー「{{folderlabel}}」 ({{folder}}) を共有するよう求めています。"
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} がフォルダー「{{folderlabel}}」 ({{folder}}) を共有するよう求めています。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "현재 공유된 기기들",
|
||||
"Danger!": "경고!",
|
||||
"Debugging Facilities": "디버깅 기능",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "기본 폴더 경로",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "삭제됨",
|
||||
"Deselect All": "Deselect All",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "다운로드 중",
|
||||
"Edit": "편집",
|
||||
"Edit Device": "기기 수정",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "폴더 수정",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "{{path}} 수정하기.",
|
||||
"Enable Crash Reporting": "충돌 보고 활성화",
|
||||
"Enable NAT traversal": "NAT traversal 활성화",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "항목",
|
||||
"seconds": "초",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 에서 폴더 \\\"{{folder}}\\\" 를 공유하길 원합니다.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 에서 폴더 \"{{folderLabel}}\" ({{folder}})를 공유하길 원합니다."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 에서 폴더 \"{{folderLabel}}\" ({{folder}})를 공유하길 원합니다.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Šiuo metu bendrinama su įrenginiais",
|
||||
"Danger!": "Pavojus!",
|
||||
"Debugging Facilities": "Derinimo priemonės",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Numatytojo aplanko kelias",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Ištrinta",
|
||||
"Deselect All": "Nuimti žymėjimą nuo visų",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Siunčiama",
|
||||
"Edit": "Redaguoti",
|
||||
"Edit Device": "Taisyti įrenginį",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Taisyti aplanką",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Redaguojama {{path}}.",
|
||||
"Enable Crash Reporting": "Įjungti automatines ataskaitas apie strigtis",
|
||||
"Enable NAT traversal": "Leisti kirsti NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "įrašai",
|
||||
"seconds": "sek.",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} nori dalintis aplanku \"{{folder}}\"",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} nori dalintis aplanku \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} nori dalintis aplanku \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Fare!",
|
||||
"Debugging Facilities": "Feilrettingsverktøy",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Forvalgt mappeplassering",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Slettet",
|
||||
"Deselect All": "Fjern alle markeringer",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Laster ned",
|
||||
"Edit": "Rediger",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Redigerer {{path}}.",
|
||||
"Enable Crash Reporting": "Skru på krasjrapportering",
|
||||
"Enable NAT traversal": "Slå på NAT-traversering",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "elementer",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker å dele mappa \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ønsker å dele mappa \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ønsker å dele mappa \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Momenteel gedeeld met apparaten",
|
||||
"Danger!": "Let op!",
|
||||
"Debugging Facilities": "Debugmogelijkheden",
|
||||
"Default Configuration": "Standaardconfiguratie",
|
||||
"Default Device": "Standaardapparaat",
|
||||
"Default Folder": "Standaardmap",
|
||||
"Default Folder Path": "Standaardmaplocatie",
|
||||
"Defaults": "Standaardwaarden",
|
||||
"Delete Unexpected Items": "Onverwachte items verwijderen",
|
||||
"Deleted": "Verwijderd",
|
||||
"Deselect All": "Alles deselecteren",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Downloaden",
|
||||
"Edit": "Bewerken",
|
||||
"Edit Device": "Apparaat bewerken",
|
||||
"Edit Device Defaults": "Standaardwaarden van apparaat bewerken",
|
||||
"Edit Folder": "Map bewerken",
|
||||
"Edit Folder Defaults": "Standaardwaarden van map bewerken",
|
||||
"Editing {%path%}.": "{{path}} bewerken.",
|
||||
"Enable Crash Reporting": "Crashrapportage inschakelen",
|
||||
"Enable NAT traversal": "NAT traversal inschakelen",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "items",
|
||||
"seconds": "seconden",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil map \"{{folder}}\" delen.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wil map \"{{folderlabel}}\" ({{folder}}) delen."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wil map \"{{folderlabel}}\" ({{folder}}) delen.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan dit apparaat mogelijk opnieuw introduceren."
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
"Bugs": "Błędy",
|
||||
"Changelog": "Historia zmian",
|
||||
"Clean out after": "Uporządkuj",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
"Cleaning Versions": "Wyczyść wersje",
|
||||
"Cleanup Interval": "Cleanup Interval",
|
||||
"Click to see discovery failures": "Kliknij aby zobaczyć błędy odnajdywania",
|
||||
"Close": "Zamknij",
|
||||
@@ -49,7 +49,7 @@
|
||||
"Comment, when used at the start of a line": "Komentarz, jeżeli użyty na początku linii",
|
||||
"Compression": "Kompresja",
|
||||
"Configured": "Skonfigurowane",
|
||||
"Connected (Unused)": "Connected (Unused)",
|
||||
"Connected (Unused)": "Połączone (nieużywane)",
|
||||
"Connection Error": "Błąd połączenia",
|
||||
"Connection Type": "Rodzaj połączenia",
|
||||
"Connections": "Połączenia",
|
||||
@@ -58,10 +58,14 @@
|
||||
"Copied from original": "Skopiowane z oryginału",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Wszelkie prawa zastrzeżone © 2014-2019 dla twórców:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Ustawienie wzorów ignorowania, nadpisze istniejący plik w {{path}}.",
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Currently Shared With Devices": "Aktualnie udostępniane między urządzeniami",
|
||||
"Danger!": "Niebezpieczne!",
|
||||
"Debugging Facilities": "Odpluskwianie",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Domyślna ścieżka folderu",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Usunięto",
|
||||
"Deselect All": "Odznacz wszystko",
|
||||
@@ -84,7 +88,7 @@
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Odrzuć",
|
||||
"Disconnected": "Rozłączony",
|
||||
"Disconnected (Unused)": "Disconnected (Unused)",
|
||||
"Disconnected (Unused)": "Rozłączono (nieużywane)",
|
||||
"Discovered": "Odkryte",
|
||||
"Discovery": "Odnajdywanie",
|
||||
"Discovery Failures": "Błędy odnajdowania",
|
||||
@@ -96,8 +100,10 @@
|
||||
"Downloaded": "Pobrane",
|
||||
"Downloading": "Pobieranie",
|
||||
"Edit": "Edytuj",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Device": "Edytuj urządzenie",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edytuj folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Edytowanie {{path}}.",
|
||||
"Enable Crash Reporting": "Włącz raportowanie błędów",
|
||||
"Enable NAT traversal": "Włącz trawersowanie NAT",
|
||||
@@ -212,7 +218,7 @@
|
||||
"Pause": "Zatrzymaj",
|
||||
"Pause All": "Zatrzymaj wszystkie",
|
||||
"Paused": "Zatrzymany",
|
||||
"Paused (Unused)": "Paused (Unused)",
|
||||
"Paused (Unused)": "Wstrzymane (nieużywane)",
|
||||
"Pending changes": "Oczekujące zmiany",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Okresowe skanowanie w podanym przedziale czasu i wyłączone obserwowanie zmian",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Okresowe skanowanie w podanym przedziale czasu i włączone obserwowanie zmian",
|
||||
@@ -223,12 +229,12 @@
|
||||
"Please wait": "Proszę czekać",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefiks wskazujący, że plik może zostać usunięty w przypadku zapobiegania usunięciu katalogu",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefiks wskazujący, że wzorzec powinien być dopasowany bez rozróżniania wielkości liter",
|
||||
"Preparing to Sync": "Preparing to Sync",
|
||||
"Preparing to Sync": "Przygotowanie do synchronizacji",
|
||||
"Preview": "Podgląd",
|
||||
"Preview Usage Report": "Podgląd raportu użycia.",
|
||||
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
|
||||
"Random": "Losowo",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Encrypted": "Odbieraj zaszyfrowane",
|
||||
"Receive Only": "Tylko odbieraj",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Recent Changes": "Ostatnie zmiany",
|
||||
@@ -271,7 +277,7 @@
|
||||
"Share Folder": "Udostępnij folder",
|
||||
"Share Folders With Device": "Udostępnij foldery między urządzeniami",
|
||||
"Share this folder?": "Udostępnić ten folder?",
|
||||
"Shared Folders": "Shared Folders",
|
||||
"Shared Folders": "Udostępnione foldery",
|
||||
"Shared With": "Współdzielony z",
|
||||
"Sharing": "Współdzielenie",
|
||||
"Show ID": "Pokaż ID",
|
||||
@@ -309,7 +315,7 @@
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing nie może przetworzyć twojego zapytania. Proszę przeładuj stronę lub zrestartuj Syncthing, jeśli problem pozostanie.",
|
||||
"Take me back": "Zabierz mnie z powrotem",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Adres GUI jest nadpisywany przez opcje uruchamiania. Zmiany tutaj nie będą obowiązywać, dopóki ta opcja uruchamiania jest używana.",
|
||||
"The Syncthing Authors": "The Syncthing Authors",
|
||||
"The Syncthing Authors": "Autorzy Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Interfejs administracyjny Syncthing jest skonfigurowany w sposób pozwalający na zdalny dostęp bez hasła.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Zebrane statystyki są publicznie dostępne pod poniższym linkiem.",
|
||||
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
|
||||
@@ -357,8 +363,8 @@
|
||||
"Unknown": "Nieznany",
|
||||
"Unshared": "Nieudostępnione",
|
||||
"Unshared Devices": "Unshared Devices",
|
||||
"Unshared Folders": "Unshared Folders",
|
||||
"Untrusted": "Untrusted",
|
||||
"Unshared Folders": "Nieudostępnione foldery",
|
||||
"Untrusted": "Niezaufane",
|
||||
"Up to Date": "Aktualny",
|
||||
"Updated": "Zaktualizowano",
|
||||
"Upgrade": "Aktualizacja",
|
||||
@@ -374,8 +380,8 @@
|
||||
"Versions Path": "Ścieżka wersji",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Wersje zostają automatycznie usunięte jeżeli są starsze niż maksymalny wiek lub przekraczają liczbę dopuszczalnych wersji.",
|
||||
"Waiting to Clean": "Waiting to Clean",
|
||||
"Waiting to Scan": "Waiting to Scan",
|
||||
"Waiting to Sync": "Waiting to Sync",
|
||||
"Waiting to Scan": "Oczekiwanie na skanowanie",
|
||||
"Waiting to Sync": "Oczekiwanie na synchronizację",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Uwaga, ta ścieżka jest nadrzędnym folderem istniejącego folderu \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Uwaga, ten folder jest nadfolderem istniejącego folderu \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Uwaga, ta ścieżka to podkatalog istniejącego folderu \"{{otherFolder}}\".",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "pozycji",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce udostępnić folder \"{{folder}}\"",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce udostępnić folder \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce udostępnić folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Compartilhado com outros dispositivos",
|
||||
"Danger!": "Perigo!",
|
||||
"Debugging Facilities": "Facilidades de depuração",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Caminho padrão da pasta",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Apagado",
|
||||
"Deselect All": "Deselect All",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Recebendo",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Editando {{path}}.",
|
||||
"Enable Crash Reporting": "Habilitar relatório de falhas",
|
||||
"Enable NAT traversal": "Habilitar NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "itens",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer compartilhar a pasta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer compartilhar a pasta \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer compartilhar a pasta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Dispositivos com os quais está partilhada",
|
||||
"Danger!": "Perigo!",
|
||||
"Debugging Facilities": "Recursos de depuração",
|
||||
"Default Configuration": "Configuração predefinida",
|
||||
"Default Device": "Dispositivo predefinido",
|
||||
"Default Folder": "Pasta predefinida",
|
||||
"Default Folder Path": "Caminho da pasta predefinida",
|
||||
"Defaults": "Predefinições",
|
||||
"Delete Unexpected Items": "Eliminar itens inesperados",
|
||||
"Deleted": "Eliminado",
|
||||
"Deselect All": "Retirar todas as selecções",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Recebendo",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Editar dispositivo",
|
||||
"Edit Device Defaults": "Editar as predefinições do dispositivo",
|
||||
"Edit Folder": "Editar pasta",
|
||||
"Edit Folder Defaults": "Editar as predefinições da pasta",
|
||||
"Editing {%path%}.": "Editando {{path}}.",
|
||||
"Enable Crash Reporting": "Activar Relatório de Estouro",
|
||||
"Enable NAT traversal": "Activar travessia de NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "itens",
|
||||
"seconds": "segundos",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer partilhar a pasta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer partilhar a pasta \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer partilhar a pasta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} poderá reintroduzir este dispositivo"
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "В настоящее время используется совместно с устройствами",
|
||||
"Danger!": "Опасно!",
|
||||
"Debugging Facilities": "Средства отладки",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Путь для папок",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Удалено",
|
||||
"Deselect All": "Снять выделение",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Загрузка",
|
||||
"Edit": "Редактировать",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Правка {{path}}.",
|
||||
"Enable Crash Reporting": "Включить отчёты о сбоях",
|
||||
"Enable NAT traversal": "Включить NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "элементы",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хочет поделиться папкой «{{folder}}».",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хочет поделиться папкой «{{folderlabel}}» ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хочет поделиться папкой «{{folderlabel}}» ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Danger!": "Pozor!",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Predvolená adresárová cesta",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Zmazané",
|
||||
"Deselect All": "Odznačiť všetko",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Sťahovanie",
|
||||
"Edit": "Upraviť",
|
||||
"Edit Device": "Upraviť zariadenie",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Upraviť priečinok",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Úprava {{path}}.",
|
||||
"Enable Crash Reporting": "Zapnúť hlásenie chýb",
|
||||
"Enable NAT traversal": "Povoliť priechod NAT",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "položiek",
|
||||
"seconds": "sekúnd",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce zdieľať adresár \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "För närvarande delas med enheter",
|
||||
"Danger!": "Fara!",
|
||||
"Debugging Facilities": "Felsökningsfunktioner",
|
||||
"Default Configuration": "Standardkonfiguration",
|
||||
"Default Device": "Standardenhet",
|
||||
"Default Folder": "Standardmapp",
|
||||
"Default Folder Path": "Standardmappsökväg",
|
||||
"Defaults": "Standard",
|
||||
"Delete Unexpected Items": "Ta bort oväntade objekt",
|
||||
"Deleted": "Borttagna",
|
||||
"Deselect All": "Avmarkera alla",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Hämtar",
|
||||
"Edit": "Redigera",
|
||||
"Edit Device": "Redigera enhet",
|
||||
"Edit Device Defaults": "Redigera enhetsstandard",
|
||||
"Edit Folder": "Redigera mapp",
|
||||
"Edit Folder Defaults": "Redigera mappstandard",
|
||||
"Editing {%path%}.": "Redigerar {{path}}.",
|
||||
"Enable Crash Reporting": "Aktivera kraschrapportering",
|
||||
"Enable NAT traversal": "Aktivera NAT-genompassering",
|
||||
@@ -297,7 +303,7 @@
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Lagrar och synkroniserar endast krypterade data. Mappar på alla anslutna enheter måste ställas in med samma lösenord eller vara av typen \"{{receiveEncrypted}}\".",
|
||||
"Support": "Support",
|
||||
"Support Bundle": "Support Bundle",
|
||||
"Sync Protocol Listen Addresses": "Lyssnaradresser för synkroniseringsprotokollet",
|
||||
"Sync Protocol Listen Addresses": "Lyssnaradresser för synkroniseringsprotokoll",
|
||||
"Syncing": "Synkroniserar",
|
||||
"Syncthing has been shut down.": "Syncthing har stängts.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing innehåller följande mjukvarupaket eller delar av dem:",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "objekt",
|
||||
"seconds": "sekunder",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mapp \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mapp \"{{folderlabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mapp \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan återinföra denna enhet."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "Şu Anda Paylaşıldığı Cihazlar",
|
||||
"Danger!": "Tehlike!",
|
||||
"Debugging Facilities": "Hata Ayıklama Olanakları",
|
||||
"Default Configuration": "Varsayılan Yapılandırma",
|
||||
"Default Device": "Varsayılan Cihaz",
|
||||
"Default Folder": "Varsayılan Klasör",
|
||||
"Default Folder Path": "Varsayılan Klasör Yolu",
|
||||
"Defaults": "Varsayılanlar",
|
||||
"Delete Unexpected Items": "Beklenmeyen Öğeleri Sil",
|
||||
"Deleted": "Silinen",
|
||||
"Deselect All": "Tüm Seçimi Kaldır",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "İndiriliyor",
|
||||
"Edit": "Düzenle",
|
||||
"Edit Device": "Düzenlenen Cihaz",
|
||||
"Edit Device Defaults": "Cihaz Varsayılanlarını Düzenle",
|
||||
"Edit Folder": "Düzenlenen Klasör",
|
||||
"Edit Folder Defaults": "Klasör Varsayılanlarını Düzenle",
|
||||
"Editing {%path%}.": "Düzenlenen {{path}}.",
|
||||
"Enable Crash Reporting": "Çökme Bildirmeyi etkinleştir",
|
||||
"Enable NAT traversal": "NAT geçişini etkinleştir",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "öğe",
|
||||
"seconds": "saniye",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}}, \"{{folder}}\" klasörünü paylaşmak istiyor.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}, \"{{folderlabel}}\" ({{folder}}) klasörünü paylaşmak istiyor."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}, \"{{folderlabel}}\" ({{folder}}) klasörünü paylaşmak istiyor.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} bu cihazı yeniden tanıtabilir."
|
||||
}
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "На даний момент є спільний доступ пристроїв",
|
||||
"Danger!": "Небезпечно!",
|
||||
"Debugging Facilities": "Засоби відладки",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "Шлях до директорії по замовчанню",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "Видалене",
|
||||
"Deselect All": "Зняти вибір з усіх",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "Завантаження",
|
||||
"Edit": "Редагувати",
|
||||
"Edit Device": "Налаштування пристрою",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "Налаштування директорії",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "Редагування {{path}}.",
|
||||
"Enable Crash Reporting": "Увімкнути звітування про збої",
|
||||
"Enable NAT traversal": "Увімкнути NAT traversal",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "елементи",
|
||||
"seconds": "секунд",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися директорією \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хоче поділитися директорією \"{{folderLabel}}\" ({{folder}})."
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хоче поділитися директорією \"{{folderLabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令接管了版本控制。该外部命令必须自行从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
|
||||
"Anonymous Usage Reporting": "匿名使用报告",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情况的报告格式已经变更。是否要迁移到新的格式?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Are you sure you want to permanently delete all these files?": "确认要永久删除这些文件吗?",
|
||||
"Are you sure you want to remove device {%name%}?": "您确定要移除设备 {{name}} 吗?",
|
||||
"Are you sure you want to remove folder {%label%}?": "您确定要移除文件夹 {{label}} 吗?",
|
||||
"Are you sure you want to restore {%count%} files?": "您确定要恢复这 {{count}} 个文件吗?",
|
||||
@@ -61,7 +61,11 @@
|
||||
"Currently Shared With Devices": "当前设备已共享",
|
||||
"Danger!": "危险!",
|
||||
"Debugging Facilities": "调试功能",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "默认文件夹路径",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Deleted": "已删除",
|
||||
"Deselect All": "取消全选",
|
||||
@@ -72,7 +76,7 @@
|
||||
"Device ID": "设备 ID",
|
||||
"Device Identification": "设备标识",
|
||||
"Device Name": "设备名",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "设备不可信,请输入加密密码",
|
||||
"Device rate limits": "设备速率限制",
|
||||
"Device that last modified the item": "最近修改该项的设备",
|
||||
"Devices": "设备",
|
||||
@@ -97,7 +101,9 @@
|
||||
"Downloading": "下载中",
|
||||
"Edit": "选项",
|
||||
"Edit Device": "编辑设备",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "编辑文件夹",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "正在编辑 {{path}}。",
|
||||
"Enable Crash Reporting": "启用自动发送崩溃报告",
|
||||
"Enable NAT traversal": "启用 NAT 遍历",
|
||||
@@ -402,5 +408,6 @@
|
||||
"items": "条目",
|
||||
"seconds": "秒",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想将 “{{folder}}” 文件夹共享给您。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享 \"{{folderlabel}}\" ({{folder}}) 文件夹给您。"
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享 \"{{folderlabel}}\" ({{folder}}) 文件夹给您。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
}
|
||||
413
gui/default/assets/lang/lang-zh-HK.json
Normal file
413
gui/default/assets/lang/lang-zh-HK.json
Normal file
@@ -0,0 +1,413 @@
|
||||
{
|
||||
"A device with that ID is already added.": "您已添加過擁有相同 ID 的設備",
|
||||
"A negative number of days doesn't make sense.": "負數天數不符合邏輯。",
|
||||
"A new major version may not be compatible with previous versions.": "重大更新可能與之前的版本之間無法兼容",
|
||||
"API Key": "API Key",
|
||||
"About": "關於",
|
||||
"Action": "操作",
|
||||
"Actions": "操作",
|
||||
"Add": "新增",
|
||||
"Add Device": "新增設備",
|
||||
"Add Folder": "新增資料夾",
|
||||
"Add Remote Device": "新增遠程設備",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "將這個設備上那些,跟本機有著共同文件夾的「遠程設備」,都添加到本機的「遠程設備」列表。",
|
||||
"Add new folder?": "新增新文件夾?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整重新掃瞄的間隔將增大(時間 60,以新的默認 1 小時為例)。你也可以在選擇「否」後手動配置每個文件夾的時間。",
|
||||
"Address": "地址",
|
||||
"Addresses": "地址列表",
|
||||
"Advanced": "高級",
|
||||
"Advanced Configuration": "高級配置",
|
||||
"All Data": "所有資料",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
|
||||
"Allow Anonymous Usage Reporting?": "允許匿名使用報告?",
|
||||
"Allowed Networks": "允許的網絡",
|
||||
"Alphabetic": "字母順序",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令接管了版本控制。該外部命令必須自行從共享文件夾中刪除該文件。如果此應用程序的路徑包含空格,應該用半角引號括起來。",
|
||||
"Anonymous Usage Reporting": "匿名使用報告",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情況的報告格式已經變更。是否要遷移到新的格式?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Are you sure you want to remove device {%name%}?": "您確定要移除設備 {{name}} 嗎?",
|
||||
"Are you sure you want to remove folder {%label%}?": "您確定要移除文件夾 {{label}} 嗎?",
|
||||
"Are you sure you want to restore {%count%} files?": "您確定要恢復這 {{count}} 個文件嗎?",
|
||||
"Are you sure you want to upgrade?": "你確定要升級嗎?",
|
||||
"Auto Accept": "自動接受",
|
||||
"Automatic Crash Reporting": "自動發送崩潰報告",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動升級現在提供了穩定版本和候選發佈版的選項。",
|
||||
"Automatic upgrades": "自動升級",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "候選發佈版會一直啟用自動升級。",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "在本機默認文件夾中,自動地創建或共享這個設備共享出來的所有文件夾。",
|
||||
"Available debug logging facilities:": "可用的調試日誌功能:",
|
||||
"Be careful!": "小心!",
|
||||
"Bugs": "問題回報",
|
||||
"Changelog": "更新日誌",
|
||||
"Clean out after": "在該時間後清除",
|
||||
"Cleaning Versions": "清除版本",
|
||||
"Cleanup Interval": "清除間隔",
|
||||
"Click to see discovery failures": "點擊查看設備發現錯誤",
|
||||
"Close": "關閉",
|
||||
"Command": "命令",
|
||||
"Comment, when used at the start of a line": "註釋,在行首使用",
|
||||
"Compression": "壓縮",
|
||||
"Configured": "已配置",
|
||||
"Connected (Unused)": "已連接(未使用)",
|
||||
"Connection Error": "連接出錯",
|
||||
"Connection Type": "連接類型",
|
||||
"Connections": "連接",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing 現在可以持續監視更改了。這將檢測磁盤上的更改,然後對有修改的路徑發起掃瞄。這樣的好處是更改可以更快地傳播,且需要的完整掃瞄會更少。",
|
||||
"Copied from elsewhere": "從其他設備複製",
|
||||
"Copied from original": "從源複製",
|
||||
"Copyright © 2014-2019 the following Contributors:": "版權所有©2014-2019以下貢獻者:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "正在創建忽略模式,覆蓋位於 {{path}} 的已有文件。",
|
||||
"Currently Shared With Devices": "當前設備已共享",
|
||||
"Danger!": "危險!",
|
||||
"Debugging Facilities": "調試功能",
|
||||
"Default Configuration": "Default Configuration",
|
||||
"Default Device": "Default Device",
|
||||
"Default Folder": "Default Folder",
|
||||
"Default Folder Path": "默認文件夾路徑",
|
||||
"Defaults": "Defaults",
|
||||
"Delete Unexpected Items": "刪除不需要項目",
|
||||
"Deleted": "已刪除",
|
||||
"Deselect All": "取消全選",
|
||||
"Deselect devices to stop sharing this folder with.": "反選設備以停止共享此文件夾",
|
||||
"Deselect folders to stop sharing with this device.": "停止選擇文件夾以停止與此設備共享。",
|
||||
"Device": "設備",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "設備 \"{{name}}\"(位於 {{address}} 的 {{device}})請求連接。是否添加新設備?",
|
||||
"Device ID": "設備 ID",
|
||||
"Device Identification": "設備標識",
|
||||
"Device Name": "設備名",
|
||||
"Device is untrusted, enter encryption password": "設備不受信任,請輸入加密密碼",
|
||||
"Device rate limits": "設備速率限制",
|
||||
"Device that last modified the item": "最近修改該項的設備",
|
||||
"Devices": "設備",
|
||||
"Disable Crash Reporting": "禁用自動發送崩潰報告",
|
||||
"Disabled": "已禁用",
|
||||
"Disabled periodic scanning and disabled watching for changes": "已禁用定期掃瞄和更改監視",
|
||||
"Disabled periodic scanning and enabled watching for changes": "已禁用定期掃瞄並啟用更改監視",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "已禁用定期掃瞄但設置更改監視失敗,正在以每 1m 一次重試:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "禁用比較和同步文件權限。 適用於不存在或自定義權限的系統(例如FAT,exFAT,Synology,Android)。",
|
||||
"Discard": "丟棄",
|
||||
"Disconnected": "連接已斷開",
|
||||
"Disconnected (Unused)": "斷開連接(未使用)",
|
||||
"Discovered": "已發現",
|
||||
"Discovery": "設備發現",
|
||||
"Discovery Failures": "設備發現錯誤",
|
||||
"Do not restore": "不要恢復",
|
||||
"Do not restore all": "不要全部恢復",
|
||||
"Do you want to enable watching for changes for all your folders?": "您想要啟用對監視您所有文件夾的更改嗎?",
|
||||
"Documentation": "文檔",
|
||||
"Download Rate": "下載速度",
|
||||
"Downloaded": "已下載",
|
||||
"Downloading": "下載中",
|
||||
"Edit": "選項",
|
||||
"Edit Device": "編輯設備",
|
||||
"Edit Device Defaults": "Edit Device Defaults",
|
||||
"Edit Folder": "編輯文件夾",
|
||||
"Edit Folder Defaults": "Edit Folder Defaults",
|
||||
"Editing {%path%}.": "正在編輯 {{path}}。",
|
||||
"Enable Crash Reporting": "啟用自動發送崩潰報告",
|
||||
"Enable NAT traversal": "啟用 NAT 遍歷",
|
||||
"Enable Relaying": "開啟中繼",
|
||||
"Enabled": "已啟用",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "輸入一個非負數(例如「2.35」)並選擇單位。%表示占磁盤總容量的百分比。",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "輸入一個非特權的端口號 (1024 - 65535)。",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "輸入以半角逗號分隔的(\"tcp://ip:port\", \"tcp://host:port\")設備地址列表,或者輸入「dynamic」以自動發現設備地址。",
|
||||
"Enter ignore patterns, one per line.": "請輸入忽略模式,每行一條。",
|
||||
"Enter up to three octal digits.": "最多輸入三個8進制數字",
|
||||
"Error": "錯誤",
|
||||
"External File Versioning": "外部版本控制",
|
||||
"Failed Items": "失敗的項目",
|
||||
"Failed to setup, retrying": "設置失敗,正在重試。",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本機沒有配置IPv6,則無法連接IPv6服務器是正常的。",
|
||||
"File Pull Order": "文件拉取順序",
|
||||
"File Versioning": "版本控制",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "當文件被 Syncthing 替換或刪除時,將被移動到 .stversions 目錄。",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "當某個文件在其他設備被替換或刪除時,本設備將在 .stversions 目錄中保留該文件的備份,並在文件名中加入時間戳信息。",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "在其它設備中對該文件夾內文件的修改並不會被同步到本機,但是在本機上對其的修改,則會被同步到集群中的其它設備。",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "文件將從集群同步,但本地所作的任何更改都不會被發送到其他設備。",
|
||||
"Filesystem Watcher Errors": "文件系統監視器錯誤",
|
||||
"Filter by date": "按日期篩選",
|
||||
"Filter by name": "按名稱篩選",
|
||||
"Folder": "文件夾",
|
||||
"Folder ID": "文件夾 ID",
|
||||
"Folder Label": "文件夾標籤",
|
||||
"Folder Path": "文件夾路徑",
|
||||
"Folder Type": "文件夾類型",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
"Folders": "文件夾",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "開始監視下列文件夾時發生錯誤。由於每分鐘都會重試,所以錯誤可能很快就消失。如果它們仍存在,請試著修復潛在問題,如不會則請求幫助。",
|
||||
"Full Rescan Interval (s)": "完整掃瞄間隔",
|
||||
"GUI": "圖形用戶界面",
|
||||
"GUI Authentication Password": "圖形管理界面密碼",
|
||||
"GUI Authentication User": "圖形管理界面用戶名",
|
||||
"GUI Authentication: Set User and Password": "GUI身份驗證:設置用戶和密碼",
|
||||
"GUI Listen Address": "GUI 監聽地址",
|
||||
"GUI Theme": "GUI 主題",
|
||||
"General": "常規",
|
||||
"Generate": "生成",
|
||||
"Global Discovery": "全球發現",
|
||||
"Global Discovery Servers": "全球發現服務器",
|
||||
"Global State": "全局狀態",
|
||||
"Help": "幫助",
|
||||
"Home page": "主頁",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "我們已經為您關閉了自動崩潰報告發送功能,因為您當前的設置顯示您可能並不想啟用該功能。",
|
||||
"If untrusted, enter encryption password": "如果不受信任,請輸入加密密碼",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "如果要阻止此計算機上的其他用戶訪問Syncthing並通過它訪問文件,請考慮設置身份驗證。",
|
||||
"Ignore": "忽略",
|
||||
"Ignore Patterns": "忽略模式",
|
||||
"Ignore Permissions": "忽略文件權限",
|
||||
"Ignored Devices": "已忽略的設備",
|
||||
"Ignored Folders": "已忽略的文件夾",
|
||||
"Ignored at": "已忽略於",
|
||||
"Incoming Rate Limit (KiB/s)": "下載速率限制 (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "錯誤的配置可能損壞您文件夾內的內容,使得 Syncthing 無法工作。",
|
||||
"Introduced By": "介紹自",
|
||||
"Introducer": "作為中介",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "對本條件取反(例如:不要排除某項)",
|
||||
"Keep Versions": "保留版本數量",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "大文件優先",
|
||||
"Last Scan": "最後掃瞄",
|
||||
"Last seen": "最後可見",
|
||||
"Latest Change": "最後更改",
|
||||
"Learn more": "瞭解更多",
|
||||
"Limit": "限制",
|
||||
"Listeners": "偵聽程序",
|
||||
"Loading data...": "正在載入數據…",
|
||||
"Loading...": "正在載入…",
|
||||
"Local Additions": "從本地添加",
|
||||
"Local Discovery": "本地發現",
|
||||
"Local State": "本地狀態",
|
||||
"Local State (Total)": "本地狀態匯總",
|
||||
"Locally Changed Items": "本地更改的項目",
|
||||
"Log": "日誌",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "已暫停日誌跟蹤。滾動到底部以繼續。",
|
||||
"Logs": "日誌",
|
||||
"Major Upgrade": "重大更新",
|
||||
"Mass actions": "批量操作",
|
||||
"Maximum Age": "最長保留時間",
|
||||
"Metadata Only": "僅元數據",
|
||||
"Minimum Free Disk Space": "最低可用磁盤空間",
|
||||
"Mod. Device": "修改設備",
|
||||
"Mod. Time": "修改時間",
|
||||
"Move to top of queue": "移動到隊列頂端",
|
||||
"Multi level wildcard (matches multiple directory levels)": "多級通配符(用以匹配多層文件夾)",
|
||||
"Never": "從未",
|
||||
"New Device": "新設備",
|
||||
"New Folder": "新文件夾",
|
||||
"Newest First": "新文件優先",
|
||||
"No": "否",
|
||||
"No File Versioning": "不啟用版本控制",
|
||||
"No files will be deleted as a result of this operation.": "此操作結果不會刪除任何文件。",
|
||||
"No upgrades": "無更新",
|
||||
"Not shared": "未分享",
|
||||
"Notice": "提示",
|
||||
"OK": "確定",
|
||||
"Off": "關閉",
|
||||
"Oldest First": "舊文件優先",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "可選的文件夾說明性標籤。在不同設備上可以不一致。",
|
||||
"Options": "選項",
|
||||
"Out of Sync": "未同步",
|
||||
"Out of Sync Items": "未同步的項目",
|
||||
"Outgoing Rate Limit (KiB/s)": "上傳速度限制 (KiB/s)",
|
||||
"Override Changes": "撤銷改變",
|
||||
"Path": "路徑",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "文件夾在本地的路徑。如果不存在,則會被創建。波浪線符號(~)是如下路徑的縮略符:",
|
||||
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "用於創建自動接受新文件夾的路徑,同時也是通過 UI 添加新文件夾時的默認值。波浪號 (~) 擴展成 {{tilde}}。",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "歷史版本儲存路徑(留空則會默認存儲在共享文件夾中的 .stversions 目錄)。",
|
||||
"Pause": "暫停",
|
||||
"Pause All": "全部暫停",
|
||||
"Paused": "已暫停",
|
||||
"Paused (Unused)": "已暫停(未使用)",
|
||||
"Pending changes": "待定的更改",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "正以給定的間隔定期掃瞄並已禁用更改監視",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "正以給定的間隔定期掃瞄並已啟用更改監視",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "正以給定的間隔定期掃瞄但設置更改監視失敗,正在以每 1m 一次重試:",
|
||||
"Permissions": "權限",
|
||||
"Please consult the release notes before performing a major upgrade.": "請在進行重大更新前查看發佈說明。",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "請在設置對話框中設置 GUI 驗證用戶及其密碼。",
|
||||
"Please wait": "請稍候",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "表示如果刪除了阻止目錄則文件可被刪除的前綴",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "此前綴表示,後面的模式在匹配時不區分大小寫",
|
||||
"Preparing to Sync": "準備同步",
|
||||
"Preview": "預覽",
|
||||
"Preview Usage Report": "預覽使用報告",
|
||||
"Quick guide to supported patterns": "支持的通配符的簡單教程:",
|
||||
"Random": "隨機順序",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Only": "僅接收",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Recent Changes": "最近更改",
|
||||
"Reduced by ignore patterns": "已由忽略模式縮減",
|
||||
"Release Notes": "發佈說明",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "發佈候選版包含最新的特性和修復。它們跟傳統的 Syncthing 雙周發佈版類似。",
|
||||
"Remote Devices": "遠程設備",
|
||||
"Remote GUI": "Remote GUI",
|
||||
"Remove": "移除",
|
||||
"Remove Device": "移除設備",
|
||||
"Remove Folder": "移除文件夾",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "必需的文件夾唯一標識。同一個文件夾在集群中的所有設備上ID必須相同。",
|
||||
"Rescan": "重新掃瞄",
|
||||
"Rescan All": "全部重新掃瞄",
|
||||
"Rescans": "重新掃瞄",
|
||||
"Restart": "重啟 Syncthing",
|
||||
"Restart Needed": "需要重啟 Syncthing",
|
||||
"Restarting": "重啟中",
|
||||
"Restore": "恢復",
|
||||
"Restore Versions": "恢復歷史版本",
|
||||
"Resume": "恢復",
|
||||
"Resume All": "全部恢復",
|
||||
"Reused": "復用",
|
||||
"Revert Local Changes": "恢復本地更改",
|
||||
"Save": "保存",
|
||||
"Scan Time Remaining": "掃瞄剩餘時間",
|
||||
"Scanning": "掃瞄中",
|
||||
"See external versioning help for supported templated command line parameters.": "有關受支持的模板命令行參數,請參閱外部版本控制幫助。",
|
||||
"Select All": "全選",
|
||||
"Select a version": "選擇版本",
|
||||
"Select additional devices to share this folder with.": "選擇其他共享此文件夾的設備",
|
||||
"Select additional folders to share with this device.": "選擇其他文件夾與此設備共享。",
|
||||
"Select latest version": "選擇最新的版本",
|
||||
"Select oldest version": "選擇最舊的版本",
|
||||
"Select the folders to share with this device.": "選擇與該設備共享的文件夾。",
|
||||
"Send & Receive": "發送與接收",
|
||||
"Send Only": "僅發送",
|
||||
"Settings": "設置",
|
||||
"Share": "共享",
|
||||
"Share Folder": "共享文件夾",
|
||||
"Share Folders With Device": "將指定文件夾共享給設備",
|
||||
"Share this folder?": "是否共享該文件夾?",
|
||||
"Shared Folders": "共享文件夾",
|
||||
"Shared With": "共享給",
|
||||
"Sharing": "共享",
|
||||
"Show ID": "顯示 ID",
|
||||
"Show QR": "顯示 QR 碼",
|
||||
"Show diff with previous version": "顯示與先前版本的差異",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "在集群狀態中顯示該名稱,而不是設備 ID。將會作為當前設備的可選的默認名稱,報告給所有其他設備。",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "在集群狀態中顯示該名稱,而不是設備 ID。如果設置為空,則會使用目標設備自報的默認名稱。",
|
||||
"Shutdown": "關閉 Syncthing",
|
||||
"Shutdown Complete": "關閉完成",
|
||||
"Simple File Versioning": "簡易版本控制",
|
||||
"Single level wildcard (matches within a directory only)": "單級通配符(僅匹配單層文件夾)",
|
||||
"Size": "大小",
|
||||
"Smallest First": "小文件優先",
|
||||
"Some items could not be restored:": "有些項目無法被恢復:",
|
||||
"Source Code": "源代碼",
|
||||
"Stable releases and release candidates": "穩定版本和發佈候選版",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "穩定版本約延遲兩個星期。這段時間它們將作為發佈候選版來測試。",
|
||||
"Stable releases only": "僅穩定版本",
|
||||
"Staggered File Versioning": "階段版本控制",
|
||||
"Start Browser": "啟動瀏覽器",
|
||||
"Statistics": "統計",
|
||||
"Stopped": "已停止",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Support": "支持",
|
||||
"Support Bundle": "支持捆綁包",
|
||||
"Sync Protocol Listen Addresses": "協議監聽地址",
|
||||
"Syncing": "同步中",
|
||||
"Syncthing has been shut down.": "Syncthing 已關閉。",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing 使用了下列軟件或其中的一部分:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 是個以 MPL v2.0 授權的免費開源軟件。",
|
||||
"Syncthing is restarting.": "Syncthing 正在重啟。",
|
||||
"Syncthing is upgrading.": "Syncthing 正在升級。",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 現在已經支持將崩潰報告自動發送給開發者。該功能默認開啟。",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing 似乎關閉了,或者您的網絡連接存在故障。重試中…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing 在處理您的請求時似乎遇到了問題。如果問題持續,請刷新頁面,或重啟 Syncthing。",
|
||||
"Take me back": "帶我回去",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "GUI 地址已被啟動選項覆蓋。當覆蓋存在時,此處的更改就不會生效。",
|
||||
"The Syncthing Authors": "Syncthing的作者",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "當前配置允許在不使用密碼的情況下遠程訪問 Syncthing 管理界面。",
|
||||
"The aggregated statistics are publicly available at the URL below.": "全局統計數據公佈於以下 URL。",
|
||||
"The cleanup interval cannot be blank.": "清理間隔不能為空。",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "設置已經保存,但是還未生效。Syncthing 需要重啟以啟用新的設置。",
|
||||
"The device ID cannot be blank.": "設備 ID 不能為空。",
|
||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "在這裡所需要輸入的設備 ID,可以在目標設備的「操作->顯示 ID」中看到。空格和橫線可選(將會被忽略)。",
|
||||
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "經過加密的使用報告會每天發送。它用來跟蹤統計使用本軟件的平台,文件夾大小,以及本軟件的版本。如果報告的內容有任何變化,本對話框會再次彈出提示您。",
|
||||
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "輸入的設備 ID 似乎無效。設備 ID 包含字母和數字,長度為 52 或 56,空格和橫線不計在內。",
|
||||
"The folder ID cannot be blank.": "文件夾 ID 不能為空。",
|
||||
"The folder ID must be unique.": "文件夾 ID 不得重複。",
|
||||
"The folder path cannot be blank.": "文件夾路徑不能為空。",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保留的歷史版本會遵循以下條件:最近一小時內的歷史版本,更新間隔小於三十秒的僅保留一份。最近一天內的歷史版本,更新間隔小於一小時的僅保留一份。最近一個月內的歷史版本,更新間隔小於一天的僅保留一份。距離現在超過一個月且小於最長保留時間的,更新間隔小於一周的僅保留一份。",
|
||||
"The following items could not be synchronized.": "下列項目無法被同步。",
|
||||
"The following items were changed locally.": "下列項目存在本地更改。",
|
||||
"The following unexpected items were found.": "找到以下不需要項目。",
|
||||
"The interval must be a positive number of seconds.": "間隔必須為正數秒。",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "在版本目錄中運行清理的間隔(秒)。0表示禁用定期清除。",
|
||||
"The maximum age must be a number and cannot be blank.": "最長保留時間必須為數字,且不能為空。",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "歷史版本保留的最長天數,0 為永久保存。",
|
||||
"The number of days must be a number and cannot be blank.": "天數必須為數字,且不能為空。",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "文件保存在回收站的天數。零表示永久。",
|
||||
"The number of old versions to keep, per file.": "每個文件保留的版本數量上限。",
|
||||
"The number of versions must be a number and cannot be blank.": "保留版本數量必須為數字,且不能為空。",
|
||||
"The path cannot be blank.": "路徑不能為空。",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "傳輸速度限制為非負整數(0 表示不限制)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "掃瞄間隔單位為秒,且不能為負數。",
|
||||
"There are no devices to share this folder with.": "沒有設備共享此文件夾",
|
||||
"There are no folders to share with this device.": "沒有與此設備共享的文件夾。",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "系統將會自動重試,當錯誤被解決時,它們將會被同步。",
|
||||
"This Device": "當前設備",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "這會讓駭客能夠輕而易舉地訪問及修改您的文件。",
|
||||
"This is a major version upgrade.": "這是一個重大版本更新。",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "此設置控制主(例如索引數據庫)磁盤上需要的可用空間。",
|
||||
"Time": "時間",
|
||||
"Time the item was last modified": "該項最近修改的時間",
|
||||
"Trash Can File Versioning": "回收站式版本控制",
|
||||
"Type": "類型",
|
||||
"UNIX Permissions": "UNIX權限",
|
||||
"Unavailable": "無效",
|
||||
"Unavailable/Disabled by administrator or maintainer": "無效/禁用(由管理員或維護者)",
|
||||
"Undecided (will prompt)": "待定(將提示)",
|
||||
"Unexpected Items": "Unexpected Items",
|
||||
"Unexpected items have been found in this folder.": "在此文件夾中發現了不需要的項目。",
|
||||
"Unignore": "解除忽略",
|
||||
"Unknown": "未知",
|
||||
"Unshared": "未共享",
|
||||
"Unshared Devices": "未共享設備",
|
||||
"Unshared Folders": "未共享的文件夾",
|
||||
"Untrusted": "不信任",
|
||||
"Up to Date": "同步完成",
|
||||
"Updated": "已更新",
|
||||
"Upgrade": "更新",
|
||||
"Upgrade To {%version%}": "升級至版本 {{version}}",
|
||||
"Upgrading": "升級中",
|
||||
"Upload Rate": "上傳速度",
|
||||
"Uptime": "已啟動",
|
||||
"Usage reporting is always enabled for candidate releases.": "發佈候選版總是會啟用使用報告。",
|
||||
"Use HTTPS for GUI": "使用加密連接到圖形管理頁面",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "尚未為GUI身份驗證設置用戶名/密碼。 請考慮進行設置。",
|
||||
"Version": "版本",
|
||||
"Versions": "歷史版本",
|
||||
"Versions Path": "歷史版本路徑",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "超過最長保留時間,或者不滿足下列條件的歷史版本,將會被刪除。",
|
||||
"Waiting to Clean": "等待清除",
|
||||
"Waiting to Scan": "等待掃瞄",
|
||||
"Waiting to Sync": "等待同步",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "警告,該路徑是已有文件夾\"{{otherFolder}}\"的上級目錄。",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告,該路徑是已有文件夾\"{{otherFolderLabel}}\" ({{otherFolder}})的上級目錄。",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告,該路徑是已有文件夾\"{{otherFolder}}\"的下級目錄。",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告,該路徑是已有文件夾\"{{otherFolderLabel}}\" ({{otherFolder}})的下級目錄。",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "警告:如果你在使用外部的監視器如 {{syncthingInotify}},你應該確保它已經取消激活。",
|
||||
"Watch for Changes": "監視更改",
|
||||
"Watching for Changes": "正在監視更改",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "對更改的監視無需定期掃瞄就可以發現大多數更改。",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "若您在本機添加新設備,記住您也必須在這個新設備上添加本機。",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "若你添加了新文件夾,記住文件夾 ID 是用以在不同設備間建立聯繫的。在不同設備間擁有相同 ID 的文件夾將會被同步。且文件夾 ID 區分大小寫。",
|
||||
"Yes": "是",
|
||||
"You can also select one of these nearby devices:": "您也可以從這些附近的設備中選擇:",
|
||||
"You can change your choice at any time in the Settings dialog.": "您可以在任何時候在設置對話框中更改選擇。",
|
||||
"You can read more about the two release channels at the link below.": "您可以從以下鏈接讀取更多關於兩個發行渠道的信息。",
|
||||
"You have no ignored devices.": "你沒有已忽略的設備。",
|
||||
"You have no ignored folders.": "你沒有已忽略的文件夾。",
|
||||
"You have unsaved changes. Do you really want to discard them?": "你有未保存的更改。你真的要丟棄它們嗎?",
|
||||
"You must keep at least one version.": "您必須保留至少一個版本。",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "您絕對不應在\"{{receiveEncrypted}}\"文件夾中本地添加或更改任何內容。",
|
||||
"days": "天",
|
||||
"directories": "目錄",
|
||||
"files": "文件",
|
||||
"full documentation": "完整文檔",
|
||||
"items": "條目",
|
||||
"seconds": "秒",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想將 「{{folder}}」 文件夾共享給您。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享 \"{{folderlabel}}\" ({{folder}}) 文件夾給您。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} 可能會重新引入此設備。"
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
{
|
||||
"A device with that ID is already added.": "該裝置識別碼已被新增。",
|
||||
"A negative number of days doesn't make sense.": "一個負的天數並不合理。",
|
||||
"A new major version may not be compatible with previous versions.": "新的主要版本可能與以前的版本不相容。",
|
||||
"A new major version may not be compatible with previous versions.": "新的主要版本可能與先前的版本不相容。",
|
||||
"API Key": "API 金鑰",
|
||||
"About": "關於",
|
||||
"Action": "動作",
|
||||
"Action": "操作",
|
||||
"Actions": "操作",
|
||||
"Add": "增加",
|
||||
"Add Device": "增加裝置",
|
||||
"Add Folder": "增加資料夾",
|
||||
"Add": "新增",
|
||||
"Add Device": "新增裝置",
|
||||
"Add Folder": "添加資料夾",
|
||||
"Add Remote Device": "新增遠端裝置",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "對於共用的資料夾,匯入引入者的裝置清單。",
|
||||
"Add new folder?": "新增資料夾?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "對於共享的資料夾,匯入引入者的裝置清單。",
|
||||
"Add new folder?": "新增資料夾?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整地重新掃瞄的間隔將增大(時間 60,例如:新的預設值為 1 小時)。您也可以在選擇「否」後,手動配置每個資料夾的時間間隔。",
|
||||
"Address": "位址",
|
||||
"Addresses": "位址",
|
||||
"Advanced": "進階",
|
||||
"Advanced Configuration": "進階配置",
|
||||
"All Data": "全部資料",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
|
||||
"Allow Anonymous Usage Reporting?": "允許匿名的使用資訊回報?",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "所有與此裝置分享的資料夾必須使用密碼保護起來,如此一來,沒有提供密碼將無法閱覽資料。",
|
||||
"Allow Anonymous Usage Reporting?": "允許回報匿名數據?",
|
||||
"Allowed Networks": "允許的網路",
|
||||
"Alphabetic": "字母順序",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.",
|
||||
"Anonymous Usage Reporting": "匿名的使用資訊回報",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名的使用資訊回報格式已經改變,你想要移至新格式嗎?",
|
||||
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
|
||||
"Anonymous Usage Reporting": "匿名數據回報",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名數據回報格式已經變更,想要移至新格式嗎?",
|
||||
"Are you sure you want to permanently delete all these files?": "確認永久刪除檔案?",
|
||||
"Are you sure you want to remove device {%name%}?": "確定要移除 {{name}} 裝置?",
|
||||
"Are you sure you want to remove folder {%label%}?": "確定要移除 {{label}} 資料夾?",
|
||||
"Are you sure you want to restore {%count%} files?": "確定想要還原 {{count}} 個檔案?",
|
||||
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
|
||||
"Are you sure you want to upgrade?": "確定想要更新?",
|
||||
"Auto Accept": "自動接受",
|
||||
"Automatic Crash Reporting": "Automatic Crash Reporting",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動更新目前有穩定發行版及發行候選版可供選擇。",
|
||||
"Automatic upgrades": "自動升級",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
|
||||
"Automatic Crash Reporting": "自動回傳當機報告",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動更新目前提供選項有穩定版及候選發行版。",
|
||||
"Automatic upgrades": "自動更新",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "候選發行版永遠啟用自動更新。",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "自動在預設資料夾路徑建立或分享該裝置推薦的資料夾。",
|
||||
"Available debug logging facilities:": "可用的除錯日誌工具:",
|
||||
"Be careful!": "請小心!",
|
||||
"Bugs": "程式錯誤",
|
||||
"Changelog": "更新日誌",
|
||||
"Clean out after": "於之後清空",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
"Cleanup Interval": "Cleanup Interval",
|
||||
"Cleaning Versions": "正在清除歷史版本",
|
||||
"Cleanup Interval": "清除間隔",
|
||||
"Click to see discovery failures": "點擊以查閱失敗的探索",
|
||||
"Close": "關閉",
|
||||
"Command": "指令",
|
||||
@@ -53,35 +53,39 @@
|
||||
"Connection Error": "連線錯誤",
|
||||
"Connection Type": "連線類型",
|
||||
"Connections": "連線",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing 現在能持續地監視變動了。此機制將偵測到磁碟上的變動並僅對修改過的項目發起掃描。好處是檔案的變動會傳播的更快,並且較不需要完整掃描。",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing 現在能持續地監視變動了。此機制將偵測到磁碟上的變動並僅對修改過的項目發起掃描。優點是檔案的變動將更快地傳播,並且減少完整掃描的需求。",
|
||||
"Copied from elsewhere": "從別處複製",
|
||||
"Copied from original": "從原處複製",
|
||||
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 下列貢獻者:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "建立忽略樣式,覆蓋已存在的 {{path}}。",
|
||||
"Currently Shared With Devices": "Currently Shared With Devices",
|
||||
"Currently Shared With Devices": "目前與裝置共享",
|
||||
"Danger!": "危險!",
|
||||
"Debugging Facilities": "除錯工具",
|
||||
"Default Configuration": "預設配置",
|
||||
"Default Device": "預設裝置",
|
||||
"Default Folder": "預設資料夾",
|
||||
"Default Folder Path": "預設資料夾路徑",
|
||||
"Delete Unexpected Items": "Delete Unexpected Items",
|
||||
"Defaults": "預設",
|
||||
"Delete Unexpected Items": "刪除不預期的項目",
|
||||
"Deleted": "已刪除",
|
||||
"Deselect All": "取消選取全部",
|
||||
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
|
||||
"Deselect devices to stop sharing this folder with.": "取消選擇裝置以停用與其共享資料夾。",
|
||||
"Deselect folders to stop sharing with this device.": "取消選擇資料夾以停用與此裝置共享。",
|
||||
"Device": "裝置",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "裝置 \"{{name}}\" ({{device}} 位於 {{address}}) 想要連線。要增加新裝置嗎?",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "裝置 \"{{name}}\" ({{device}} 位於 {{address}}) 想要連線。要添加新裝置嗎?",
|
||||
"Device ID": "裝置識別碼",
|
||||
"Device Identification": "裝置識別",
|
||||
"Device Name": "裝置名稱",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device is untrusted, enter encryption password": "裝置不受信任,輸入加密密碼",
|
||||
"Device rate limits": "裝置速率限制",
|
||||
"Device that last modified the item": "前次修改裝置",
|
||||
"Devices": "裝置",
|
||||
"Disable Crash Reporting": "Disable Crash Reporting",
|
||||
"Disable Crash Reporting": "停用回傳當機報告",
|
||||
"Disabled": "停用",
|
||||
"Disabled periodic scanning and disabled watching for changes": "已停用定期掃描及觀察變動",
|
||||
"Disabled periodic scanning and enabled watching for changes": "已停用定期掃描及啟用觀察變動",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "已停用定期掃描,無法設定觀察變動,每 1 分鐘重試:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "停用比較與同步檔案權限。在沒有或自定義權限的系統上很有用(例如FAT、exFAT、Synology、Android)。",
|
||||
"Discard": "忽略",
|
||||
"Disconnected": "斷線",
|
||||
"Disconnected (Unused)": "斷線(未使用)",
|
||||
@@ -96,29 +100,31 @@
|
||||
"Downloaded": "已下載",
|
||||
"Downloading": "正在下載",
|
||||
"Edit": "編輯",
|
||||
"Edit Device": "Edit Device",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Edit Device": "編輯裝置",
|
||||
"Edit Device Defaults": "編輯裝置 預設",
|
||||
"Edit Folder": "編輯資料夾",
|
||||
"Edit Folder Defaults": "編輯資料夾 預設",
|
||||
"Editing {%path%}.": "正在編輯 {{path}} 。",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable Crash Reporting": "啟用回傳當機報告",
|
||||
"Enable NAT traversal": "啟用 NAT 穿透",
|
||||
"Enable Relaying": "啟用中繼",
|
||||
"Enabled": "啟用",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "請輸入一非負數(如:\"2\\.35\")並選擇一個單位。百分比表示佔用磁碟容量的大小。",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "請輸入一非負數(如:\"2.35\")並選擇一個單位。百分比表示佔用磁碟容量的大小。",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "輸入一個非特權通訊埠號 (1024 - 65535)。",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.",
|
||||
"Enter ignore patterns, one per line.": "輸入忽略樣式,每行一種。",
|
||||
"Enter up to three octal digits.": "Enter up to three octal digits.",
|
||||
"Enter up to three octal digits.": "輸入最多三位八進位數字。",
|
||||
"Error": "錯誤",
|
||||
"External File Versioning": "外部的檔案版本控制",
|
||||
"Failed Items": "失敗的項目",
|
||||
"Failed to setup, retrying": "無法設定,正在重試",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "若沒有 IPv6 連線能力,則無法連接 IPv6 伺服器係屬正常現象。",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "若沒有 IPv6 連線能力,則無法連接 IPv6 伺服器為正常現象。",
|
||||
"File Pull Order": "提取檔案的順序",
|
||||
"File Versioning": "檔案版本控制",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "當檔案被 Syncthing 取代或刪除時,它們將被移至 .stversions 資料夾。",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "當檔案被 Syncthing 取代或刪除時,它們將被移至 .stversions 資料夾並添加日期戳記。",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "其他裝置做的改變不會影響此裝置上的檔案,但在此裝置上的變動將被發送到叢集的其他部分。",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "檔案已從叢集同步,但任何本機的變更將無法傳送至其它裝置。",
|
||||
"Filesystem Watcher Errors": "檔案系統監視器錯誤",
|
||||
"Filter by date": "以日期篩選",
|
||||
"Filter by name": "以名稱篩選",
|
||||
@@ -127,14 +133,14 @@
|
||||
"Folder Label": "資料夾標籤",
|
||||
"Folder Path": "資料夾路徑",
|
||||
"Folder Type": "資料夾類型",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "資料夾類型 \"{{receiveEncrypted}}\" 無法在新增後變更。您需要移除資料夾、刪除或解密磁碟上的資料,並再次新增資料夾。",
|
||||
"Folders": "資料夾",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.",
|
||||
"Full Rescan Interval (s)": "完全重新掃描間隔 (秒)",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "GUI 認證密碼",
|
||||
"GUI Authentication User": "GUI 使用者認證名稱",
|
||||
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
|
||||
"GUI Authentication Password": "GUI 驗證密碼",
|
||||
"GUI Authentication User": "GUI 驗證使用者名稱",
|
||||
"GUI Authentication: Set User and Password": "GUI 驗證:設定使用者名稱與密碼",
|
||||
"GUI Listen Address": "GUI 監聽位址",
|
||||
"GUI Theme": "主題",
|
||||
"General": "一般",
|
||||
@@ -144,9 +150,9 @@
|
||||
"Global State": "全域狀態",
|
||||
"Help": "說明",
|
||||
"Home page": "首頁",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.",
|
||||
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "但是,當前設定表明您可能不希望啟用它。我們為您停用了當機自動回報。",
|
||||
"If untrusted, enter encryption password": "如未受信任,請輸入加密密碼",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "如果您想防止在此電腦上的其他使用者存取 Syncthing 及其文件,請考慮設定身份驗證。",
|
||||
"Ignore": "忽略",
|
||||
"Ignore Patterns": "忽略樣式",
|
||||
"Ignore Permissions": "忽略權限",
|
||||
@@ -175,7 +181,7 @@
|
||||
"Local State (Total)": "本機狀態 (總結)",
|
||||
"Locally Changed Items": "本地變動項目",
|
||||
"Log": "日誌",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "日誌自動滾動已暫停。滾動到底部以繼續。",
|
||||
"Logs": "日誌",
|
||||
"Major Upgrade": "重大更新",
|
||||
"Mass actions": "大量操作",
|
||||
@@ -194,7 +200,7 @@
|
||||
"No File Versioning": "無檔案版本控制",
|
||||
"No files will be deleted as a result of this operation.": "此操作將不會移除您的檔案。",
|
||||
"No upgrades": "不更新",
|
||||
"Not shared": "Not shared",
|
||||
"Not shared": "未共享",
|
||||
"Notice": "注意",
|
||||
"OK": "確定",
|
||||
"Off": "關閉",
|
||||
@@ -208,7 +214,7 @@
|
||||
"Path": "路徑",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "資料夾在本機的路徑。若資料夾不存在則會建立。波浪符號 (~) 可用作下列資料夾的捷徑:",
|
||||
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "新自動接受的資料夾將會建立在此路徑下,從 UI 加入資料夾時也將預設推薦此路徑。波浪符字元 (~) 將被展開為 {{tilde}}。",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "儲存歷史版本的路徑(若為空,則預設使用資料夾中的 .stversions 資料夾。)",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "儲存歷史版本的路徑(共享資料夾中的預設 .stversions 目錄則留白)。",
|
||||
"Pause": "暫停",
|
||||
"Pause All": "全部暫停",
|
||||
"Paused": "暫停",
|
||||
@@ -218,25 +224,25 @@
|
||||
"Periodic scanning at given interval and enabled watching for changes": "在一定的時間間隔,定期掃描及啟用觀察變動",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "在一定的時間間隔,定期掃描,無法設定觀察變動,每 1 分鐘重試:",
|
||||
"Permissions": "權限",
|
||||
"Please consult the release notes before performing a major upgrade.": "執行重大升級前請先參閱版本資訊。",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "請在設定對話框內設置 GUI 使用者認證名稱及密碼。",
|
||||
"Please consult the release notes before performing a major upgrade.": "執行重大更新前請先參閱版本資訊。",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "請在設定對話框內設置 GUI 驗證使用者名稱及密碼。",
|
||||
"Please wait": "請稍候",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "前綴表示當此檔案阻礙了資料夾刪除時,可一併刪除此檔",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "前綴表示此樣式不區分大小寫",
|
||||
"Preparing to Sync": "Preparing to Sync",
|
||||
"Preparing to Sync": "正在準備同步",
|
||||
"Preview": "預覽",
|
||||
"Preview Usage Report": "預覽使用資訊報告",
|
||||
"Preview Usage Report": "預覽數據報告",
|
||||
"Quick guide to supported patterns": "可支援樣式的快速指南",
|
||||
"Random": "隨機",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
"Receive Encrypted": "接收已加密",
|
||||
"Receive Only": "僅接收",
|
||||
"Received data is already encrypted": "Received data is already encrypted",
|
||||
"Received data is already encrypted": "接收到的數據已經加密",
|
||||
"Recent Changes": "最近變動",
|
||||
"Reduced by ignore patterns": "已由忽略樣式縮減",
|
||||
"Release Notes": "版本資訊",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "發行候選版包含最新的功能及修補。與傳統 Syncthing 雙週發行版相似。",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "候選發行版包含最新的功能及修補。與傳統 Syncthing 雙週發行版相似。",
|
||||
"Remote Devices": "遠端裝置",
|
||||
"Remote GUI": "Remote GUI",
|
||||
"Remote GUI": "遠端 GUI",
|
||||
"Remove": "移除",
|
||||
"Remove Device": "移除裝置",
|
||||
"Remove Folder": "移除資料夾",
|
||||
@@ -252,28 +258,28 @@
|
||||
"Resume": "繼續",
|
||||
"Resume All": "全部繼續",
|
||||
"Reused": "重用",
|
||||
"Revert Local Changes": "Revert Local Changes",
|
||||
"Revert Local Changes": "撤銷本機變更",
|
||||
"Save": "儲存",
|
||||
"Scan Time Remaining": "剩餘掃描時間",
|
||||
"Scanning": "正在掃描",
|
||||
"See external versioning help for supported templated command line parameters.": "查看關於命令列模板參數請參閱外部版本管理說明。",
|
||||
"Select All": "Select All",
|
||||
"Select All": "選擇全部",
|
||||
"Select a version": "選擇一個版本",
|
||||
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
|
||||
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
|
||||
"Select additional devices to share this folder with.": "選擇額外裝置與其共享此資料夾。",
|
||||
"Select additional folders to share with this device.": "選擇額外資料夾以共享至此裝置。",
|
||||
"Select latest version": "選擇最新的版本",
|
||||
"Select oldest version": "選擇最舊的版本",
|
||||
"Select the folders to share with this device.": "選擇要共享這個資料夾的裝置。",
|
||||
"Send & Receive": "傳送及接收",
|
||||
"Send Only": "僅傳送",
|
||||
"Settings": "設定",
|
||||
"Share": "分享",
|
||||
"Share Folder": "分享資料夾",
|
||||
"Share": "共享",
|
||||
"Share Folder": "共享資料夾",
|
||||
"Share Folders With Device": "與裝置共享資料夾",
|
||||
"Share this folder?": "分享此資料夾?",
|
||||
"Shared Folders": "Shared Folders",
|
||||
"Share this folder?": "共享此資料夾?",
|
||||
"Shared Folders": "已共享的資料夾",
|
||||
"Shared With": "與誰共享",
|
||||
"Sharing": "Sharing",
|
||||
"Sharing": "正在共享",
|
||||
"Show ID": "顯示識別碼",
|
||||
"Show QR": "顯示 QR 碼",
|
||||
"Show diff with previous version": "顯示與前一個版本的差異",
|
||||
@@ -287,8 +293,8 @@
|
||||
"Smallest First": "最小的優先",
|
||||
"Some items could not be restored:": "有些項目無法被還原:",
|
||||
"Source Code": "原始碼",
|
||||
"Stable releases and release candidates": "穩定發行版及發行候選版",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "穩定發行版大約延遲兩週發佈。這段期間將作為發行候選版來測試。",
|
||||
"Stable releases and release candidates": "穩定版及候選發行版",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "穩定版大約延遲兩週發佈。這段期間將作為候選發行版來測試。",
|
||||
"Stable releases only": "僅穩定發行版",
|
||||
"Staggered File Versioning": "變動式檔案版本控制",
|
||||
"Start Browser": "啟動瀏覽器",
|
||||
@@ -296,37 +302,37 @@
|
||||
"Stopped": "已停止",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Support": "支援",
|
||||
"Support Bundle": "Support Bundle",
|
||||
"Support Bundle": "支援包",
|
||||
"Sync Protocol Listen Addresses": "同步通訊協定監聽位址",
|
||||
"Syncing": "正在同步",
|
||||
"Syncthing has been shut down.": "Syncthing 已經關閉。",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing 包括以下軟體或其中的一部分:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as MPL v2.0.",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 為自由且開源授權條款為 MPL v2.0。",
|
||||
"Syncthing is restarting.": "Syncthing 正在重新啟動。",
|
||||
"Syncthing is upgrading.": "Syncthing 正在進行升級。",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 已支援將當機報告回傳至開發者。此功能預設為啟用。",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing 似乎離線了,或者您的網際網路連線出現問題。正在重試...",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing 在處理您的請求時似乎遇到了問題。請重新整理本頁面,若問題持續發生,請重新啟動 Syncthing。",
|
||||
"Take me back": "Take me back",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
|
||||
"The Syncthing Authors": "The Syncthing Authors",
|
||||
"The Syncthing Authors": "Syncthing 作者們",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthing 管理介面被設定允許無密碼的遠端存取。",
|
||||
"The aggregated statistics are publicly available at the URL below.": "匯總統計資訊可於下方網址取得。",
|
||||
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
|
||||
"The cleanup interval cannot be blank.": "清除間隔不能為空白。",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "組態已經儲存但尚未啟用。Syncthing 必須重新啟動以便啟用新的組態。",
|
||||
"The device ID cannot be blank.": "裝置識別碼不能為空白。",
|
||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "其它裝置的裝置識別碼可在它們的 \"操作 > 顯示識別碼\" 對話框找到。空白及連接符號可省略。",
|
||||
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "經過加密的使用資訊報告會每天傳送。報告是用來追蹤常用的平台、資料夾大小以及應用程式版本。若傳送的資料集有異動,您會再次看到這個對話框。",
|
||||
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "經過加密的數據報告將每日傳送。報告是用來追蹤常用的平台、資料夾大小以及應用程式版本。若傳送的資料集有異動,您將再次看到此對話框。",
|
||||
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "輸入的裝置識別碼似乎無效。它應該為一串長度為 52 或 56 個字元長的半形英文字母及數字,並可能會含有額外的空白或連接符號。",
|
||||
"The folder ID cannot be blank.": "資料夾識別碼不能為空白。",
|
||||
"The folder ID must be unique.": "資料夾識別碼必須為獨一無二的。",
|
||||
"The folder path cannot be blank.": "資料夾路徑不能空白。",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "使用下列的間隔:在第一個小時內每 30 秒保留一個版本,在第一天內每小時保留一個版本,在第 30 天內每一天保留一個版本,在達到最長保留時間前每一星期保留一個版本。",
|
||||
"The following items could not be synchronized.": "無法同步以下項目。",
|
||||
"The following items were changed locally.": "The following items were changed locally.",
|
||||
"The following unexpected items were found.": "The following unexpected items were found.",
|
||||
"The interval must be a positive number of seconds.": "The interval must be a positive number of seconds.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
|
||||
"The following items were changed locally.": "以下項目在本機進行了變更。",
|
||||
"The following unexpected items were found.": "找到以下不預期項目。",
|
||||
"The interval must be a positive number of seconds.": "間隔秒數必須為正數。",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "間隔,以秒為單位,執行清除歷史版本目錄。如欲停用週期清除,設 0 。",
|
||||
"The maximum age must be a number and cannot be blank.": "最長保留時間必須為一個數字且不得為空。",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "一個版本被保留的最長時間 (單位為天,若設定為 0 則表示永遠保留)。",
|
||||
"The number of days must be a number and cannot be blank.": "天數必須必須為一個數字且不得為空。",
|
||||
@@ -336,8 +342,8 @@
|
||||
"The path cannot be blank.": "路徑不能空白。",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "限制速率必須為非負的數字 (0: 不設限制)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "重新掃描間隔必須為一個非負數的秒數。",
|
||||
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
|
||||
"There are no folders to share with this device.": "There are no folders to share with this device.",
|
||||
"There are no devices to share this folder with.": "沒有裝置可以共享此資料夾。",
|
||||
"There are no folders to share with this device.": "沒有資料夾分享給此裝置。",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "解決問題後,將會自動重試和同步。",
|
||||
"This Device": "本機",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "這能給駭客輕易的來讀取、變更電腦中的任何檔案。",
|
||||
@@ -347,35 +353,35 @@
|
||||
"Time the item was last modified": "前次修改時間",
|
||||
"Trash Can File Versioning": "垃圾筒式檔案版本控制",
|
||||
"Type": "類型",
|
||||
"UNIX Permissions": "UNIX Permissions",
|
||||
"UNIX Permissions": "UNIX 權限",
|
||||
"Unavailable": "無法使用",
|
||||
"Unavailable/Disabled by administrator or maintainer": "無法使用 / 被系統管理員或維護者停用",
|
||||
"Undecided (will prompt)": "未決定(將會提示)",
|
||||
"Unexpected Items": "Unexpected Items",
|
||||
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
|
||||
"Unignore": "Unignore",
|
||||
"Unexpected Items": "不預期的項目",
|
||||
"Unexpected items have been found in this folder.": "在此資料夾中發現不預期項目。",
|
||||
"Unignore": "未忽略",
|
||||
"Unknown": "未知",
|
||||
"Unshared": "未共享",
|
||||
"Unshared Devices": "Unshared Devices",
|
||||
"Unshared Folders": "Unshared Folders",
|
||||
"Untrusted": "Untrusted",
|
||||
"Unshared Devices": "未共享的裝置",
|
||||
"Unshared Folders": "未共享的資料夾",
|
||||
"Untrusted": "不信任",
|
||||
"Up to Date": "最新",
|
||||
"Updated": "已更新",
|
||||
"Upgrade": "升級",
|
||||
"Upgrade To {%version%}": "升級至 {{version}}",
|
||||
"Upgrade": "更新",
|
||||
"Upgrade To {%version%}": "更新至 {{version}}",
|
||||
"Upgrading": "正在升級",
|
||||
"Upload Rate": "上載速率",
|
||||
"Uptime": "上線時間",
|
||||
"Usage reporting is always enabled for candidate releases.": "發行候選版永遠啟用使用數據回報。",
|
||||
"Usage reporting is always enabled for candidate releases.": "候選發行版永遠啟用使用數據回報。",
|
||||
"Use HTTPS for GUI": "為 GUI 使用 HTTPS",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "尚未設定GUI 驗證的使用者名稱/密碼。請考慮進行設定。",
|
||||
"Version": "版本",
|
||||
"Versions": "版本",
|
||||
"Versions Path": "歷史版本路徑",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "當檔案歷史版本的存留時間大於設定的最大值,或是其數量在一段時間內超出允許值時,則會被刪除。",
|
||||
"Waiting to Clean": "Waiting to Clean",
|
||||
"Waiting to Scan": "Waiting to Scan",
|
||||
"Waiting to Sync": "Waiting to Sync",
|
||||
"Waiting to Clean": "正在等待清除",
|
||||
"Waiting to Scan": "正在等待掃描",
|
||||
"Waiting to Sync": "正在等待同步",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "警告,此路徑是現存資料夾 \"{{otherFolder}}\" 的上級目錄。",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告,此路徑是現存資料夾 \"{{otherFolderLabel}}\" ({{otherFolder}}) 的上級目錄。",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告,此路徑是現存資料夾 \"{{otherFolder}}\" 的下級目錄。",
|
||||
@@ -383,7 +389,7 @@
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "警告:如果您正在使用外部監視工具,如 {{syncthingInotify}},您應該確認已經將其關閉。",
|
||||
"Watch for Changes": "監視變動",
|
||||
"Watching for Changes": "正在監視變動",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "監視變動會發現大多數變更,而無需定期掃描。",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "當新增一個裝置時,務必記住,當前的這個裝置也同樣必須被添加至另一邊。",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "當新增一個資料夾時,請記住,資料夾識別碼是用來將裝置之間的資料夾綁定在一起的。它們有區分大小寫,且必須在所有裝置之間完全相同。",
|
||||
"Yes": "是",
|
||||
@@ -392,15 +398,16 @@
|
||||
"You can read more about the two release channels at the link below.": "您可於下方連結閱讀更多關於發行頻道的說明。",
|
||||
"You have no ignored devices.": "您沒有已忽略的裝置。\n",
|
||||
"You have no ignored folders.": "您沒有已忽略的資料夾。\n",
|
||||
"You have unsaved changes. Do you really want to discard them?": "You have unsaved changes. Do you really want to discard them?",
|
||||
"You have unsaved changes. Do you really want to discard them?": "您有未儲存的變更。確認棄用嗎?",
|
||||
"You must keep at least one version.": "您必須保留至少一個版本。",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "您不應該在 \"{{receiveEncrypted}}\" 資料夾中新增或變更任何內容。",
|
||||
"days": "日",
|
||||
"directories": "directories",
|
||||
"directories": "目錄",
|
||||
"files": "個檔案",
|
||||
"full documentation": "完整說明文件",
|
||||
"items": "個項目",
|
||||
"seconds": "seconds",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想要分享資料夾 \"{{folder}}\"。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要分享資料夾 \"{{folderlabel}}\" ({{folder}})。"
|
||||
"seconds": "秒",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想要共享資料夾 \"{{folder}}\"。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享資料夾 \"{{folderlabel}}\" ({{folder}})。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} 可能會重新引入此裝置。"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-AU":"English (Australia)","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fi":"Finnish","fr":"French","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-TW":"Chinese (Taiwan)"}
|
||||
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-AU":"English (Australia)","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fi":"Finnish","fr":"French","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-HK":"Chinese (Hong Kong)","zh-TW":"Chinese (Taiwan)"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-AU","en-GB","eo","es","es-ES","eu","fi","fr","fy","hu","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ru","sk","sv","tr","uk","zh-CN","zh-TW"]
|
||||
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-AU","en-GB","eo","es","es-ES","eu","fi","fr","fy","hu","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ru","sk","sv","tr","uk","zh-CN","zh-HK","zh-TW"]
|
||||
|
||||
@@ -548,13 +548,13 @@
|
||||
<button ng-if="folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, false)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type">
|
||||
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type && folder.versioning.type != 'external'">
|
||||
<span class="fas fa-undo"></span> <span translate>Versions</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-disabled="['idle', 'stopped', 'unshared', 'outofsync', 'faileditems', 'localadditions'].indexOf(folderStatus(folder)) < 0">
|
||||
<span class="fas fa-refresh"></span> <span translate>Rescan</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editFolder(folder)">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editFolderExisting(folder)">
|
||||
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
||||
</button>
|
||||
</span>
|
||||
@@ -570,7 +570,7 @@
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="setAllFoldersPause(false)" ng-if="isAtleastOneFolderPausedStateSetTo(true)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume All</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanAllFolders()">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanAllFolders()" ng-if="folderList().length > 0" ng-disabled="!isAtleastOneFolderPausedStateSetTo(false)">
|
||||
<span class="fas fa-refresh"></span> <span translate>Rescan All</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="addFolder()">
|
||||
@@ -708,6 +708,11 @@
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
||||
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
|
||||
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm:ss"}}</td>
|
||||
</tr>
|
||||
<tr ng-if="connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-cloud-download-alt"></span> <span translate>Download Rate</span></th>
|
||||
<td class="text-right">
|
||||
@@ -793,11 +798,6 @@
|
||||
<th><span class="fas fa-fw fa-tag"></span> <span translate>Version</span></th>
|
||||
<td class="text-right">{{connections[deviceCfg.deviceID].clientVersion}}</td>
|
||||
</tr>
|
||||
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
||||
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
|
||||
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm:ss"}}</td>
|
||||
</tr>
|
||||
<tr ng-if="deviceFolders(deviceCfg).length > 0">
|
||||
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folders</span></th>
|
||||
<td class="text-right" ng-attr-title="{{deviceFolders(deviceCfg).map(folderLabel).join(', ')}}">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
|
||||
@@ -821,7 +821,7 @@
|
||||
<button ng-if="deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, false)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editDevice(deviceCfg)">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editDeviceExisting(deviceCfg)">
|
||||
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<h4 class="text-center" translate>The Syncthing Authors</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tomasz Wilczyński, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, mv1005, otbutz, perewa, rubenbe, wangguoliang, xarx00, xjtdy888, 佛跳墙
|
||||
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, mv1005, otbutz, perewa, rubenbe, wangguoliang, xarx00, xjtdy888, 佛跳墙
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -60,12 +60,14 @@ angular.module('syncthing.core')
|
||||
DEVICE_CONNECTED: 'DeviceConnected', // Generated each time a connection to a device has been established
|
||||
DEVICE_DISCONNECTED: 'DeviceDisconnected', // Generated each time a connection to a device has been terminated
|
||||
DEVICE_DISCOVERED: 'DeviceDiscovered', // Emitted when a new device is discovered using local discovery
|
||||
DEVICE_REJECTED: 'DeviceRejected', // Emitted when there is a connection from a device we are not configured to talk to
|
||||
DEVICE_REJECTED: 'DeviceRejected', // DEPRECATED: Emitted when there is a connection from a device we are not configured to talk to
|
||||
PENDING_DEVICES_CHANGED: 'PendingDevicesChanged', // Emitted when pending devices were added / updated (connection from unknown ID) or removed (device is ignored or added)
|
||||
DEVICE_PAUSED: 'DevicePaused', // Emitted when a device has been paused
|
||||
DEVICE_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed
|
||||
DOWNLOAD_PROGRESS: 'DownloadProgress', // Emitted during file downloads for each folder for each file
|
||||
FOLDER_COMPLETION: 'FolderCompletion', //Emitted when the local or remote contents for a folder changes
|
||||
FOLDER_REJECTED: 'FolderRejected', // Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question
|
||||
FOLDER_REJECTED: 'FolderRejected', // DEPRECATED: Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question
|
||||
PENDING_FOLDERS_CHANGED: 'PendingFoldersChanged', // Emitted when pending folders were added / updated (offered by some device, but not shared to them) or removed (folder ignored or added or no longer offered from the remote device)
|
||||
FOLDER_SUMMARY: 'FolderSummary', // Emitted when folder contents have changed locally
|
||||
ITEM_FINISHED: 'ItemFinished', // Generated when Syncthing ends synchronizing a file to a newer version
|
||||
ITEM_STARTED: 'ItemStarted', // Generated when Syncthing begins synchronizing a file to a newer version
|
||||
|
||||
@@ -52,6 +52,7 @@ angular.module('syncthing.core')
|
||||
$scope.metricRates = false;
|
||||
$scope.folderPathErrors = {};
|
||||
$scope.currentFolder = {};
|
||||
$scope.currentDevice = {};
|
||||
$scope.ignores = {
|
||||
text: '',
|
||||
error: null,
|
||||
@@ -63,26 +64,14 @@ angular.module('syncthing.core')
|
||||
$scope.metricRates = (window.localStorage["metricRates"] == "true");
|
||||
} catch (exception) { }
|
||||
|
||||
$scope.folderDefaults = {
|
||||
devices: [],
|
||||
type: "sendreceive",
|
||||
rescanIntervalS: 3600,
|
||||
fsWatcherDelayS: 10,
|
||||
fsWatcherEnabled: true,
|
||||
minDiskFree: { value: 1, unit: "%" },
|
||||
maxConflicts: 10,
|
||||
fsync: true,
|
||||
order: "random",
|
||||
fileVersioningSelector: "none",
|
||||
$scope.versioningDefaults = {
|
||||
selector: "none",
|
||||
trashcanClean: 0,
|
||||
versioningCleanupIntervalS: 3600,
|
||||
cleanupIntervalS: 3600,
|
||||
simpleKeep: 5,
|
||||
staggeredMaxAge: 365,
|
||||
staggeredCleanInterval: 3600,
|
||||
staggeredVersionsPath: "",
|
||||
externalCommand: "",
|
||||
autoNormalize: true,
|
||||
path: "",
|
||||
};
|
||||
|
||||
$scope.localStateTotal = {
|
||||
@@ -122,6 +111,7 @@ angular.module('syncthing.core')
|
||||
refreshSystem();
|
||||
refreshDiscoveryCache();
|
||||
refreshConfig();
|
||||
refreshCluster();
|
||||
refreshConnectionStats();
|
||||
refreshDeviceStats();
|
||||
refreshFolderStats();
|
||||
@@ -222,6 +212,9 @@ angular.module('syncthing.core')
|
||||
});
|
||||
|
||||
$scope.$on(Events.DEVICE_DISCONNECTED, function (event, arg) {
|
||||
if (!$scope.connections[arg.data.id]) {
|
||||
return;
|
||||
}
|
||||
$scope.connections[arg.data.id].connected = false;
|
||||
refreshDeviceStats();
|
||||
});
|
||||
@@ -244,32 +237,69 @@ angular.module('syncthing.core')
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(Events.DEVICE_REJECTED, function (event, arg) {
|
||||
var pendingDevice = {
|
||||
time: arg.time,
|
||||
name: arg.data.name,
|
||||
address: arg.data.address
|
||||
};
|
||||
console.log("rejected device:", arg.data.device, pendingDevice);
|
||||
$scope.$on(Events.PENDING_DEVICES_CHANGED, function (event, arg) {
|
||||
if (!(arg.data.added || arg.data.removed)) {
|
||||
// Not enough information to update in place, just refresh it completely
|
||||
refreshCluster();
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.pendingDevices[arg.data.device] = pendingDevice;
|
||||
if (arg.data.added) {
|
||||
arg.data.added.forEach(function (rejected) {
|
||||
var pendingDevice = {
|
||||
time: arg.time,
|
||||
name: rejected.name,
|
||||
address: rejected.address
|
||||
};
|
||||
console.log("rejected device:", rejected.deviceID, pendingDevice);
|
||||
$scope.pendingDevices[rejected.deviceID] = pendingDevice;
|
||||
});
|
||||
}
|
||||
|
||||
if (arg.data.removed) {
|
||||
arg.data.removed.forEach(function (dev) {
|
||||
console.log("no longer pending device:", dev.deviceID);
|
||||
delete $scope.pendingDevices[dev.deviceID];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
|
||||
var offeringDevice = {
|
||||
time: arg.time,
|
||||
label: arg.data.folderLabel
|
||||
};
|
||||
console.log("rejected folder", arg.data.folder, "from device:", arg.data.device, offeringDevice);
|
||||
|
||||
var pendingFolder = $scope.pendingFolders[arg.data.folder];
|
||||
if (pendingFolder === undefined) {
|
||||
pendingFolder = {
|
||||
offeredBy: {}
|
||||
};
|
||||
$scope.$on(Events.PENDING_FOLDERS_CHANGED, function (event, arg) {
|
||||
if (!(arg.data.added || arg.data.removed)) {
|
||||
// Not enough information to update in place, just refresh it completely
|
||||
refreshCluster();
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg.data.added) {
|
||||
arg.data.added.forEach(function (rejected) {
|
||||
var offeringDevice = {
|
||||
time: arg.time,
|
||||
label: rejected.folderLabel
|
||||
};
|
||||
console.log("rejected folder", rejected.folderID, "from device:", rejected.deviceID, offeringDevice);
|
||||
|
||||
var pendingFolder = $scope.pendingFolders[rejected.folderID];
|
||||
if (pendingFolder === undefined) {
|
||||
pendingFolder = {
|
||||
offeredBy: {}
|
||||
};
|
||||
}
|
||||
pendingFolder.offeredBy[rejected.deviceID] = offeringDevice;
|
||||
$scope.pendingFolders[rejected.folderID] = pendingFolder;
|
||||
});
|
||||
}
|
||||
|
||||
if (arg.data.removed) {
|
||||
arg.data.removed.forEach(function (folderDev) {
|
||||
console.log("no longer pending folder", folderDev.folderID, "from device:", folderDev.deviceID);
|
||||
if (folderDev.deviceID === undefined) {
|
||||
delete $scope.pendingFolders[folderDev.folderID];
|
||||
} else if ($scope.pendingFolders[folderDev.folderID]) {
|
||||
delete $scope.pendingFolders[folderDev.folderID].offeredBy[folderDev.deviceID];
|
||||
}
|
||||
});
|
||||
}
|
||||
pendingFolder.offeredBy[arg.data.device] = offeringDevice;
|
||||
$scope.pendingFolders[arg.data.folder] = pendingFolder;
|
||||
});
|
||||
|
||||
$scope.$on('ConfigLoaded', function () {
|
||||
@@ -421,7 +451,6 @@ angular.module('syncthing.core')
|
||||
});
|
||||
});
|
||||
|
||||
refreshCluster();
|
||||
refreshNoAuthWarning();
|
||||
setDefaultTheme();
|
||||
|
||||
@@ -690,7 +719,7 @@ angular.module('syncthing.core')
|
||||
}
|
||||
|
||||
function shouldSetDefaultFolderPath() {
|
||||
return $scope.config.options && $scope.config.options.defaultFolderPath && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine
|
||||
return $scope.config.defaults.folder.path && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine && !$scope.editingDefaults;
|
||||
}
|
||||
|
||||
function resetRemoteNeed() {
|
||||
@@ -732,8 +761,23 @@ angular.module('syncthing.core')
|
||||
$scope.currentSharing.shared = [];
|
||||
$scope.currentSharing.unrelated = [];
|
||||
$scope.currentSharing.selected = {};
|
||||
if (editing === 'folder') {
|
||||
initShareEditingFolder();
|
||||
}
|
||||
};
|
||||
|
||||
function initShareEditingFolder() {
|
||||
$scope.currentFolder.devices.forEach(function (n) {
|
||||
if (n.deviceID !== $scope.myID) {
|
||||
$scope.currentSharing.shared.push($scope.devices[n.deviceID]);
|
||||
}
|
||||
$scope.currentSharing.selected[n.deviceID] = true;
|
||||
});
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID && !$scope.currentSharing.selected[n.deviceID];
|
||||
});
|
||||
}
|
||||
|
||||
$scope.refreshFailed = function (page, perpage) {
|
||||
if (!$scope.failed || !$scope.failed.folder) {
|
||||
return;
|
||||
@@ -1198,6 +1242,7 @@ angular.module('syncthing.core')
|
||||
}).error($scope.emitHTTPError);
|
||||
},
|
||||
show: function () {
|
||||
$scope.logging.paused = false;
|
||||
$scope.logging.refreshFacilities();
|
||||
$scope.logging.timer = $timeout($scope.logging.fetch);
|
||||
var textArea = $('#logViewerText');
|
||||
@@ -1455,9 +1500,40 @@ angular.module('syncthing.core')
|
||||
$scope.configInSync = true;
|
||||
};
|
||||
|
||||
$scope.editDevice = function (deviceCfg) {
|
||||
function editDeviceModal() {
|
||||
$scope.currentDevice._addressesStr = $scope.currentDevice.addresses.join(', ');
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
}
|
||||
|
||||
$scope.editDeviceModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
return $translate.instant("Edit Device Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
title += $translate.instant("Edit Device");
|
||||
} else {
|
||||
title += $translate.instant("Add Device");
|
||||
}
|
||||
var name = $scope.deviceName($scope.currentDevice);
|
||||
if (name !== '') {
|
||||
title += ' (' + name + ')';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
$scope.editDeviceModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-desktop';
|
||||
};
|
||||
|
||||
$scope.editDeviceExisting = function (deviceCfg) {
|
||||
$scope.currentDevice = $.extend({}, deviceCfg);
|
||||
$scope.editingExisting = true;
|
||||
$scope.editingDefaults = false;
|
||||
$scope.willBeReintroducedBy = undefined;
|
||||
if (deviceCfg.introducedBy) {
|
||||
var introducerDevice = $scope.devices[deviceCfg.introducedBy];
|
||||
@@ -1465,7 +1541,6 @@ angular.module('syncthing.core')
|
||||
$scope.willBeReintroducedBy = $scope.deviceName(introducerDevice);
|
||||
}
|
||||
}
|
||||
$scope.currentDevice._addressesStr = deviceCfg.addresses.join(', ');
|
||||
initShareEditing('device');
|
||||
$scope.deviceFolders($scope.currentDevice).forEach(function (folderID) {
|
||||
$scope.currentSharing.shared.push($scope.folders[folderID]);
|
||||
@@ -1474,8 +1549,15 @@ angular.module('syncthing.core')
|
||||
$scope.currentSharing.unrelated = $scope.folderList().filter(function (n) {
|
||||
return !$scope.currentSharing.selected[n.id];
|
||||
});
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
editDeviceModal();
|
||||
};
|
||||
|
||||
$scope.editDeviceDefaults = function () {
|
||||
$http.get(urlbase + '/config/defaults/device').then(function (p) {
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.editingDefaults = true;
|
||||
editDeviceModal();
|
||||
}, $scope.emitHTTPError);
|
||||
};
|
||||
|
||||
$scope.selectAllSharedFolders = function (state) {
|
||||
@@ -1507,19 +1589,16 @@ angular.module('syncthing.core')
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
$scope.currentDevice = {
|
||||
name: name,
|
||||
deviceID: deviceID,
|
||||
_addressesStr: 'dynamic',
|
||||
compression: 'metadata',
|
||||
introducer: false,
|
||||
ignoredFolders: []
|
||||
};
|
||||
$scope.editingExisting = false;
|
||||
initShareEditing('device');
|
||||
$scope.currentSharing.unrelated = $scope.folderList();
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
$http.get(urlbase + '/config/defaults/device').then(function (p) {
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.currentDevice.name = name;
|
||||
$scope.currentDevice.deviceID = deviceID;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
initShareEditing('device');
|
||||
$scope.currentSharing.unrelated = $scope.folderList();
|
||||
editDeviceModal();
|
||||
}, $scope.emitHTTPError);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1544,22 +1623,30 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.saveDevice = function () {
|
||||
$('#editDevice').modal('hide');
|
||||
$scope.saveDeviceConfig($scope.currentDevice);
|
||||
};
|
||||
|
||||
$scope.saveDeviceConfig = function (deviceCfg) {
|
||||
deviceCfg.addresses = deviceCfg._addressesStr.split(',').map(function (x) {
|
||||
$scope.currentDevice.addresses = $scope.currentDevice._addressesStr.split(',').map(function (x) {
|
||||
return x.trim();
|
||||
});
|
||||
delete $scope.currentDevice._addressesStr;
|
||||
if ($scope.editingDefaults) {
|
||||
$scope.config.defaults.device = $scope.currentDevice;
|
||||
} else {
|
||||
setDeviceConfig();
|
||||
}
|
||||
delete $scope.currentSharing;
|
||||
delete $scope.currentDevice;
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
$scope.devices[deviceCfg.deviceID] = deviceCfg;
|
||||
function setDeviceConfig() {
|
||||
var currentID = $scope.currentDevice.deviceID;
|
||||
$scope.devices[currentID] = $scope.currentDevice;
|
||||
$scope.config.devices = deviceList($scope.devices);
|
||||
|
||||
for (var id in $scope.currentSharing.selected) {
|
||||
if ($scope.currentSharing.selected[id]) {
|
||||
var found = false;
|
||||
for (i = 0; i < $scope.folders[id].devices.length; i++) {
|
||||
if ($scope.folders[id].devices[i].deviceID === deviceCfg.deviceID) {
|
||||
if ($scope.folders[id].devices[i].deviceID === currentID) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -1568,21 +1655,18 @@ angular.module('syncthing.core')
|
||||
if (!found) {
|
||||
// Add device to folder
|
||||
$scope.folders[id].devices.push({
|
||||
deviceID: deviceCfg.deviceID
|
||||
deviceID: currentID,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Remove device from folder
|
||||
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
||||
return n.deviceID !== deviceCfg.deviceID;
|
||||
return n.deviceID !== currentID;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delete $scope.currentSharing;
|
||||
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
$scope.ignoreDevice = function (deviceID, pendingDevice) {
|
||||
@@ -1719,14 +1803,14 @@ angular.module('syncthing.core')
|
||||
if (!newvalue || !shouldSetDefaultFolderPath()) {
|
||||
return;
|
||||
}
|
||||
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
||||
$scope.currentFolder.path = pathJoin($scope.config.defaults.folder.path, newvalue);
|
||||
});
|
||||
|
||||
$scope.$watch('currentFolder.id', function (newvalue) {
|
||||
if (!newvalue || !shouldSetDefaultFolderPath() || $scope.currentFolder.label) {
|
||||
return;
|
||||
}
|
||||
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
||||
$scope.currentFolder.path = pathJoin($scope.config.defaults.folder.path, newvalue);
|
||||
});
|
||||
|
||||
$scope.fsWatcherToggled = function () {
|
||||
@@ -1753,7 +1837,8 @@ angular.module('syncthing.core')
|
||||
$('#globalChanges').modal();
|
||||
};
|
||||
|
||||
$scope.editFolderModal = function () {
|
||||
function editFolderModal() {
|
||||
initVersioningEditing();
|
||||
$scope.folderPathErrors = {};
|
||||
$scope.folderEditor.$setPristine();
|
||||
$('#editFolder').modal().one('shown.bs.tab', function (e) {
|
||||
@@ -1766,64 +1851,81 @@ angular.module('syncthing.core')
|
||||
});
|
||||
};
|
||||
|
||||
$scope.editFolder = function (folderCfg) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.currentFolder = angular.copy(folderCfg);
|
||||
$scope.editFolderModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
return $translate.instant("Edit Folder Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
title += $translate.instant("Edit Folder");
|
||||
} else {
|
||||
title += $translate.instant("Add Folder");
|
||||
}
|
||||
if ($scope.currentFolder.id !== '') {
|
||||
title += ' (' + $scope.folderLabel($scope.currentFolder.id) + ')';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
$scope.editFolderModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-folder';
|
||||
};
|
||||
|
||||
function editFolder() {
|
||||
if ($scope.currentFolder.path.length > 1 && $scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
|
||||
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
|
||||
} else if (!$scope.currentFolder.path) {
|
||||
// undefined path leads to invalid input field
|
||||
$scope.currentFolder.path = '';
|
||||
}
|
||||
// Cache complete device objects indexed by ID for lookups
|
||||
initShareEditing('folder');
|
||||
$scope.currentFolder.devices.forEach(function (n) {
|
||||
if (n.deviceID !== $scope.myID) {
|
||||
$scope.currentSharing.shared.push($scope.devices[n.deviceID]);
|
||||
}
|
||||
$scope.currentSharing.selected[n.deviceID] = true;
|
||||
});
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID && !$scope.currentSharing.selected[n.deviceID];
|
||||
});
|
||||
if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "trashcan") {
|
||||
$scope.currentFolder.trashcanFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "trashcan";
|
||||
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "simple") {
|
||||
$scope.currentFolder.simpleFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "simple";
|
||||
$scope.currentFolder.simpleKeep = +$scope.currentFolder.versioning.params.keep;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "staggered") {
|
||||
$scope.currentFolder.staggeredFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "staggered";
|
||||
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.versioning.params.maxAge / 86400);
|
||||
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.versioning.params.cleanInterval;
|
||||
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.versioning.params.versionsPath;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "external") {
|
||||
$scope.currentFolder.externalFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "external";
|
||||
$scope.currentFolder.externalCommand = $scope.currentFolder.versioning.params.command;
|
||||
} else {
|
||||
$scope.currentFolder.fileVersioningSelector = "none";
|
||||
editFolderModal();
|
||||
}
|
||||
|
||||
$scope.internalVersioningEnabled = function(guiVersioning) {
|
||||
if (!$scope.currentFolder._guiVersioning) {
|
||||
return false;
|
||||
}
|
||||
$scope.currentFolder.trashcanClean = $scope.currentFolder.trashcanClean || 0; // weeds out nulls and undefineds
|
||||
$scope.currentFolder.simpleKeep = $scope.currentFolder.simpleKeep || 5;
|
||||
$scope.currentFolder.staggeredCleanInterval = $scope.currentFolder.staggeredCleanInterval || 3600;
|
||||
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.staggeredVersionsPath || "";
|
||||
// Zero is a valid, non-default value (disabled)
|
||||
if ($scope.currentFolder.versioningCleanupIntervalS !== 0) {
|
||||
$scope.currentFolder.versioningCleanupIntervalS = $scope.currentFolder.versioningCleanupIntervalS || 3600;
|
||||
return ['none', 'external'].indexOf($scope.currentFolder._guiVersioning.selector) === -1;
|
||||
};
|
||||
|
||||
function initVersioningEditing() {
|
||||
$scope.currentFolder._guiVersioning = angular.copy($scope.versioningDefaults);
|
||||
|
||||
var currentVersioning = $scope.currentFolder.versioning;
|
||||
|
||||
if (!currentVersioning || !currentVersioning.type || currentVersioning.type === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
// staggeredMaxAge can validly be zero, which we should not replace
|
||||
// with the default value of 365. So only set the default if it's
|
||||
// actually undefined.
|
||||
if (typeof $scope.currentFolder.staggeredMaxAge === 'undefined') {
|
||||
$scope.currentFolder.staggeredMaxAge = 365;
|
||||
$scope.currentFolder._guiVersioning.cleanupIntervalS = +currentVersioning.cleanupIntervalS;
|
||||
$scope.currentFolder._guiVersioning.selector = currentVersioning.type;
|
||||
|
||||
// Apply parameters currently in use
|
||||
switch (currentVersioning.type) {
|
||||
case "trashcan":
|
||||
$scope.currentFolder._guiVersioning.trashcanClean = +currentVersioning.params.cleanoutDays;
|
||||
break;
|
||||
case "simple":
|
||||
$scope.currentFolder._guiVersioning.simpleKeep = +currentVersioning.params.keep;
|
||||
$scope.currentFolder._guiVersioning.trashcanClean = +currentVersioning.params.cleanoutDays;
|
||||
break;
|
||||
case "staggered":
|
||||
$scope.currentFolder._guiVersioning.staggeredMaxAge = Math.floor(+currentVersioning.params.maxAge / 86400);
|
||||
$scope.currentFolder._guiVersioning.staggeredCleanInterval = +currentVersioning.params.cleanInterval;
|
||||
break;
|
||||
case "external":
|
||||
$scope.currentFolder._guiVersioning.externalCommand = currentVersioning.params.command;
|
||||
break;
|
||||
}
|
||||
$scope.currentFolder.externalCommand = $scope.currentFolder.externalCommand || "";
|
||||
};
|
||||
|
||||
$scope.editFolderExisting = function(folderCfg) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.currentFolder = angular.copy(folderCfg);
|
||||
|
||||
$scope.ignores.text = 'Loading...';
|
||||
$scope.ignores.error = null;
|
||||
@@ -1840,7 +1942,18 @@ angular.module('syncthing.core')
|
||||
$scope.emitHTTPError(err);
|
||||
});
|
||||
|
||||
$scope.editFolderModal();
|
||||
editFolder();
|
||||
};
|
||||
|
||||
$scope.editFolderDefaults = function() {
|
||||
$http.get(urlbase + '/config/defaults/folder')
|
||||
.success(function (data) {
|
||||
$scope.currentFolder = data;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = true;
|
||||
editFolder();
|
||||
})
|
||||
.error($scope.emitHTTPError);
|
||||
};
|
||||
|
||||
$scope.selectAllSharedDevices = function (state) {
|
||||
@@ -1859,37 +1972,43 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.addFolder = function () {
|
||||
$http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
||||
initShareEditing('folder');
|
||||
$scope.currentFolder.id = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
|
||||
$scope.currentSharing.unrelated = $scope.otherDevices();
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
$scope.editFolderModal();
|
||||
var folderID = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
|
||||
addFolderInit(folderID).then(function() {
|
||||
// Triggers the watch that sets the path
|
||||
$scope.currentFolder.label = $scope.currentFolder.label;
|
||||
editFolderModal();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addFolderAndShare = function (folder, folderLabel, device) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
||||
$scope.currentFolder.id = folder;
|
||||
$scope.currentFolder.label = folderLabel;
|
||||
$scope.currentFolder.viewFlags = {
|
||||
importFromOtherDevice: true
|
||||
};
|
||||
initShareEditing('folder');
|
||||
$scope.currentSharing.selected[device] = true;
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID;
|
||||
$scope.addFolderAndShare = function (folderID, folderLabel, device) {
|
||||
addFolderInit(folderID).then(function() {
|
||||
$scope.currentFolder.viewFlags = {
|
||||
importFromOtherDevice: true
|
||||
};
|
||||
$scope.currentSharing.selected[device] = true;
|
||||
$scope.currentFolder.label = folderLabel;
|
||||
editFolderModal();
|
||||
});
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
$scope.editFolderModal();
|
||||
};
|
||||
|
||||
function addFolderInit(folderID) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
return $http.get(urlbase + '/config/defaults/folder').then(function(p) {
|
||||
$scope.currentFolder = p.data;
|
||||
$scope.currentFolder.id = folderID;
|
||||
|
||||
initShareEditing('folder');
|
||||
$scope.currentSharing.unrelated = $scope.currentSharing.unrelated.concat($scope.currentSharing.shared);
|
||||
$scope.currentSharing.shared = [];
|
||||
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
}, $scope.emitHTTPError);
|
||||
}
|
||||
|
||||
$scope.shareFolderWithDevice = function (folder, device) {
|
||||
$scope.folders[folder].devices.push({
|
||||
deviceID: device
|
||||
@@ -1919,55 +2038,39 @@ angular.module('syncthing.core')
|
||||
folderCfg.devices = newDevices;
|
||||
delete $scope.currentSharing;
|
||||
|
||||
if (folderCfg.fileVersioningSelector === "trashcan") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'trashcan',
|
||||
'params': {
|
||||
'cleanoutDays': '' + folderCfg.trashcanClean
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.trashcanFileVersioning;
|
||||
delete folderCfg.trashcanClean;
|
||||
} else if (folderCfg.fileVersioningSelector === "simple") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'simple',
|
||||
'params': {
|
||||
'keep': '' + folderCfg.simpleKeep,
|
||||
'cleanoutDays': '' + folderCfg.trashcanClean
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.simpleFileVersioning;
|
||||
delete folderCfg.simpleKeep;
|
||||
} else if (folderCfg.fileVersioningSelector === "staggered") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'staggered',
|
||||
'params': {
|
||||
'maxAge': '' + (folderCfg.staggeredMaxAge * 86400),
|
||||
'cleanInterval': '' + folderCfg.staggeredCleanInterval,
|
||||
'versionsPath': '' + folderCfg.staggeredVersionsPath
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.staggeredFileVersioning;
|
||||
delete folderCfg.staggeredMaxAge;
|
||||
delete folderCfg.staggeredCleanInterval;
|
||||
delete folderCfg.staggeredVersionsPath;
|
||||
} else if (folderCfg.fileVersioningSelector === "external") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'external',
|
||||
'params': {
|
||||
'command': '' + folderCfg.externalCommand
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.externalFileVersioning;
|
||||
delete folderCfg.externalCommand;
|
||||
} else {
|
||||
folderCfg.versioning.type = folderCfg._guiVersioning.selector;
|
||||
if ($scope.internalVersioningEnabled()) {
|
||||
folderCfg.versioning.cleanupIntervalS = folderCfg._guiVersioning.cleanupIntervalS;
|
||||
}
|
||||
switch (folderCfg._guiVersioning.selector) {
|
||||
case "trashcan":
|
||||
folderCfg.versioning.params.cleanoutDays = '' + folderCfg._guiVersioning.trashcanClean;
|
||||
break;
|
||||
case "simple":
|
||||
folderCfg.versioning.params.keep = '' + folderCfg._guiVersioning.simpleKeep,
|
||||
folderCfg.versioning.params.cleanoutDays = '' + folderCfg._guiVersioning.trashcanClean;
|
||||
break;
|
||||
case "staggered":
|
||||
folderCfg.versioning.params.maxAge = '' + (folderCfg._guiVersioning.staggeredMaxAge * 86400);
|
||||
folderCfg.versioning.params.cleanInterval = '' + folderCfg._guiVersioning.staggeredCleanInterval;
|
||||
break;
|
||||
case "external":
|
||||
folderCfg.versioning.params.command = '' + folderCfg._guiVersioning.externalCommand;
|
||||
break;
|
||||
default:
|
||||
delete folderCfg.versioning;
|
||||
}
|
||||
delete folderCfg._guiVersioning;
|
||||
|
||||
if ($scope.editingDefaults) {
|
||||
$scope.config.defaults.folder = folderCfg;
|
||||
$scope.saveConfig();
|
||||
} else {
|
||||
saveFolderExisting(folderCfg);
|
||||
}
|
||||
};
|
||||
|
||||
function saveFolderExisting(folderCfg) {
|
||||
var ignoresLoaded = !$scope.ignores.disabled;
|
||||
var ignores = $scope.ignores.text.split('\n');
|
||||
// Split always returns a minimum 1-length array even for no patterns
|
||||
@@ -1981,7 +2084,11 @@ angular.module('syncthing.core')
|
||||
$scope.folders[folderCfg.id] = folderCfg;
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
|
||||
if (ignoresLoaded && $scope.editingExisting && ignores !== folderCfg.ignores) {
|
||||
function arrayEquals(a, b) {
|
||||
return a.length === b.length && a.every(function(v, i) { return v === b[i] });
|
||||
}
|
||||
|
||||
if (ignoresLoaded && $scope.editingExisting && !arrayEquals(ignores, folderCfg.ignores)) {
|
||||
saveIgnores(ignores);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<modal id="editDevice" status="default" icon="{{editingExisting ? 'fas fa-pencil-alt' : 'fas fa-desktop'}}" heading="{{editingExisting ? 'Edit Device' : 'Add Device' | translate}} {{currentDevice.name}}" large="yes" closeable="yes">
|
||||
<modal id="editDevice" status="default" icon="{{editDeviceModalIcon()}}" heading="{{editDeviceModalTitle()}}" large="yes" closeable="yes">
|
||||
<div class="modal-body">
|
||||
<form role="form" name="deviceEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#device-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li ng-if="!editingDefaults"><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#device-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="device-general" class="tab-pane in active">
|
||||
<div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<label translate for="deviceID">Device ID</label>
|
||||
<div ng-if="!editingExisting">
|
||||
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
|
||||
@@ -38,7 +38,7 @@
|
||||
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-sharing" class="tab-pane">
|
||||
<div ng-if="!editingDefaults" id="device-sharing" class="tab-pane">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
@@ -164,17 +164,14 @@
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="saveDevice()" ng-disabled="deviceEditor.$invalid">
|
||||
<span class="fas fa-check"></span> <span translate>Save</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#idqr" ng-if="editingExisting || deviceEditor.deviceID.$valid">
|
||||
<button ng-if="!editingDefaults" type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#idqr" ng-if="editingExisting || deviceEditor.deviceID.$valid">
|
||||
<span class="fas fa-qrcode"></span> <span translate>Show QR</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<div ng-if="editingExisting" class="pull-left">
|
||||
<button type="button" class="btn btn-warning btn-sm disabled" ng-if="willBeReintroducedBy" tooltip data-original-title="This device will be reintroduced by {{ willBeReintroducedBy }}">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#remove-device-confirmation" ng-if="!willBeReintroducedBy">
|
||||
<div ng-if="editingExisting && !editingDefaults" class="pull-left">
|
||||
<button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#remove-device-confirmation">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<p ng-model="currentDevice.name" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
||||
<span translate translate-value-name="{{currentDevice.name}}">Are you sure you want to remove device {%name%}?</span>
|
||||
</p>
|
||||
<p ng-if="willBeReintroducedBy" translate translate-value-reintroducer="{{willBeReintroducedBy}}">{%reintroducer%} might reintroduce this device.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-dismiss="modal" ng-click="deleteDevice()">
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<modal id="editFolder" status="default" icon="{{editingExisting ? 'fas fa-pencil-alt' : 'fas fa-folder'}}" heading="{{editingExisting ? 'Edit Folder' : 'Add Folder' | translate}} ({{folderLabel(currentFolder.id)}})" large="yes" closeable="yes">
|
||||
<modal id="editFolder" status="default" icon="{{editFolderModalIcon()}}" heading="{{editFolderModalTitle()}}" large="yes" closeable="yes">
|
||||
<div class="modal-body">
|
||||
<form role="form" name="folderEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(folderEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#folder-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-versioning"><span class="fas fa-copy"></span> <span translate>File Versioning</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-ignores"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li ng-if="!editingDefaults"><a data-toggle="tab" href="#folder-ignores"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="folder-general" class="tab-pane in active">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty && !editingDefaults}">
|
||||
<label for="folderLabel"><span translate>Folder Label</span></label>
|
||||
<input name="folderLabel" id="folderLabel" class="form-control" type="text" ng-model="currentFolder.label" value="{{currentFolder.label}}" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<label for="folderID"><span translate>Folder ID</span></label>
|
||||
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}" />
|
||||
<p class="help-block">
|
||||
@@ -28,19 +28,21 @@
|
||||
<span translate ng-show="!editingExisting">When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty && !editingDefaults}">
|
||||
<label translate for="folderPath">Folder Path</label>
|
||||
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required="" aria-required="true" path-is-sub-dir />
|
||||
<input name="folderPath" ng-readonly="editingExisting && !editingDefaults" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" ng-required="!editingDefaults" ng-aria-required="!editingDefaults" path-is-sub-dir />
|
||||
<datalist id="directory-list">
|
||||
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
|
||||
</datalist>
|
||||
<p class="help-block">
|
||||
<span ng-if="folderEditor.folderPath.$valid || folderEditor.folderPath.$pristine"><span translate>Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for</span> <code>{{system.tilde}}</code>.</br></span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty">The folder path cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty && !editingDefaults">The folder path cannot be blank.</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length == 0">Warning, this path is a subdirectory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length != 0">Warning, this path is a subdirectory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.isParent && folderPathErrors.otherLabel.length == 0">Warning, this path is a parent directory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.isParent && folderPathErrors.otherLabel.length != 0">Warning, this path is a parent directory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
<span ng-if="folderPathErrors.isParent && !editingDefaults">
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.otherLabel.length == 0">Warning, this path is a parent directory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.otherLabel.length != 0">Warning, this path is a parent directory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,7 +90,7 @@
|
||||
<div id="folder-versioning" class="tab-pane">
|
||||
<div class="form-group">
|
||||
<label translate>File Versioning</label> <a href="https://docs.syncthing.net/users/versioning.html" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Help</span></a>
|
||||
<select class="form-control" ng-model="currentFolder.fileVersioningSelector">
|
||||
<select class="form-control" ng-model="currentFolder._guiVersioning.selector">
|
||||
<option value="none" translate>No File Versioning</option>
|
||||
<option value="trashcan" translate>Trash Can File Versioning</option>
|
||||
<option value="simple" translate>Simple File Versioning</option>
|
||||
@@ -96,69 +98,69 @@
|
||||
<option value="external" translate>External File Versioning</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='trashcan' || currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.trashcanClean.$invalid && folderEditor.trashcanClean.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='trashcan' || currentFolder._guiVersioning.selectorector=='simple'" ng-class="{'has-error': folderEditor._guiVersioning.trashcanClean.$invalid && folderEditor._guiVersioning.trashcanClean.$dirty}">
|
||||
<p translate class="help-block">Files are moved to .stversions directory when replaced or deleted by Syncthing.</p>
|
||||
<label translate for="trashcanClean">Clean out after</label>
|
||||
<div class="input-group">
|
||||
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required="" aria-required="true" min="0" />
|
||||
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder._guiVersioning.trashcanClean" required="" aria-required="true" min="0" />
|
||||
<div class="input-group-addon" translate>days</div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.trashcanClean.$valid || folderEditor.trashcanClean.$pristine">The number of days to keep files in the trash can. Zero means forever.</span>
|
||||
<span translate ng-if="folderEditor.trashcanClean.$error.required && folderEditor.trashcanClean.$dirty">The number of days must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.trashcanClean.$error.min && folderEditor.trashcanClean.$dirty">A negative number of days doesn't make sense.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$valid || folderEditor._guiVersioning.trashcanClean.$pristine">The number of days to keep files in the trash can. Zero means forever.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$error.required && folderEditor._guiVersioning.trashcanClean.$dirty">The number of days must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$error.min && folderEditor._guiVersioning.trashcanClean.$dirty">A negative number of days doesn't make sense.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='simple'" ng-class="{'has-error': folderEditor._guiVersioning.simpleKeep.$invalid && folderEditor._guiVersioning.simpleKeep.$dirty}">
|
||||
<p translate class="help-block">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</p>
|
||||
<label translate for="simpleKeep">Keep Versions</label>
|
||||
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required="" aria-required="true" min="1" />
|
||||
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder._guiVersioning.simpleKeep" required="" aria-required="true" min="1" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.simpleKeep.$valid || folderEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
|
||||
<span translate ng-if="folderEditor.simpleKeep.$error.required && folderEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.simpleKeep.$error.min && folderEditor.simpleKeep.$dirty">You must keep at least one version.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$valid || folderEditor._guiVersioning.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$error.required && folderEditor._guiVersioning.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$error.min && folderEditor._guiVersioning.simpleKeep.$dirty">You must keep at least one version.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='staggered'" ng-class="{'has-error': folderEditor.staggeredMaxAge.$invalid && folderEditor.staggeredMaxAge.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='staggered'" ng-class="{'has-error': folderEditor._guiVersioning.staggeredMaxAge.$invalid && folderEditor._guiVersioning.staggeredMaxAge.$dirty}">
|
||||
<p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p>
|
||||
<p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p>
|
||||
<label translate for="staggeredMaxAge">Maximum Age</label>
|
||||
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required="" aria-required="true" min="0" />
|
||||
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder._guiVersioning.staggeredMaxAge" required="" aria-required="true" min="0" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$valid || folderEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$error.min && folderEditor.staggeredMaxAge.$dirty">A negative number of days doesn't make sense.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$valid || folderEditor._guiVersioning.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$error.required && folderEditor._guiVersioning.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$error.min && folderEditor._guiVersioning.staggeredMaxAge.$dirty">A negative number of days doesn't make sense.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
|
||||
<label translate for="staggeredVersionsPath">Versions Path</label>
|
||||
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath" />
|
||||
<div class="form-group" ng-if="internalVersioningEnabled()">
|
||||
<label translate for="fsPath">Versions Path</label>
|
||||
<input name="fsPath" id="fsPath" class="form-control" type="text" ng-model="currentFolder.versioning.fsPath" />
|
||||
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
|
||||
<p translate class="help-block">An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.</p>
|
||||
<label translate for="externalCommand">Command</label>
|
||||
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder.externalCommand" required="" aria-required="true" />
|
||||
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder._guiVersioning.externalCommand" required="" aria-required="true" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.externalCommand.$valid || folderEditor.externalCommand.$pristine">See external versioning help for supported templated command line parameters.</span>
|
||||
<span translate ng-if="folderEditor.externalCommand.$error.required && folderEditor.externalCommand.$dirty">The path cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector != 'none'" ng-class="{'has-error': folderEditor.versioningCleanupIntervalS.$invalid && folderEditor.versioningCleanupIntervalS.$dirty}">
|
||||
<div class="form-group" ng-if="internalVersioningEnabled()" ng-class="{'has-error': folderEditor._guiVersioning.cleanupIntervalS.$invalid && folderEditor._guiVersioning.cleanupIntervalS.$dirty}">
|
||||
<label translate for="versioningCleanupIntervalS">Cleanup Interval</label>
|
||||
<div class="input-group">
|
||||
<input name="versioningCleanupIntervalS" id="versioningCleanupIntervalS" class="form-control text-right" type="number" ng-model="currentFolder.versioningCleanupIntervalS" required="" min="0" max="31536000" aria-required="true" />
|
||||
<input name="versioningCleanupIntervalS" id="versioningCleanupIntervalS" class="form-control text-right" type="number" ng-model="currentFolder._guiVersioning.cleanupIntervalS" required="" min="0" max="31536000" aria-required="true" />
|
||||
<div class="input-group-addon" translate>seconds</div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$valid || folderEditor.versioningCleanupIntervalS.$pristine"class="help-block">The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.</span>
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$error.required && folderEditor.versioningCleanupIntervalS.$dirty">The cleanup interval cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$error.min && folderEditor.versioningCleanupIntervalS.$dirty">The interval must be a positive number of seconds.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$valid || folderEditor._guiVersioning.cleanupIntervalS.$pristine"class="help-block">The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$error.required && folderEditor._guiVersioning.cleanupIntervalS.$dirty">The cleanup interval cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$error.min && folderEditor._guiVersioning.cleanupIntervalS.$dirty">The interval must be a positive number of seconds.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="folder-ignores" class="tab-pane">
|
||||
<div ng-if="!editingDefaults" id="folder-ignores" class="tab-pane">
|
||||
<p translate>Enter ignore patterns, one per line.</p>
|
||||
<div ng-class="{'has-error': ignores.error != null}">
|
||||
<textarea class="form-control" rows="5" ng-model="ignores.text" ng-disabled="ignores.disabled"></textarea>
|
||||
@@ -280,7 +282,7 @@
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingExisting">
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingExisting && !editingDefaults">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<div ng-repeat="(key, value) in advancedConfig.gui" ng-init="type = inputTypeFor(key, value)" ng-if="type != 'skip'" class="form-group">
|
||||
<label for="guiInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.gui[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.gui[key]" />
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="guiInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.gui[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="guiInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.gui[key]" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -65,50 +65,113 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default" ng-repeat="folder in advancedConfig.folders">
|
||||
<div class="panel-heading" role="tab" id="folder{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#folder{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;">
|
||||
<h4 ng-if="folder.label.length == 0" class="panel-title" tabindex="0">
|
||||
<span translate>Folder</span> "{{folder.id}}"
|
||||
</h4>
|
||||
<h4 ng-if="folder.label.length != 0" class="panel-title" tabindex="0">
|
||||
<span translate>Folder</span> "{{folder.label}}" ({{folder.id}})
|
||||
</h4>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="advancedFoldersHeading" data-toggle="collapse" data-parent="#advancedAccordion" href="#advancedFolders" aria-expanded="false" aria-controls="advancedFolders" style="cursor: pointer;">
|
||||
<h4 class="panel-title" translate>Folders</h4>
|
||||
</div>
|
||||
<div id="folder{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="folder{{$index}}Heading">
|
||||
<div id="advancedFolders" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedFoldersHeading">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in folder" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="folder{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="folder[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="folder[key]" />
|
||||
<div class="panel panel-default" ng-repeat="folder in advancedConfig.folders" ng-init="folderIndex = $index">
|
||||
<div class="panel-heading" role="tab" id="folder{{folderIndex}}Heading" data-toggle="collapse" data-parent="#advancedFolders" href="#folder{{folderIndex}}Config" aria-expanded="false" aria-controls="folder{{folderIndex}}Config" style="cursor: pointer;">
|
||||
<h4 ng-if="folder.label.length == 0" class="panel-title" tabindex="0">
|
||||
<span translate>Folder</span> "{{folder.id}}"
|
||||
</h4>
|
||||
<h4 ng-if="folder.label.length != 0" class="panel-title" tabindex="0">
|
||||
<span translate>Folder</span> "{{folder.label}}" ({{folder.id}})
|
||||
</h4>
|
||||
</div>
|
||||
<div id="folder{{folderIndex}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="folder{{folderIndex}}Heading">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in folder" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="folder{{folderIndex}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="folder{{folderIndex}}Input{{$index}}" class="form-control" type="text" ng-model="folder[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="folder{{folderIndex}}Input{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="folder[key]" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default" ng-repeat="device in advancedConfig.devices">
|
||||
<div class="panel-heading" role="tab" id="device{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#device{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0">
|
||||
<span translate>Device</span> "{{deviceName(device)}}"
|
||||
</h4>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="advancedDevicesHeading" data-toggle="collapse" data-parent="#advancedAccordion" href="#advancedDevices" aria-expanded="false" aria-controls="advancedDevices" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0" translate>Devices</h4>
|
||||
</div>
|
||||
<div id="device{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="device{{$index}}Heading">
|
||||
<div id="advancedDevices" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedDevicesHeading">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in device" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="device{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="device[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="device[key]" />
|
||||
<div class="panel panel-default" ng-repeat="device in advancedConfig.devices" ng-init="deviceIndex = $index">
|
||||
<div class="panel-heading" role="tab" id="device{{deviceIndex}}Heading" data-toggle="collapse" data-parent="#advancedDevices" href="#device{{deviceIndex}}Config" aria-expanded="false" aria-controls="device{{deviceIndex}}Config" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0">
|
||||
<span translate>Device</span> "{{deviceName(device)}}"
|
||||
</h4>
|
||||
</div>
|
||||
<div id="device{{deviceIndex}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="device{{deviceIndex}}Heading">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in device" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="device{{deviceIndex}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="device{{deviceIndex}}Input{{$index}}" class="form-control" type="text" ng-model="device[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="device{{deviceIndex}}Input{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="device[key]" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="advancedDefaultsHeading" data-toggle="collapse" data-parent="#advancedAccordion" href="#advancedDefaults" aria-expanded="false" aria-controls="advancedDefaults" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0" translate>Defaults</h4>
|
||||
</div>
|
||||
<div id="advancedDefaults" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedDefaultsHeading">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="advancedDefaultFolderHeading" data-toggle="collapse" data-parent="#advancedDefaults" href="#advancedDefaultFolder" aria-expanded="false" aria-controls="advancedDefaultFolder" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0" translate>Default Folder</h4>
|
||||
</div>
|
||||
<div id="advancedDefaultFolder" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedDefaultFolderHeading">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in advancedConfig.defaults.folder" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="advancedDefaultFolderInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="advancedDefaultFolderInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.defaults.folder[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="advancedDefaultFolderInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.defaults.folder[key]" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="advancedDefaultDeviceHeading" data-toggle="collapse" data-parent="#advancedDefaults" href="#advancedDefaultDevice" aria-expanded="false" aria-controls="advancedDefaultDevice" style="cursor: pointer;">
|
||||
<h4 class="panel-title" tabindex="0" translate>Default Device</h4>
|
||||
</div>
|
||||
<div id="advancedDefaultDevice" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedDefaultDeviceHeading">
|
||||
<form class="form-horizontal" role="form">
|
||||
<div ng-repeat="(key, value) in advancedConfig.defaults.device" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
|
||||
<label for="advancedDefaultDeviceInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-if="inputTypeFor(key, value) == 'list'" id="advancedDefaultDeviceInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.defaults.device[key]" ng-list />
|
||||
<input ng-if="inputTypeFor(key, value) != 'list'" id="advancedDefaultDeviceInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.defaults.device[key]" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -100,13 +100,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label translate for="urVersion">Default Folder Path</label>
|
||||
<input id="DefaultFolderPath" class="form-control" type="text" ng-model="tmpOptions.defaultFolderPath" />
|
||||
<p class="help-block">
|
||||
<span translate translate-value-tilde="{{system.tilde}}">
|
||||
Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.
|
||||
</span>
|
||||
<div>
|
||||
<label translate>Default Configuration</label>
|
||||
<p>
|
||||
<button type="button" class="btn btn-default btn-secondary" ng-click="editFolderDefaults()">
|
||||
<span translate>Edit Folder Defaults</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-secondary" ng-click="editDeviceDefaults()">
|
||||
<span translate>Edit Device Defaults</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -560,13 +560,13 @@
|
||||
<button ng-if="folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, false)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type">
|
||||
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type && folder.versioning.type != 'external'">
|
||||
<span class="fas fa-undo"></span> <span translate>Versions</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-disabled="['idle', 'stopped', 'unshared', 'outofsync', 'faileditems', 'localadditions'].indexOf(folderStatus(folder)) < 0">
|
||||
<span class="fas fa-refresh"></span> <span translate>Rescan</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editFolder(folder)">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editFolderExisting(folder)">
|
||||
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
||||
</button>
|
||||
</span>
|
||||
@@ -582,7 +582,7 @@
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="setAllFoldersPause(false)" ng-if="isAtleastOneFolderPausedStateSetTo(true)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume All</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanAllFolders()">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="rescanAllFolders()" ng-if="folderList().length > 0" ng-disabled="!isAtleastOneFolderPausedStateSetTo(false)">
|
||||
<span class="fas fa-refresh"></span> <span translate>Rescan All</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="addFolder()">
|
||||
@@ -720,6 +720,11 @@
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
||||
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
|
||||
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm:ss"}}</td>
|
||||
</tr>
|
||||
<tr ng-if="connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-cloud-download-alt"></span> <span translate>Download Rate</span></th>
|
||||
<td class="text-right">
|
||||
@@ -805,11 +810,6 @@
|
||||
<th><span class="fas fa-fw fa-tag"></span> <span translate>Version</span></th>
|
||||
<td class="text-right">{{connections[deviceCfg.deviceID].clientVersion}}</td>
|
||||
</tr>
|
||||
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
||||
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
|
||||
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm:ss"}}</td>
|
||||
</tr>
|
||||
<tr ng-if="deviceFolders(deviceCfg).length > 0">
|
||||
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folders</span></th>
|
||||
<td class="text-right" ng-attr-title="{{deviceFolders(deviceCfg).map(folderLabel).join(', ')}}">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
|
||||
@@ -833,7 +833,7 @@
|
||||
<button ng-if="deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, false)">
|
||||
<span class="fas fa-play"></span> <span translate>Resume</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editDevice(deviceCfg)">
|
||||
<button type="button" class="btn btn-sm btn-default" ng-click="editDeviceExisting(deviceCfg)">
|
||||
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@@ -52,6 +52,7 @@ angular.module('syncthing.core')
|
||||
$scope.metricRates = false;
|
||||
$scope.folderPathErrors = {};
|
||||
$scope.currentFolder = {};
|
||||
$scope.currentDevice = {};
|
||||
$scope.ignores = {
|
||||
text: '',
|
||||
error: null,
|
||||
@@ -63,26 +64,14 @@ angular.module('syncthing.core')
|
||||
$scope.metricRates = (window.localStorage["metricRates"] == "true");
|
||||
} catch (exception) { }
|
||||
|
||||
$scope.folderDefaults = {
|
||||
devices: [],
|
||||
type: "sendreceive",
|
||||
rescanIntervalS: 3600,
|
||||
fsWatcherDelayS: 10,
|
||||
fsWatcherEnabled: true,
|
||||
minDiskFree: { value: 1, unit: "%" },
|
||||
maxConflicts: 10,
|
||||
fsync: true,
|
||||
order: "random",
|
||||
fileVersioningSelector: "none",
|
||||
$scope.versioningDefaults = {
|
||||
selector: "none",
|
||||
trashcanClean: 0,
|
||||
versioningCleanupIntervalS: 3600,
|
||||
cleanupIntervalS: 3600,
|
||||
simpleKeep: 5,
|
||||
staggeredMaxAge: 365,
|
||||
staggeredCleanInterval: 3600,
|
||||
staggeredVersionsPath: "",
|
||||
externalCommand: "",
|
||||
autoNormalize: true,
|
||||
path: "",
|
||||
};
|
||||
|
||||
$scope.localStateTotal = {
|
||||
@@ -122,6 +111,7 @@ angular.module('syncthing.core')
|
||||
refreshSystem();
|
||||
refreshDiscoveryCache();
|
||||
refreshConfig();
|
||||
refreshCluster();
|
||||
refreshConnectionStats();
|
||||
refreshDeviceStats();
|
||||
refreshFolderStats();
|
||||
@@ -222,6 +212,9 @@ angular.module('syncthing.core')
|
||||
});
|
||||
|
||||
$scope.$on(Events.DEVICE_DISCONNECTED, function (event, arg) {
|
||||
if (!$scope.connections[arg.data.id]) {
|
||||
return;
|
||||
}
|
||||
$scope.connections[arg.data.id].connected = false;
|
||||
refreshDeviceStats();
|
||||
});
|
||||
@@ -244,32 +237,69 @@ angular.module('syncthing.core')
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(Events.DEVICE_REJECTED, function (event, arg) {
|
||||
var pendingDevice = {
|
||||
time: arg.time,
|
||||
name: arg.data.name,
|
||||
address: arg.data.address
|
||||
};
|
||||
console.log("rejected device:", arg.data.device, pendingDevice);
|
||||
$scope.$on(Events.PENDING_DEVICES_CHANGED, function (event, arg) {
|
||||
if (!(arg.data.added || arg.data.removed)) {
|
||||
// Not enough information to update in place, just refresh it completely
|
||||
refreshCluster();
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.pendingDevices[arg.data.device] = pendingDevice;
|
||||
if (arg.data.added) {
|
||||
arg.data.added.forEach(function (rejected) {
|
||||
var pendingDevice = {
|
||||
time: arg.time,
|
||||
name: rejected.name,
|
||||
address: rejected.address
|
||||
};
|
||||
console.log("rejected device:", rejected.deviceID, pendingDevice);
|
||||
$scope.pendingDevices[rejected.deviceID] = pendingDevice;
|
||||
});
|
||||
}
|
||||
|
||||
if (arg.data.removed) {
|
||||
arg.data.removed.forEach(function (dev) {
|
||||
console.log("no longer pending device:", dev.deviceID);
|
||||
delete $scope.pendingDevices[dev.deviceID];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
|
||||
var offeringDevice = {
|
||||
time: arg.time,
|
||||
label: arg.data.folderLabel
|
||||
};
|
||||
console.log("rejected folder", arg.data.folder, "from device:", arg.data.device, offeringDevice);
|
||||
|
||||
var pendingFolder = $scope.pendingFolders[arg.data.folder];
|
||||
if (pendingFolder === undefined) {
|
||||
pendingFolder = {
|
||||
offeredBy: {}
|
||||
};
|
||||
$scope.$on(Events.PENDING_FOLDERS_CHANGED, function (event, arg) {
|
||||
if (!(arg.data.added || arg.data.removed)) {
|
||||
// Not enough information to update in place, just refresh it completely
|
||||
refreshCluster();
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg.data.added) {
|
||||
arg.data.added.forEach(function (rejected) {
|
||||
var offeringDevice = {
|
||||
time: arg.time,
|
||||
label: rejected.folderLabel
|
||||
};
|
||||
console.log("rejected folder", rejected.folderID, "from device:", rejected.deviceID, offeringDevice);
|
||||
|
||||
var pendingFolder = $scope.pendingFolders[rejected.folderID];
|
||||
if (pendingFolder === undefined) {
|
||||
pendingFolder = {
|
||||
offeredBy: {}
|
||||
};
|
||||
}
|
||||
pendingFolder.offeredBy[rejected.deviceID] = offeringDevice;
|
||||
$scope.pendingFolders[rejected.folderID] = pendingFolder;
|
||||
});
|
||||
}
|
||||
|
||||
if (arg.data.removed) {
|
||||
arg.data.removed.forEach(function (folderDev) {
|
||||
console.log("no longer pending folder", folderDev.folderID, "from device:", folderDev.deviceID);
|
||||
if (folderDev.deviceID === undefined) {
|
||||
delete $scope.pendingFolders[folderDev.folderID];
|
||||
} else if ($scope.pendingFolders[folderDev.folderID]) {
|
||||
delete $scope.pendingFolders[folderDev.folderID].offeredBy[folderDev.deviceID];
|
||||
}
|
||||
});
|
||||
}
|
||||
pendingFolder.offeredBy[arg.data.device] = offeringDevice;
|
||||
$scope.pendingFolders[arg.data.folder] = pendingFolder;
|
||||
});
|
||||
|
||||
$scope.$on('ConfigLoaded', function () {
|
||||
@@ -421,7 +451,6 @@ angular.module('syncthing.core')
|
||||
});
|
||||
});
|
||||
|
||||
refreshCluster();
|
||||
refreshNoAuthWarning();
|
||||
setDefaultTheme();
|
||||
|
||||
@@ -690,7 +719,7 @@ angular.module('syncthing.core')
|
||||
}
|
||||
|
||||
function shouldSetDefaultFolderPath() {
|
||||
return $scope.config.options && $scope.config.options.defaultFolderPath && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine
|
||||
return $scope.config.defaults.folder.path && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine && !$scope.editingDefaults;
|
||||
}
|
||||
|
||||
function resetRemoteNeed() {
|
||||
@@ -733,8 +762,26 @@ angular.module('syncthing.core')
|
||||
$scope.currentSharing.unrelated = [];
|
||||
$scope.currentSharing.selected = {};
|
||||
$scope.currentSharing.encryptionPasswords = {};
|
||||
if (editing === 'folder') {
|
||||
initShareEditingFolder();
|
||||
}
|
||||
};
|
||||
|
||||
function initShareEditingFolder() {
|
||||
$scope.currentFolder.devices.forEach(function (n) {
|
||||
if (n.deviceID !== $scope.myID) {
|
||||
$scope.currentSharing.shared.push($scope.devices[n.deviceID]);
|
||||
}
|
||||
if (n.encryptionPassword !== '') {
|
||||
$scope.currentSharing.encryptionPasswords[n.deviceID] = n.encryptionPassword;
|
||||
}
|
||||
$scope.currentSharing.selected[n.deviceID] = true;
|
||||
});
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID && !$scope.currentSharing.selected[n.deviceID];
|
||||
});
|
||||
}
|
||||
|
||||
$scope.refreshFailed = function (page, perpage) {
|
||||
if (!$scope.failed || !$scope.failed.folder) {
|
||||
return;
|
||||
@@ -1202,6 +1249,7 @@ angular.module('syncthing.core')
|
||||
}).error($scope.emitHTTPError);
|
||||
},
|
||||
show: function () {
|
||||
$scope.logging.paused = false;
|
||||
$scope.logging.refreshFacilities();
|
||||
$scope.logging.timer = $timeout($scope.logging.fetch);
|
||||
var textArea = $('#logViewerText');
|
||||
@@ -1459,9 +1507,40 @@ angular.module('syncthing.core')
|
||||
$scope.configInSync = true;
|
||||
};
|
||||
|
||||
$scope.editDevice = function (deviceCfg) {
|
||||
function editDeviceModal() {
|
||||
$scope.currentDevice._addressesStr = $scope.currentDevice.addresses.join(', ');
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
}
|
||||
|
||||
$scope.editDeviceModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
return $translate.instant("Edit Device Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
title += $translate.instant("Edit Device");
|
||||
} else {
|
||||
title += $translate.instant("Add Device");
|
||||
}
|
||||
var name = $scope.deviceName($scope.currentDevice);
|
||||
if (name !== '') {
|
||||
title += ' (' + name + ')';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
$scope.editDeviceModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-desktop';
|
||||
};
|
||||
|
||||
$scope.editDeviceExisting = function (deviceCfg) {
|
||||
$scope.currentDevice = $.extend({}, deviceCfg);
|
||||
$scope.editingExisting = true;
|
||||
$scope.editingDefaults = false;
|
||||
$scope.willBeReintroducedBy = undefined;
|
||||
if (deviceCfg.introducedBy) {
|
||||
var introducerDevice = $scope.devices[deviceCfg.introducedBy];
|
||||
@@ -1469,7 +1548,6 @@ angular.module('syncthing.core')
|
||||
$scope.willBeReintroducedBy = $scope.deviceName(introducerDevice);
|
||||
}
|
||||
}
|
||||
$scope.currentDevice._addressesStr = deviceCfg.addresses.join(', ');
|
||||
initShareEditing('device');
|
||||
$scope.deviceFolders($scope.currentDevice).forEach(function (folderID) {
|
||||
$scope.currentSharing.shared.push($scope.folders[folderID]);
|
||||
@@ -1485,8 +1563,15 @@ angular.module('syncthing.core')
|
||||
$scope.currentSharing.unrelated = $scope.folderList().filter(function (n) {
|
||||
return !$scope.currentSharing.selected[n.id];
|
||||
});
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
editDeviceModal();
|
||||
};
|
||||
|
||||
$scope.editDeviceDefaults = function () {
|
||||
$http.get(urlbase + '/config/defaults/device').then(function (p) {
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.editingDefaults = true;
|
||||
editDeviceModal();
|
||||
}, $scope.emitHTTPError);
|
||||
};
|
||||
|
||||
$scope.selectAllSharedFolders = function (state) {
|
||||
@@ -1518,19 +1603,16 @@ angular.module('syncthing.core')
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
$scope.currentDevice = {
|
||||
name: name,
|
||||
deviceID: deviceID,
|
||||
_addressesStr: 'dynamic',
|
||||
compression: 'metadata',
|
||||
introducer: false,
|
||||
ignoredFolders: []
|
||||
};
|
||||
$scope.editingExisting = false;
|
||||
initShareEditing('device');
|
||||
$scope.currentSharing.unrelated = $scope.folderList();
|
||||
$scope.deviceEditor.$setPristine();
|
||||
$('#editDevice').modal();
|
||||
$http.get(urlbase + '/config/defaults/device').then(function (p) {
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.currentDevice.name = name;
|
||||
$scope.currentDevice.deviceID = deviceID;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
initShareEditing('device');
|
||||
$scope.currentSharing.unrelated = $scope.folderList();
|
||||
editDeviceModal();
|
||||
}, $scope.emitHTTPError);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1555,22 +1637,30 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.saveDevice = function () {
|
||||
$('#editDevice').modal('hide');
|
||||
$scope.saveDeviceConfig($scope.currentDevice);
|
||||
};
|
||||
|
||||
$scope.saveDeviceConfig = function (deviceCfg) {
|
||||
deviceCfg.addresses = deviceCfg._addressesStr.split(',').map(function (x) {
|
||||
$scope.currentDevice.addresses = $scope.currentDevice._addressesStr.split(',').map(function (x) {
|
||||
return x.trim();
|
||||
});
|
||||
delete $scope.currentDevice._addressesStr;
|
||||
if ($scope.editingDefaults) {
|
||||
$scope.config.defaults.device = $scope.currentDevice;
|
||||
} else {
|
||||
setDeviceConfig();
|
||||
}
|
||||
delete $scope.currentSharing;
|
||||
delete $scope.currentDevice;
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
$scope.devices[deviceCfg.deviceID] = deviceCfg;
|
||||
function setDeviceConfig() {
|
||||
var currentID = $scope.currentDevice.deviceID;
|
||||
$scope.devices[currentID] = $scope.currentDevice;
|
||||
$scope.config.devices = deviceList($scope.devices);
|
||||
|
||||
for (var id in $scope.currentSharing.selected) {
|
||||
if ($scope.currentSharing.selected[id]) {
|
||||
var found = false;
|
||||
for (i = 0; i < $scope.folders[id].devices.length; i++) {
|
||||
if ($scope.folders[id].devices[i].deviceID === deviceCfg.deviceID) {
|
||||
if ($scope.folders[id].devices[i].deviceID === currentID) {
|
||||
found = true;
|
||||
// Update encryption pw
|
||||
$scope.folders[id].devices[i].encryptionPassword = $scope.currentSharing.encryptionPasswords[id];
|
||||
@@ -1581,22 +1671,19 @@ angular.module('syncthing.core')
|
||||
if (!found) {
|
||||
// Add device to folder
|
||||
$scope.folders[id].devices.push({
|
||||
deviceID: deviceCfg.deviceID,
|
||||
encryptionPassword: $scope.currentSharing.encryptionPasswords[id]
|
||||
deviceID: currentID,
|
||||
encryptionPassword: $scope.currentSharing.encryptionPasswords[id],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Remove device from folder
|
||||
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
||||
return n.deviceID !== deviceCfg.deviceID;
|
||||
return n.deviceID !== currentID;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delete $scope.currentSharing;
|
||||
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
$scope.ignoreDevice = function (deviceID, pendingDevice) {
|
||||
@@ -1733,14 +1820,14 @@ angular.module('syncthing.core')
|
||||
if (!newvalue || !shouldSetDefaultFolderPath()) {
|
||||
return;
|
||||
}
|
||||
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
||||
$scope.currentFolder.path = pathJoin($scope.config.defaults.folder.path, newvalue);
|
||||
});
|
||||
|
||||
$scope.$watch('currentFolder.id', function (newvalue) {
|
||||
if (!newvalue || !shouldSetDefaultFolderPath() || $scope.currentFolder.label) {
|
||||
return;
|
||||
}
|
||||
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
||||
$scope.currentFolder.path = pathJoin($scope.config.defaults.folder.path, newvalue);
|
||||
});
|
||||
|
||||
$scope.fsWatcherToggled = function () {
|
||||
@@ -1767,7 +1854,8 @@ angular.module('syncthing.core')
|
||||
$('#globalChanges').modal();
|
||||
};
|
||||
|
||||
$scope.editFolderModal = function () {
|
||||
function editFolderModal() {
|
||||
initVersioningEditing();
|
||||
$scope.folderPathErrors = {};
|
||||
$scope.folderEditor.$setPristine();
|
||||
$('#editFolder').modal().one('shown.bs.tab', function (e) {
|
||||
@@ -1780,67 +1868,81 @@ angular.module('syncthing.core')
|
||||
});
|
||||
};
|
||||
|
||||
$scope.editFolder = function (folderCfg) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.currentFolder = angular.copy(folderCfg);
|
||||
$scope.editFolderModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
return $translate.instant("Edit Folder Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
title += $translate.instant("Edit Folder");
|
||||
} else {
|
||||
title += $translate.instant("Add Folder");
|
||||
}
|
||||
if ($scope.currentFolder.id !== '') {
|
||||
title += ' (' + $scope.folderLabel($scope.currentFolder.id) + ')';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
$scope.editFolderModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-folder';
|
||||
};
|
||||
|
||||
function editFolder() {
|
||||
if ($scope.currentFolder.path.length > 1 && $scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
|
||||
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
|
||||
} else if (!$scope.currentFolder.path) {
|
||||
// undefined path leads to invalid input field
|
||||
$scope.currentFolder.path = '';
|
||||
}
|
||||
// Cache complete device objects indexed by ID for lookups
|
||||
initShareEditing('folder');
|
||||
$scope.currentFolder.devices.forEach(function (n) {
|
||||
if (n.deviceID !== $scope.myID) {
|
||||
$scope.currentSharing.shared.push($scope.devices[n.deviceID]);
|
||||
}
|
||||
if (n.encryptionPassword !== '') {
|
||||
$scope.currentSharing.encryptionPasswords[n.deviceID] = n.encryptionPassword;
|
||||
}
|
||||
$scope.currentSharing.selected[n.deviceID] = true;
|
||||
});
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID && !$scope.currentSharing.selected[n.deviceID];
|
||||
});
|
||||
if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "trashcan") {
|
||||
$scope.currentFolder.trashcanFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "trashcan";
|
||||
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "simple") {
|
||||
$scope.currentFolder.simpleFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "simple";
|
||||
$scope.currentFolder.simpleKeep = +$scope.currentFolder.versioning.params.keep;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "staggered") {
|
||||
$scope.currentFolder.staggeredFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "staggered";
|
||||
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.versioning.params.maxAge / 86400);
|
||||
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.versioning.params.cleanInterval;
|
||||
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.versioning.params.versionsPath;
|
||||
$scope.currentFolder.versioningCleanupIntervalS = +$scope.currentFolder.versioning.cleanupIntervalS;
|
||||
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "external") {
|
||||
$scope.currentFolder.externalFileVersioning = true;
|
||||
$scope.currentFolder.fileVersioningSelector = "external";
|
||||
$scope.currentFolder.externalCommand = $scope.currentFolder.versioning.params.command;
|
||||
} else {
|
||||
$scope.currentFolder.fileVersioningSelector = "none";
|
||||
editFolderModal();
|
||||
}
|
||||
|
||||
$scope.internalVersioningEnabled = function(guiVersioning) {
|
||||
if (!$scope.currentFolder._guiVersioning) {
|
||||
return false;
|
||||
}
|
||||
$scope.currentFolder.trashcanClean = $scope.currentFolder.trashcanClean || 0; // weeds out nulls and undefineds
|
||||
$scope.currentFolder.simpleKeep = $scope.currentFolder.simpleKeep || 5;
|
||||
$scope.currentFolder.staggeredCleanInterval = $scope.currentFolder.staggeredCleanInterval || 3600;
|
||||
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.staggeredVersionsPath || "";
|
||||
// Zero is a valid, non-default value (disabled)
|
||||
if ($scope.currentFolder.versioningCleanupIntervalS !== 0) {
|
||||
$scope.currentFolder.versioningCleanupIntervalS = $scope.currentFolder.versioningCleanupIntervalS || 3600;
|
||||
return ['none', 'external'].indexOf($scope.currentFolder._guiVersioning.selector) === -1;
|
||||
};
|
||||
|
||||
function initVersioningEditing() {
|
||||
$scope.currentFolder._guiVersioning = angular.copy($scope.versioningDefaults);
|
||||
|
||||
var currentVersioning = $scope.currentFolder.versioning;
|
||||
|
||||
if (!currentVersioning || !currentVersioning.type || currentVersioning.type === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
// staggeredMaxAge can validly be zero, which we should not replace
|
||||
// with the default value of 365. So only set the default if it's
|
||||
// actually undefined.
|
||||
if (typeof $scope.currentFolder.staggeredMaxAge === 'undefined') {
|
||||
$scope.currentFolder.staggeredMaxAge = 365;
|
||||
$scope.currentFolder._guiVersioning.cleanupIntervalS = +currentVersioning.cleanupIntervalS;
|
||||
$scope.currentFolder._guiVersioning.selector = currentVersioning.type;
|
||||
|
||||
// Apply parameters currently in use
|
||||
switch (currentVersioning.type) {
|
||||
case "trashcan":
|
||||
$scope.currentFolder._guiVersioning.trashcanClean = +currentVersioning.params.cleanoutDays;
|
||||
break;
|
||||
case "simple":
|
||||
$scope.currentFolder._guiVersioning.simpleKeep = +currentVersioning.params.keep;
|
||||
$scope.currentFolder._guiVersioning.trashcanClean = +currentVersioning.params.cleanoutDays;
|
||||
break;
|
||||
case "staggered":
|
||||
$scope.currentFolder._guiVersioning.staggeredMaxAge = Math.floor(+currentVersioning.params.maxAge / 86400);
|
||||
$scope.currentFolder._guiVersioning.staggeredCleanInterval = +currentVersioning.params.cleanInterval;
|
||||
break;
|
||||
case "external":
|
||||
$scope.currentFolder._guiVersioning.externalCommand = currentVersioning.params.command;
|
||||
break;
|
||||
}
|
||||
$scope.currentFolder.externalCommand = $scope.currentFolder.externalCommand || "";
|
||||
};
|
||||
|
||||
$scope.editFolderExisting = function(folderCfg) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.currentFolder = angular.copy(folderCfg);
|
||||
|
||||
$scope.ignores.text = 'Loading...';
|
||||
$scope.ignores.error = null;
|
||||
@@ -1857,7 +1959,18 @@ angular.module('syncthing.core')
|
||||
$scope.emitHTTPError(err);
|
||||
});
|
||||
|
||||
$scope.editFolderModal();
|
||||
editFolder();
|
||||
};
|
||||
|
||||
$scope.editFolderDefaults = function() {
|
||||
$http.get(urlbase + '/config/defaults/folder')
|
||||
.success(function (data) {
|
||||
$scope.currentFolder = data;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = true;
|
||||
editFolder();
|
||||
})
|
||||
.error($scope.emitHTTPError);
|
||||
};
|
||||
|
||||
$scope.selectAllSharedDevices = function (state) {
|
||||
@@ -1876,37 +1989,43 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.addFolder = function () {
|
||||
$http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
||||
initShareEditing('folder');
|
||||
$scope.currentFolder.id = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
|
||||
$scope.currentSharing.unrelated = $scope.otherDevices();
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
$scope.editFolderModal();
|
||||
var folderID = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
|
||||
addFolderInit(folderID).then(function() {
|
||||
// Triggers the watch that sets the path
|
||||
$scope.currentFolder.label = $scope.currentFolder.label;
|
||||
editFolderModal();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addFolderAndShare = function (folder, folderLabel, device) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
||||
$scope.currentFolder.id = folder;
|
||||
$scope.currentFolder.label = folderLabel;
|
||||
$scope.currentFolder.viewFlags = {
|
||||
importFromOtherDevice: true
|
||||
};
|
||||
initShareEditing('folder');
|
||||
$scope.currentSharing.selected[device] = true;
|
||||
$scope.currentSharing.unrelated = $scope.deviceList().filter(function (n) {
|
||||
return n.deviceID !== $scope.myID;
|
||||
$scope.addFolderAndShare = function (folderID, folderLabel, device) {
|
||||
addFolderInit(folderID).then(function() {
|
||||
$scope.currentFolder.viewFlags = {
|
||||
importFromOtherDevice: true
|
||||
};
|
||||
$scope.currentSharing.selected[device] = true;
|
||||
$scope.currentFolder.label = folderLabel;
|
||||
editFolderModal();
|
||||
});
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
$scope.editFolderModal();
|
||||
};
|
||||
|
||||
function addFolderInit(folderID) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
return $http.get(urlbase + '/config/defaults/folder').then(function(p) {
|
||||
$scope.currentFolder = p.data;
|
||||
$scope.currentFolder.id = folderID;
|
||||
|
||||
initShareEditing('folder');
|
||||
$scope.currentSharing.unrelated = $scope.currentSharing.unrelated.concat($scope.currentSharing.shared);
|
||||
$scope.currentSharing.shared = [];
|
||||
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
}, $scope.emitHTTPError);
|
||||
}
|
||||
|
||||
$scope.shareFolderWithDevice = function (folder, device) {
|
||||
$scope.folders[folder].devices.push({
|
||||
deviceID: device
|
||||
@@ -1938,55 +2057,39 @@ angular.module('syncthing.core')
|
||||
folderCfg.devices = newDevices;
|
||||
delete $scope.currentSharing;
|
||||
|
||||
if (folderCfg.fileVersioningSelector === "trashcan") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'trashcan',
|
||||
'params': {
|
||||
'cleanoutDays': '' + folderCfg.trashcanClean
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.trashcanFileVersioning;
|
||||
delete folderCfg.trashcanClean;
|
||||
} else if (folderCfg.fileVersioningSelector === "simple") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'simple',
|
||||
'params': {
|
||||
'keep': '' + folderCfg.simpleKeep,
|
||||
'cleanoutDays': '' + folderCfg.trashcanClean
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.simpleFileVersioning;
|
||||
delete folderCfg.simpleKeep;
|
||||
} else if (folderCfg.fileVersioningSelector === "staggered") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'staggered',
|
||||
'params': {
|
||||
'maxAge': '' + (folderCfg.staggeredMaxAge * 86400),
|
||||
'cleanInterval': '' + folderCfg.staggeredCleanInterval,
|
||||
'versionsPath': '' + folderCfg.staggeredVersionsPath
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.staggeredFileVersioning;
|
||||
delete folderCfg.staggeredMaxAge;
|
||||
delete folderCfg.staggeredCleanInterval;
|
||||
delete folderCfg.staggeredVersionsPath;
|
||||
} else if (folderCfg.fileVersioningSelector === "external") {
|
||||
folderCfg.versioning = {
|
||||
'type': 'external',
|
||||
'params': {
|
||||
'command': '' + folderCfg.externalCommand
|
||||
},
|
||||
'cleanupIntervalS': folderCfg.versioningCleanupIntervalS
|
||||
};
|
||||
delete folderCfg.externalFileVersioning;
|
||||
delete folderCfg.externalCommand;
|
||||
} else {
|
||||
folderCfg.versioning.type = folderCfg._guiVersioning.selector;
|
||||
if ($scope.internalVersioningEnabled()) {
|
||||
folderCfg.versioning.cleanupIntervalS = folderCfg._guiVersioning.cleanupIntervalS;
|
||||
}
|
||||
switch (folderCfg._guiVersioning.selector) {
|
||||
case "trashcan":
|
||||
folderCfg.versioning.params.cleanoutDays = '' + folderCfg._guiVersioning.trashcanClean;
|
||||
break;
|
||||
case "simple":
|
||||
folderCfg.versioning.params.keep = '' + folderCfg._guiVersioning.simpleKeep,
|
||||
folderCfg.versioning.params.cleanoutDays = '' + folderCfg._guiVersioning.trashcanClean;
|
||||
break;
|
||||
case "staggered":
|
||||
folderCfg.versioning.params.maxAge = '' + (folderCfg._guiVersioning.staggeredMaxAge * 86400);
|
||||
folderCfg.versioning.params.cleanInterval = '' + folderCfg._guiVersioning.staggeredCleanInterval;
|
||||
break;
|
||||
case "external":
|
||||
folderCfg.versioning.params.command = '' + folderCfg._guiVersioning.externalCommand;
|
||||
break;
|
||||
default:
|
||||
delete folderCfg.versioning;
|
||||
}
|
||||
delete folderCfg._guiVersioning;
|
||||
|
||||
if ($scope.editingDefaults) {
|
||||
$scope.config.defaults.folder = folderCfg;
|
||||
$scope.saveConfig();
|
||||
} else {
|
||||
saveFolderExisting(folderCfg);
|
||||
}
|
||||
};
|
||||
|
||||
function saveFolderExisting(folderCfg) {
|
||||
var ignoresLoaded = !$scope.ignores.disabled;
|
||||
var ignores = $scope.ignores.text.split('\n');
|
||||
// Split always returns a minimum 1-length array even for no patterns
|
||||
@@ -2000,7 +2103,11 @@ angular.module('syncthing.core')
|
||||
$scope.folders[folderCfg.id] = folderCfg;
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
|
||||
if (ignoresLoaded && $scope.editingExisting && ignores !== folderCfg.ignores) {
|
||||
function arrayEquals(a, b) {
|
||||
return a.length === b.length && a.every(function(v, i) { return v === b[i] });
|
||||
}
|
||||
|
||||
if (ignoresLoaded && $scope.editingExisting && !arrayEquals(ignores, folderCfg.ignores)) {
|
||||
saveIgnores(ignores);
|
||||
};
|
||||
|
||||
@@ -2418,6 +2525,8 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.advanced = function () {
|
||||
$scope.advancedConfig = angular.copy($scope.config);
|
||||
$scope.advancedConfig.devices.sort(deviceCompare);
|
||||
$scope.advancedConfig.folders.sort(folderCompare);
|
||||
$('#advanced').modal('show');
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<modal id="editDevice" status="default" icon="{{editingExisting ? 'fas fa-pencil-alt' : 'fas fa-desktop'}}" heading="{{editingExisting ? 'Edit Device' : 'Add Device' | translate}} {{currentDevice.name}}" large="yes" closeable="yes">
|
||||
<modal id="editDevice" status="default" icon="{{editDeviceModalIcon()}}" heading="{{editDeviceModalTitle()}}" large="yes" closeable="yes">
|
||||
<div class="modal-body">
|
||||
<form role="form" name="deviceEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#device-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li ng-if="!editingDefaults"><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#device-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="device-general" class="tab-pane in active">
|
||||
<div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<label translate for="deviceID">Device ID</label>
|
||||
<div ng-if="!editingExisting">
|
||||
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
|
||||
@@ -38,7 +38,7 @@
|
||||
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-sharing" class="tab-pane">
|
||||
<div ng-if="!editingDefaults" id="device-sharing" class="tab-pane">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
@@ -151,17 +151,14 @@
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="saveDevice()" ng-disabled="deviceEditor.$invalid">
|
||||
<span class="fas fa-check"></span> <span translate>Save</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#idqr" ng-if="editingExisting || deviceEditor.deviceID.$valid">
|
||||
<button ng-if="!editingDefaults" type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#idqr" ng-if="editingExisting || deviceEditor.deviceID.$valid">
|
||||
<span class="fas fa-qrcode"></span> <span translate>Show QR</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<div ng-if="editingExisting" class="pull-left">
|
||||
<button type="button" class="btn btn-warning btn-sm disabled" ng-if="willBeReintroducedBy" tooltip data-original-title="This device will be reintroduced by {{ willBeReintroducedBy }}">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#remove-device-confirmation" ng-if="!willBeReintroducedBy">
|
||||
<div ng-if="editingExisting && !editingDefaults" class="pull-left">
|
||||
<button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#remove-device-confirmation">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<modal id="editFolder" status="default" icon="{{editingExisting ? 'fas fa-pencil-alt' : 'fas fa-folder'}}" heading="{{editingExisting ? 'Edit Folder' : 'Add Folder' | translate}} ({{folderLabel(currentFolder.id)}})" large="yes" closeable="yes">
|
||||
<modal id="editFolder" status="default" icon="{{editFolderModalIcon()}}" heading="{{editFolderModalTitle()}}" large="yes" closeable="yes">
|
||||
<div class="modal-body">
|
||||
<form role="form" name="folderEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(folderEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#folder-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-versioning"><span class="fas fa-copy"></span> <span translate>File Versioning</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-ignores"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li ng-if="!editingDefaults"><a data-toggle="tab" href="#folder-ignores"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="folder-general" class="tab-pane in active">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty && !editingDefaults}">
|
||||
<label for="folderLabel"><span translate>Folder Label</span></label>
|
||||
<input name="folderLabel" id="folderLabel" class="form-control" type="text" ng-model="currentFolder.label" value="{{currentFolder.label}}" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<label for="folderID"><span translate>Folder ID</span></label>
|
||||
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}" />
|
||||
<p class="help-block">
|
||||
@@ -28,19 +28,21 @@
|
||||
<span translate ng-show="!editingExisting">When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty && !editingDefaults}">
|
||||
<label translate for="folderPath">Folder Path</label>
|
||||
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required="" aria-required="true" path-is-sub-dir />
|
||||
<input name="folderPath" ng-readonly="editingExisting && !editingDefaults" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" ng-required="!editingDefaults" ng-aria-required="!editingDefaults" path-is-sub-dir />
|
||||
<datalist id="directory-list">
|
||||
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
|
||||
</datalist>
|
||||
<p class="help-block">
|
||||
<span ng-if="folderEditor.folderPath.$valid || folderEditor.folderPath.$pristine"><span translate>Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for</span> <code>{{system.tilde}}</code>.</br></span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty">The folder path cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty && !editingDefaults">The folder path cannot be blank.</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length == 0">Warning, this path is a subdirectory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length != 0">Warning, this path is a subdirectory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.isParent && folderPathErrors.otherLabel.length == 0">Warning, this path is a parent directory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.isParent && folderPathErrors.otherLabel.length != 0">Warning, this path is a parent directory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
<span ng-if="folderPathErrors.isParent && !editingDefaults">
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.otherLabel.length == 0">Warning, this path is a parent directory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.otherLabel.length != 0">Warning, this path is a parent directory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,7 +78,7 @@
|
||||
<div id="folder-versioning" class="tab-pane">
|
||||
<div class="form-group">
|
||||
<label translate>File Versioning</label> <a href="https://docs.syncthing.net/users/versioning.html" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Help</span></a>
|
||||
<select class="form-control" ng-model="currentFolder.fileVersioningSelector">
|
||||
<select class="form-control" ng-model="currentFolder._guiVersioning.selector">
|
||||
<option value="none" translate>No File Versioning</option>
|
||||
<option value="trashcan" translate>Trash Can File Versioning</option>
|
||||
<option value="simple" translate>Simple File Versioning</option>
|
||||
@@ -84,69 +86,69 @@
|
||||
<option value="external" translate>External File Versioning</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='trashcan' || currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.trashcanClean.$invalid && folderEditor.trashcanClean.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='trashcan' || currentFolder._guiVersioning.selectorector=='simple'" ng-class="{'has-error': folderEditor._guiVersioning.trashcanClean.$invalid && folderEditor._guiVersioning.trashcanClean.$dirty}">
|
||||
<p translate class="help-block">Files are moved to .stversions directory when replaced or deleted by Syncthing.</p>
|
||||
<label translate for="trashcanClean">Clean out after</label>
|
||||
<div class="input-group">
|
||||
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required="" aria-required="true" min="0" />
|
||||
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder._guiVersioning.trashcanClean" required="" aria-required="true" min="0" />
|
||||
<div class="input-group-addon" translate>days</div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.trashcanClean.$valid || folderEditor.trashcanClean.$pristine">The number of days to keep files in the trash can. Zero means forever.</span>
|
||||
<span translate ng-if="folderEditor.trashcanClean.$error.required && folderEditor.trashcanClean.$dirty">The number of days must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.trashcanClean.$error.min && folderEditor.trashcanClean.$dirty">A negative number of days doesn't make sense.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$valid || folderEditor._guiVersioning.trashcanClean.$pristine">The number of days to keep files in the trash can. Zero means forever.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$error.required && folderEditor._guiVersioning.trashcanClean.$dirty">The number of days must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.trashcanClean.$error.min && folderEditor._guiVersioning.trashcanClean.$dirty">A negative number of days doesn't make sense.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='simple'" ng-class="{'has-error': folderEditor._guiVersioning.simpleKeep.$invalid && folderEditor._guiVersioning.simpleKeep.$dirty}">
|
||||
<p translate class="help-block">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</p>
|
||||
<label translate for="simpleKeep">Keep Versions</label>
|
||||
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required="" aria-required="true" min="1" />
|
||||
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder._guiVersioning.simpleKeep" required="" aria-required="true" min="1" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.simpleKeep.$valid || folderEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
|
||||
<span translate ng-if="folderEditor.simpleKeep.$error.required && folderEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.simpleKeep.$error.min && folderEditor.simpleKeep.$dirty">You must keep at least one version.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$valid || folderEditor._guiVersioning.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$error.required && folderEditor._guiVersioning.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.simpleKeep.$error.min && folderEditor._guiVersioning.simpleKeep.$dirty">You must keep at least one version.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='staggered'" ng-class="{'has-error': folderEditor.staggeredMaxAge.$invalid && folderEditor.staggeredMaxAge.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='staggered'" ng-class="{'has-error': folderEditor._guiVersioning.staggeredMaxAge.$invalid && folderEditor._guiVersioning.staggeredMaxAge.$dirty}">
|
||||
<p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p>
|
||||
<p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p>
|
||||
<label translate for="staggeredMaxAge">Maximum Age</label>
|
||||
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required="" aria-required="true" min="0" />
|
||||
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder._guiVersioning.staggeredMaxAge" required="" aria-required="true" min="0" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$valid || folderEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.staggeredMaxAge.$error.min && folderEditor.staggeredMaxAge.$dirty">A negative number of days doesn't make sense.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$valid || folderEditor._guiVersioning.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$error.required && folderEditor._guiVersioning.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.staggeredMaxAge.$error.min && folderEditor._guiVersioning.staggeredMaxAge.$dirty">A negative number of days doesn't make sense.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
|
||||
<label translate for="staggeredVersionsPath">Versions Path</label>
|
||||
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath" />
|
||||
<div class="form-group" ng-if="internalVersioningEnabled()">
|
||||
<label translate for="fsPath">Versions Path</label>
|
||||
<input name="fsPath" id="fsPath" class="form-control" type="text" ng-model="currentFolder.versioning.fsPath" />
|
||||
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
|
||||
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
|
||||
<p translate class="help-block">An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.</p>
|
||||
<label translate for="externalCommand">Command</label>
|
||||
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder.externalCommand" required="" aria-required="true" />
|
||||
<input name="externalCommand" id="externalCommand" class="form-control" type="text" ng-model="currentFolder._guiVersioning.externalCommand" required="" aria-required="true" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.externalCommand.$valid || folderEditor.externalCommand.$pristine">See external versioning help for supported templated command line parameters.</span>
|
||||
<span translate ng-if="folderEditor.externalCommand.$error.required && folderEditor.externalCommand.$dirty">The path cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentFolder.fileVersioningSelector != 'none'" ng-class="{'has-error': folderEditor.versioningCleanupIntervalS.$invalid && folderEditor.versioningCleanupIntervalS.$dirty}">
|
||||
<div class="form-group" ng-if="internalVersioningEnabled()" ng-class="{'has-error': folderEditor._guiVersioning.cleanupIntervalS.$invalid && folderEditor._guiVersioning.cleanupIntervalS.$dirty}">
|
||||
<label translate for="versioningCleanupIntervalS">Cleanup Interval</label>
|
||||
<div class="input-group">
|
||||
<input name="versioningCleanupIntervalS" id="versioningCleanupIntervalS" class="form-control text-right" type="number" ng-model="currentFolder.versioningCleanupIntervalS" required="" min="0" max="31536000" aria-required="true" />
|
||||
<input name="versioningCleanupIntervalS" id="versioningCleanupIntervalS" class="form-control text-right" type="number" ng-model="currentFolder._guiVersioning.cleanupIntervalS" required="" min="0" max="31536000" aria-required="true" />
|
||||
<div class="input-group-addon" translate>seconds</div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$valid || folderEditor.versioningCleanupIntervalS.$pristine"class="help-block">The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.</span>
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$error.required && folderEditor.versioningCleanupIntervalS.$dirty">The cleanup interval cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.versioningCleanupIntervalS.$error.min && folderEditor.versioningCleanupIntervalS.$dirty">The interval must be a positive number of seconds.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$valid || folderEditor._guiVersioning.cleanupIntervalS.$pristine"class="help-block">The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$error.required && folderEditor._guiVersioning.cleanupIntervalS.$dirty">The cleanup interval cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor._guiVersioning.cleanupIntervalS.$error.min && folderEditor._guiVersioning.cleanupIntervalS.$dirty">The interval must be a positive number of seconds.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="folder-ignores" class="tab-pane">
|
||||
<div ng-if="!editingDefaults" id="folder-ignores" class="tab-pane">
|
||||
<p translate>Enter ignore patterns, one per line.</p>
|
||||
<div ng-class="{'has-error': ignores.error != null}">
|
||||
<textarea class="form-control" rows="5" ng-model="ignores.text" ng-disabled="ignores.disabled"></textarea>
|
||||
@@ -271,7 +273,7 @@
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingExisting">
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingExisting && !editingDefaults">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -294,7 +294,6 @@ func (s *service) Serve(ctx context.Context) error {
|
||||
Router: restMux,
|
||||
id: s.id,
|
||||
cfg: s.cfg,
|
||||
mut: sync.NewMutex(),
|
||||
}
|
||||
|
||||
configBuilder.registerConfig("/rest/config")
|
||||
@@ -303,6 +302,8 @@ func (s *service) Serve(ctx context.Context) error {
|
||||
configBuilder.registerDevices("/rest/config/devices")
|
||||
configBuilder.registerFolder("/rest/config/folders/:id")
|
||||
configBuilder.registerDevice("/rest/config/devices/:id")
|
||||
configBuilder.registerDefaultFolder("/rest/config/defaults/folder")
|
||||
configBuilder.registerDefaultDevice("/rest/config/defaults/device")
|
||||
configBuilder.registerOptions("/rest/config/options")
|
||||
configBuilder.registerLDAP("/rest/config/ldap")
|
||||
configBuilder.registerGUI("/rest/config/gui")
|
||||
@@ -711,14 +712,19 @@ func (s *service) getDBBrowse(w http.ResponseWriter, r *http.Request) {
|
||||
qs := r.URL.Query()
|
||||
folder := qs.Get("folder")
|
||||
prefix := qs.Get("prefix")
|
||||
dirsonly := qs.Get("dirsonly") != ""
|
||||
dirsOnly := qs.Get("dirsonly") != ""
|
||||
|
||||
levels, err := strconv.Atoi(qs.Get("levels"))
|
||||
if err != nil {
|
||||
levels = -1
|
||||
}
|
||||
result, err := s.model.GlobalDirectoryTree(folder, prefix, levels, dirsOnly)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
sendJSON(w, s.model.GlobalDirectoryTree(folder, prefix, levels, dirsonly))
|
||||
sendJSON(w, result)
|
||||
}
|
||||
|
||||
func (s *service) getDBCompletion(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -1218,7 +1224,7 @@ func (s *service) getDBIgnores(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
folder := qs.Get("folder")
|
||||
|
||||
lines, patterns, err := s.model.GetIgnores(folder)
|
||||
lines, patterns, err := s.model.LoadIgnores(folder)
|
||||
if err != nil && !ignore.IsParseError(err) {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
@@ -1402,31 +1408,36 @@ func (s *service) makeDevicePauseHandler(paused bool) http.HandlerFunc {
|
||||
var qs = r.URL.Query()
|
||||
var deviceStr = qs.Get("device")
|
||||
|
||||
var cfgs []config.DeviceConfiguration
|
||||
|
||||
if deviceStr == "" {
|
||||
for _, cfg := range s.cfg.Devices() {
|
||||
cfg.Paused = paused
|
||||
cfgs = append(cfgs, cfg)
|
||||
var msg string
|
||||
var status int
|
||||
_, err := s.cfg.Modify(func(cfg *config.Configuration) {
|
||||
if deviceStr == "" {
|
||||
for i := range cfg.Devices {
|
||||
cfg.Devices[i].Paused = paused
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
device, err := protocol.DeviceIDFromString(deviceStr)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
msg = err.Error()
|
||||
status = 500
|
||||
return
|
||||
}
|
||||
|
||||
cfg, ok := s.cfg.Devices()[device]
|
||||
_, i, ok := cfg.Device(device)
|
||||
if !ok {
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
msg = "not found"
|
||||
status = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
|
||||
cfg.Paused = paused
|
||||
cfgs = append(cfgs, cfg)
|
||||
}
|
||||
cfg.Devices[i].Paused = paused
|
||||
})
|
||||
|
||||
if _, err := s.cfg.SetDevices(cfgs); err != nil {
|
||||
if msg != "" {
|
||||
http.Error(w, msg, status)
|
||||
} else if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
}
|
||||
@@ -1534,7 +1545,7 @@ func (s *service) postFolderVersionsRestore(w http.ResponseWriter, r *http.Reque
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
sendJSON(w, ferr)
|
||||
sendJSON(w, errorStringMap(ferr))
|
||||
}
|
||||
|
||||
func (s *service) getFolderErrors(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -1828,6 +1839,14 @@ func shouldRegenerateCertificate(cert tls.Certificate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func errorStringMap(errs map[string]error) map[string]*string {
|
||||
out := make(map[string]*string, len(errs))
|
||||
for s, e := range errs {
|
||||
out[s] = errorString(e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func errorString(err error) *string {
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
|
||||
@@ -1253,6 +1253,11 @@ func TestConfigChanges(t *testing.T) {
|
||||
defer os.Remove(tmpFile.Name())
|
||||
w := config.Wrap(tmpFile.Name(), cfg, protocol.LocalDeviceID, events.NoopLogger)
|
||||
tmpFile.Close()
|
||||
if cfgService, ok := w.(suture.Service); ok {
|
||||
cfgCtx, cfgCancel := context.WithCancel(context.Background())
|
||||
go cfgService.Serve(cfgCtx)
|
||||
defer cfgCancel()
|
||||
}
|
||||
baseURL, cancel, err := startHTTP(w)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error from getting base URL:", err)
|
||||
@@ -1260,7 +1265,7 @@ func TestConfigChanges(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
cli := &http.Client{
|
||||
Timeout: time.Second,
|
||||
Timeout: time.Minute,
|
||||
}
|
||||
|
||||
do := func(req *http.Request, status int) *http.Response {
|
||||
|
||||
@@ -17,14 +17,12 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
type configMuxBuilder struct {
|
||||
*httprouter.Router
|
||||
id protocol.DeviceID
|
||||
cfg config.Wrapper
|
||||
mut sync.Mutex
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) registerConfig(path string) {
|
||||
@@ -59,14 +57,14 @@ func (c *configMuxBuilder) registerFolders(path string) {
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
var folders []config.FolderConfiguration
|
||||
if err := unmarshalTo(r.Body, &folders); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetFolders(folders)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetFolders(folders)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -75,19 +73,7 @@ func (c *configMuxBuilder) registerFolders(path string) {
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
var folder config.FolderConfiguration
|
||||
if err := unmarshalTo(r.Body, &folder); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetFolder(folder)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.finish(w, waiter)
|
||||
c.adjustFolder(w, r, config.FolderConfiguration{}, false)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,14 +83,14 @@ func (c *configMuxBuilder) registerDevices(path string) {
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
var devices []config.DeviceConfiguration
|
||||
if err := unmarshalTo(r.Body, &devices); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetDevices(devices)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevices(devices)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -113,14 +99,14 @@ func (c *configMuxBuilder) registerDevices(path string) {
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
var device config.DeviceConfiguration
|
||||
if err := unmarshalTo(r.Body, &device); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetDevice(device)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevice(device)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -140,7 +126,7 @@ func (c *configMuxBuilder) registerFolder(path string) {
|
||||
})
|
||||
|
||||
c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
c.adjustFolder(w, r, config.FolderConfiguration{})
|
||||
c.adjustFolder(w, r, config.FolderConfiguration{}, false)
|
||||
})
|
||||
|
||||
c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
@@ -149,12 +135,10 @@ func (c *configMuxBuilder) registerFolder(path string) {
|
||||
http.Error(w, "No folder with given ID", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
c.adjustFolder(w, r, folder)
|
||||
c.adjustFolder(w, r, folder, false)
|
||||
})
|
||||
|
||||
c.Handle(http.MethodDelete, path, func(w http.ResponseWriter, _ *http.Request, p httprouter.Params) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
waiter, err := c.cfg.RemoveFolder(p.ByName("id"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -186,18 +170,16 @@ func (c *configMuxBuilder) registerDevice(path string) {
|
||||
})
|
||||
|
||||
c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
c.adjustDevice(w, r, config.DeviceConfiguration{})
|
||||
c.adjustDevice(w, r, config.DeviceConfiguration{}, false)
|
||||
})
|
||||
|
||||
c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
if device, ok := deviceFromParams(w, p); ok {
|
||||
c.adjustDevice(w, r, device)
|
||||
c.adjustDevice(w, r, device, false)
|
||||
}
|
||||
})
|
||||
|
||||
c.Handle(http.MethodDelete, path, func(w http.ResponseWriter, _ *http.Request, p httprouter.Params) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
id, err := protocol.DeviceIDFromString(p.ByName("id"))
|
||||
waiter, err := c.cfg.RemoveDevice(id)
|
||||
if err != nil {
|
||||
@@ -208,6 +190,34 @@ func (c *configMuxBuilder) registerDevice(path string) {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) registerDefaultFolder(path string) {
|
||||
c.HandlerFunc(http.MethodGet, path, func(w http.ResponseWriter, _ *http.Request) {
|
||||
sendJSON(w, c.cfg.DefaultFolder())
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.adjustFolder(w, r, config.FolderConfiguration{}, true)
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.adjustFolder(w, r, c.cfg.DefaultFolder(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) registerDefaultDevice(path string) {
|
||||
c.HandlerFunc(http.MethodGet, path, func(w http.ResponseWriter, _ *http.Request) {
|
||||
sendJSON(w, c.cfg.DefaultDevice())
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.adjustDevice(w, r, config.DeviceConfiguration{}, true)
|
||||
})
|
||||
|
||||
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
|
||||
c.adjustDevice(w, r, c.cfg.DefaultDevice(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) registerOptions(path string) {
|
||||
c.HandlerFunc(http.MethodGet, path, func(w http.ResponseWriter, _ *http.Request) {
|
||||
sendJSON(w, c.cfg.Options())
|
||||
@@ -251,36 +261,45 @@ func (c *configMuxBuilder) registerGUI(path string) {
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustConfig(w http.ResponseWriter, r *http.Request) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
cfg, err := config.ReadJSON(r.Body, c.id)
|
||||
to, err := config.ReadJSON(r.Body, c.id)
|
||||
r.Body.Close()
|
||||
if err != nil {
|
||||
l.Warnln("Decoding posted config:", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if cfg.GUI.Password, err = checkGUIPassword(c.cfg.GUI().Password, cfg.GUI.Password); err != nil {
|
||||
l.Warnln("bcrypting password:", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.Replace(cfg)
|
||||
if err != nil {
|
||||
var errMsg string
|
||||
var status int
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
if to.GUI.Password, err = checkGUIPassword(cfg.GUI.Password, to.GUI.Password); err != nil {
|
||||
l.Warnln("bcrypting password:", err)
|
||||
errMsg = err.Error()
|
||||
status = http.StatusInternalServerError
|
||||
return
|
||||
}
|
||||
*cfg = to
|
||||
})
|
||||
if errMsg != "" {
|
||||
http.Error(w, errMsg, status)
|
||||
} else if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.finish(w, waiter)
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustFolder(w http.ResponseWriter, r *http.Request, folder config.FolderConfiguration) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
func (c *configMuxBuilder) adjustFolder(w http.ResponseWriter, r *http.Request, folder config.FolderConfiguration, defaults bool) {
|
||||
if err := unmarshalTo(r.Body, &folder); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetFolder(folder)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
if defaults {
|
||||
cfg.Defaults.Folder = folder
|
||||
} else {
|
||||
cfg.SetFolder(folder)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -288,14 +307,18 @@ func (c *configMuxBuilder) adjustFolder(w http.ResponseWriter, r *http.Request,
|
||||
c.finish(w, waiter)
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustDevice(w http.ResponseWriter, r *http.Request, device config.DeviceConfiguration) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
func (c *configMuxBuilder) adjustDevice(w http.ResponseWriter, r *http.Request, device config.DeviceConfiguration, defaults bool) {
|
||||
if err := unmarshalTo(r.Body, &device); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetDevice(device)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
if defaults {
|
||||
cfg.Defaults.Device = device
|
||||
} else {
|
||||
cfg.SetDevice(device)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -304,13 +327,13 @@ func (c *configMuxBuilder) adjustDevice(w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustOptions(w http.ResponseWriter, r *http.Request, opts config.OptionsConfiguration) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
if err := unmarshalTo(r.Body, &opts); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetOptions(opts)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.Options = opts
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -319,21 +342,26 @@ func (c *configMuxBuilder) adjustOptions(w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustGUI(w http.ResponseWriter, r *http.Request, gui config.GUIConfiguration) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
oldPassword := gui.Password
|
||||
err := unmarshalTo(r.Body, &gui)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if gui.Password, err = checkGUIPassword(oldPassword, gui.Password); err != nil {
|
||||
l.Warnln("bcrypting password:", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetGUI(gui)
|
||||
if err != nil {
|
||||
var errMsg string
|
||||
var status int
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
if gui.Password, err = checkGUIPassword(oldPassword, gui.Password); err != nil {
|
||||
l.Warnln("bcrypting password:", err)
|
||||
errMsg = err.Error()
|
||||
status = http.StatusInternalServerError
|
||||
return
|
||||
}
|
||||
cfg.GUI = gui
|
||||
})
|
||||
if errMsg != "" {
|
||||
http.Error(w, errMsg, status)
|
||||
} else if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -341,13 +369,13 @@ func (c *configMuxBuilder) adjustGUI(w http.ResponseWriter, r *http.Request, gui
|
||||
}
|
||||
|
||||
func (c *configMuxBuilder) adjustLDAP(w http.ResponseWriter, r *http.Request, ldap config.LDAPConfiguration) {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
if err := unmarshalTo(r.Body, &ldap); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.SetLDAP(ldap)
|
||||
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.LDAP = ldap
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
@@ -28,10 +28,6 @@ func (c *mockedConfig) LDAP() config.LDAPConfiguration {
|
||||
return config.LDAPConfiguration{}
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetLDAP(config.LDAPConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) RawCopy() config.Configuration {
|
||||
cfg := config.Configuration{}
|
||||
util.SetDefaults(&cfg.Options)
|
||||
@@ -42,11 +38,13 @@ func (c *mockedConfig) Options() config.OptionsConfiguration {
|
||||
return config.OptionsConfiguration{}
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Replace(cfg config.Configuration) (config.Waiter, error) {
|
||||
func (c *mockedConfig) Modify(config.ModifyFunction) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Subscribe(cm config.Committer) {}
|
||||
func (c *mockedConfig) Subscribe(cm config.Committer) config.Configuration {
|
||||
return config.Configuration{}
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Unsubscribe(cm config.Committer) {}
|
||||
|
||||
@@ -62,14 +60,6 @@ func (c *mockedConfig) DeviceList() []config.DeviceConfiguration {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetDevice(config.DeviceConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetDevices([]config.DeviceConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Save() error {
|
||||
return nil
|
||||
}
|
||||
@@ -86,14 +76,6 @@ func (c *mockedConfig) ConfigPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetGUI(gui config.GUIConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetOptions(opts config.OptionsConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Folder(id string) (config.FolderConfiguration, bool) {
|
||||
return config.FolderConfiguration{}, false
|
||||
}
|
||||
@@ -102,14 +84,6 @@ func (c *mockedConfig) FolderList() []config.FolderConfiguration {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetFolder(fld config.FolderConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetFolders(folders []config.FolderConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) RemoveFolder(id string) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
@@ -117,7 +91,6 @@ func (c *mockedConfig) RemoveFolder(id string) (config.Waiter, error) {
|
||||
func (c *mockedConfig) FolderPasswords(device protocol.DeviceID) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) Device(id protocol.DeviceID) (config.DeviceConfiguration, bool) {
|
||||
return config.DeviceConfiguration{}, false
|
||||
}
|
||||
@@ -138,6 +111,22 @@ func (c *mockedConfig) IgnoredFolder(device protocol.DeviceID, folder string) bo
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *mockedConfig) DefaultFolder() config.FolderConfiguration {
|
||||
return config.FolderConfiguration{}
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetDefaultFolder(config.FolderConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) DefaultDevice() config.DeviceConfiguration {
|
||||
return config.DeviceConfiguration{}
|
||||
}
|
||||
|
||||
func (c *mockedConfig) SetDefaultDevice(config.DeviceConfiguration) (config.Waiter, error) {
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
func (c *mockedConfig) GlobalDiscoveryServers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
type mockedModel struct{}
|
||||
|
||||
func (m *mockedModel) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} {
|
||||
return nil
|
||||
func (m *mockedModel) GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly bool) ([]*model.TreeEntry, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockedModel) Completion(device protocol.DeviceID, folder string) model.FolderCompletion {
|
||||
@@ -80,7 +80,11 @@ func (m *mockedModel) Availability(folder string, file protocol.FileInfo, block
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockedModel) GetIgnores(folder string) ([]string, []string, error) {
|
||||
func (m *mockedModel) LoadIgnores(folder string) ([]string, []string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (m *mockedModel) CurrentIgnores(folder string) ([]string, []string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
@@ -92,7 +96,7 @@ func (m *mockedModel) GetFolderVersions(folder string) (map[string][]versioner.F
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockedModel) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) {
|
||||
func (m *mockedModel) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]error, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,20 @@ func (validationError) String() string {
|
||||
return "validationError"
|
||||
}
|
||||
|
||||
func TestReplaceCommit(t *testing.T) {
|
||||
t.Skip("broken, fails randomly, #3834")
|
||||
func replace(t testing.TB, w Wrapper, to Configuration) {
|
||||
t.Helper()
|
||||
waiter, err := w.Modify(func(cfg *Configuration) {
|
||||
*cfg = to
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
waiter.Wait()
|
||||
}
|
||||
|
||||
func TestReplaceCommit(t *testing.T) {
|
||||
w := wrap("/dev/null", Configuration{Version: 0}, device1)
|
||||
defer w.stop()
|
||||
if w.RawCopy().Version != 0 {
|
||||
t.Fatal("Config incorrect")
|
||||
}
|
||||
@@ -52,10 +62,7 @@ func TestReplaceCommit(t *testing.T) {
|
||||
// Replace config. We should get back a clean response and the config
|
||||
// should change.
|
||||
|
||||
_, err := w.Replace(Configuration{Version: 1})
|
||||
if err != nil {
|
||||
t.Fatal("Should not have a validation error:", err)
|
||||
}
|
||||
replace(t, w, Configuration{Version: 1})
|
||||
if w.RequiresRestart() {
|
||||
t.Fatal("Should not require restart")
|
||||
}
|
||||
@@ -69,11 +76,7 @@ func TestReplaceCommit(t *testing.T) {
|
||||
sub0 := requiresRestart{committed: make(chan struct{}, 1)}
|
||||
w.Subscribe(sub0)
|
||||
|
||||
_, err = w.Replace(Configuration{Version: 2})
|
||||
if err != nil {
|
||||
t.Fatal("Should not have a validation error:", err)
|
||||
}
|
||||
|
||||
replace(t, w, Configuration{Version: 1})
|
||||
<-sub0.committed
|
||||
if !w.RequiresRestart() {
|
||||
t.Fatal("Should require restart")
|
||||
@@ -87,7 +90,9 @@ func TestReplaceCommit(t *testing.T) {
|
||||
|
||||
w.Subscribe(validationError{})
|
||||
|
||||
_, err = w.Replace(Configuration{Version: 3})
|
||||
_, err := w.Modify(func(cfg *Configuration) {
|
||||
*cfg = Configuration{Version: 3}
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have a validation error")
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
|
||||
const (
|
||||
OldestHandledVersion = 10
|
||||
CurrentVersion = 33
|
||||
CurrentVersion = 35
|
||||
MaxRescanIntervalS = 365 * 24 * 60 * 60
|
||||
)
|
||||
|
||||
@@ -234,6 +234,8 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) error {
|
||||
|
||||
cfg.prepareIgnoredDevices(existingDevices)
|
||||
|
||||
cfg.Defaults.prepare(myID, existingDevices)
|
||||
|
||||
cfg.removeDeprecatedProtocols()
|
||||
|
||||
util.FillNilExceptDeprecated(cfg)
|
||||
@@ -245,7 +247,6 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) error {
|
||||
}
|
||||
|
||||
func (cfg *Configuration) ensureMyDevice(myID protocol.DeviceID) {
|
||||
// Ensure this device is present in the config
|
||||
for _, device := range cfg.Devices {
|
||||
if device.DeviceID == myID {
|
||||
return
|
||||
@@ -372,6 +373,15 @@ func (cfg *Configuration) applyMigrations() {
|
||||
migrationsMut.Unlock()
|
||||
}
|
||||
|
||||
func (cfg *Configuration) Device(id protocol.DeviceID) (DeviceConfiguration, int, bool) {
|
||||
for i, device := range cfg.Devices {
|
||||
if device.DeviceID == id {
|
||||
return device, i, true
|
||||
}
|
||||
}
|
||||
return DeviceConfiguration{}, 0, false
|
||||
}
|
||||
|
||||
// DeviceMap returns a map of device ID to device configuration for the given configuration.
|
||||
func (cfg *Configuration) DeviceMap() map[protocol.DeviceID]DeviceConfiguration {
|
||||
m := make(map[protocol.DeviceID]DeviceConfiguration, len(cfg.Devices))
|
||||
@@ -381,6 +391,44 @@ func (cfg *Configuration) DeviceMap() map[protocol.DeviceID]DeviceConfiguration
|
||||
return m
|
||||
}
|
||||
|
||||
func (cfg *Configuration) SetDevice(device DeviceConfiguration) {
|
||||
cfg.SetDevices([]DeviceConfiguration{device})
|
||||
}
|
||||
|
||||
func (cfg *Configuration) SetDevices(devices []DeviceConfiguration) {
|
||||
inds := make(map[protocol.DeviceID]int, len(cfg.Devices))
|
||||
for i, device := range cfg.Devices {
|
||||
inds[device.DeviceID] = i
|
||||
}
|
||||
filtered := devices[:0]
|
||||
for _, device := range devices {
|
||||
if i, ok := inds[device.DeviceID]; ok {
|
||||
cfg.Devices[i] = device
|
||||
} else {
|
||||
filtered = append(filtered, device)
|
||||
}
|
||||
}
|
||||
cfg.Devices = append(cfg.Devices, filtered...)
|
||||
}
|
||||
|
||||
func (cfg *Configuration) Folder(id string) (FolderConfiguration, int, bool) {
|
||||
for i, folder := range cfg.Folders {
|
||||
if folder.ID == id {
|
||||
return folder, i, true
|
||||
}
|
||||
}
|
||||
return FolderConfiguration{}, 0, false
|
||||
}
|
||||
|
||||
// FolderMap returns a map of folder ID to folder configuration for the given configuration.
|
||||
func (cfg *Configuration) FolderMap() map[string]FolderConfiguration {
|
||||
m := make(map[string]FolderConfiguration, len(cfg.Folders))
|
||||
for _, folder := range cfg.Folders {
|
||||
m[folder.ID] = folder
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// FolderPasswords returns the folder passwords set for this device, for
|
||||
// folders that have an encryption password set.
|
||||
func (cfg Configuration) FolderPasswords(device protocol.DeviceID) map[string]string {
|
||||
@@ -397,6 +445,26 @@ nextFolder:
|
||||
return res
|
||||
}
|
||||
|
||||
func (cfg *Configuration) SetFolder(folder FolderConfiguration) {
|
||||
cfg.SetFolders([]FolderConfiguration{folder})
|
||||
}
|
||||
|
||||
func (cfg *Configuration) SetFolders(folders []FolderConfiguration) {
|
||||
inds := make(map[string]int, len(cfg.Folders))
|
||||
for i, folder := range cfg.Folders {
|
||||
inds[folder.ID] = i
|
||||
}
|
||||
filtered := folders[:0]
|
||||
for _, folder := range folders {
|
||||
if i, ok := inds[folder.ID]; ok {
|
||||
cfg.Folders[i] = folder
|
||||
} else {
|
||||
filtered = append(filtered, folder)
|
||||
}
|
||||
}
|
||||
cfg.Folders = append(cfg.Folders, filtered...)
|
||||
}
|
||||
|
||||
func ensureDevicePresent(devices []FolderDeviceConfiguration, myID protocol.DeviceID) []FolderDeviceConfiguration {
|
||||
for _, device := range devices {
|
||||
if device.DeviceID.Equals(myID) {
|
||||
@@ -519,3 +587,19 @@ func getFreePort(host string, ports ...int) (int, error) {
|
||||
c.Close()
|
||||
return addr.Port, nil
|
||||
}
|
||||
|
||||
func (defaults *Defaults) prepare(myID protocol.DeviceID, existingDevices map[protocol.DeviceID]bool) {
|
||||
ensureZeroForNodefault(&FolderConfiguration{}, &defaults.Folder)
|
||||
ensureZeroForNodefault(&DeviceConfiguration{}, &defaults.Device)
|
||||
defaults.Folder.prepare(myID, existingDevices)
|
||||
defaults.Device.prepare(nil)
|
||||
}
|
||||
|
||||
func ensureZeroForNodefault(empty interface{}, target interface{}) {
|
||||
util.CopyMatchingTag(empty, target, "nodefault", func(v string) bool {
|
||||
if len(v) > 0 && v != "true" {
|
||||
panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "true"`, v))
|
||||
}
|
||||
return len(v) > 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ type Configuration struct {
|
||||
Options OptionsConfiguration `protobuf:"bytes,6,opt,name=options,proto3" json:"options" xml:"options"`
|
||||
IgnoredDevices []ObservedDevice `protobuf:"bytes,7,rep,name=ignored_devices,json=ignoredDevices,proto3" json:"remoteIgnoredDevices" xml:"remoteIgnoredDevice"`
|
||||
DeprecatedPendingDevices []ObservedDevice `protobuf:"bytes,8,rep,name=pending_devices,json=pendingDevices,proto3" json:"-" xml:"pendingDevice,omitempty"` // Deprecated: Do not use.
|
||||
Defaults Defaults `protobuf:"bytes,9,opt,name=defaults,proto3" json:"defaults" xml:"defaults"`
|
||||
}
|
||||
|
||||
func (m *Configuration) Reset() { *m = Configuration{} }
|
||||
@@ -67,50 +68,94 @@ func (m *Configuration) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_Configuration proto.InternalMessageInfo
|
||||
|
||||
type Defaults struct {
|
||||
Folder FolderConfiguration `protobuf:"bytes,1,opt,name=folder,proto3" json:"folder" xml:"folder"`
|
||||
Device DeviceConfiguration `protobuf:"bytes,2,opt,name=device,proto3" json:"device" xml:"device"`
|
||||
}
|
||||
|
||||
func (m *Defaults) Reset() { *m = Defaults{} }
|
||||
func (m *Defaults) String() string { return proto.CompactTextString(m) }
|
||||
func (*Defaults) ProtoMessage() {}
|
||||
func (*Defaults) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_baadf209193dc627, []int{1}
|
||||
}
|
||||
func (m *Defaults) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Defaults) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Defaults.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *Defaults) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Defaults.Merge(m, src)
|
||||
}
|
||||
func (m *Defaults) XXX_Size() int {
|
||||
return m.ProtoSize()
|
||||
}
|
||||
func (m *Defaults) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Defaults.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Defaults proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Configuration)(nil), "config.Configuration")
|
||||
proto.RegisterType((*Defaults)(nil), "config.Defaults")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("lib/config/config.proto", fileDescriptor_baadf209193dc627) }
|
||||
|
||||
var fileDescriptor_baadf209193dc627 = []byte{
|
||||
// 575 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4d, 0x8b, 0xd3, 0x5e,
|
||||
0x14, 0xc6, 0x93, 0x7f, 0x3b, 0xed, 0x7f, 0x32, 0x6f, 0x10, 0x45, 0x53, 0x5f, 0x72, 0x6b, 0xa8,
|
||||
0x52, 0x65, 0xec, 0xc0, 0xb8, 0x11, 0x77, 0xd6, 0xe2, 0x58, 0x14, 0x1c, 0x02, 0x23, 0xea, 0x46,
|
||||
0xda, 0xe6, 0x4e, 0x7a, 0xa1, 0xcd, 0x0d, 0xc9, 0x4d, 0x99, 0xf9, 0x08, 0xee, 0xc4, 0x4f, 0xe0,
|
||||
0xd6, 0x6f, 0xd2, 0x5d, 0xbb, 0x70, 0xe1, 0xea, 0xc2, 0xb4, 0xbb, 0x2c, 0xb3, 0x74, 0x25, 0xf7,
|
||||
0xad, 0x26, 0x10, 0x5d, 0x35, 0xe7, 0x3c, 0xcf, 0xf9, 0x9d, 0xc3, 0xd3, 0xc4, 0xb8, 0x39, 0x41,
|
||||
0xc3, 0xa3, 0x11, 0x0e, 0xce, 0x91, 0x2f, 0x7f, 0x3a, 0x61, 0x84, 0x09, 0x36, 0x6b, 0xa2, 0xba,
|
||||
0xd5, 0xca, 0x19, 0xce, 0xf1, 0xc4, 0x83, 0x91, 0x28, 0x92, 0x68, 0x40, 0x10, 0x0e, 0x84, 0xbb,
|
||||
0xe0, 0xf2, 0xe0, 0x0c, 0x8d, 0x60, 0x99, 0xeb, 0x5e, 0xce, 0xe5, 0x27, 0xa8, 0xcc, 0xe2, 0xe4,
|
||||
0x2c, 0x13, 0x6f, 0x10, 0x96, 0x79, 0xee, 0xe7, 0x3c, 0x38, 0x64, 0x42, 0x5c, 0x66, 0x6b, 0xe4,
|
||||
0x6d, 0xc3, 0x18, 0x46, 0x33, 0xe8, 0x49, 0x69, 0x1b, 0x5e, 0x10, 0xf1, 0xe8, 0xfc, 0xa8, 0x19,
|
||||
0x7b, 0x2f, 0xf2, 0xd3, 0xa6, 0x6b, 0xd4, 0x67, 0x30, 0x8a, 0x11, 0x0e, 0x2c, 0xbd, 0xa9, 0xb7,
|
||||
0xb7, 0xba, 0x4f, 0x53, 0x0a, 0x54, 0x2b, 0xa3, 0xc0, 0xbc, 0x98, 0x4e, 0x9e, 0x39, 0xb2, 0x3e,
|
||||
0x1c, 0x10, 0x12, 0x39, 0xbf, 0x28, 0xa8, 0xa0, 0x80, 0xa4, 0x8b, 0xd6, 0x6e, 0xbe, 0xef, 0xaa,
|
||||
0x29, 0xf3, 0x9d, 0x51, 0x17, 0xe1, 0xc5, 0xd6, 0x7f, 0xcd, 0x4a, 0x7b, 0xe7, 0xf8, 0x76, 0x47,
|
||||
0xa6, 0xfd, 0x92, 0xb7, 0x0b, 0x17, 0x74, 0xc1, 0x9c, 0x02, 0x8d, 0x2d, 0x95, 0x33, 0x19, 0x05,
|
||||
0xbb, 0x7c, 0xa9, 0xa8, 0x1d, 0x57, 0x09, 0x8c, 0x2b, 0xe2, 0x8e, 0xad, 0x4a, 0x91, 0xdb, 0xe3,
|
||||
0xed, 0xbf, 0x70, 0xe5, 0xcc, 0x86, 0x2b, 0x6a, 0xc7, 0x55, 0x82, 0xe9, 0x1a, 0x15, 0x3f, 0x41,
|
||||
0x56, 0xb5, 0xa9, 0xb7, 0x77, 0x8e, 0x2d, 0xc5, 0x3c, 0x39, 0xeb, 0x17, 0x81, 0x0f, 0x18, 0x70,
|
||||
0x45, 0x41, 0xe5, 0xe4, 0xac, 0x9f, 0x52, 0xc0, 0x66, 0x32, 0x0a, 0xb6, 0x39, 0xd3, 0x4f, 0x90,
|
||||
0xf3, 0x75, 0xd9, 0x62, 0x92, 0xcb, 0x04, 0xf3, 0x83, 0x51, 0x65, 0xff, 0xa8, 0xb5, 0xc5, 0xa1,
|
||||
0x0d, 0x05, 0x7d, 0xd3, 0x7b, 0x7e, 0x5a, 0xa4, 0x3e, 0x92, 0xd4, 0x2a, 0x93, 0x52, 0x0a, 0xf8,
|
||||
0x58, 0x46, 0x81, 0xc1, 0xb9, 0xac, 0x60, 0x60, 0xae, 0xba, 0x5c, 0x33, 0xdf, 0x1b, 0x75, 0xf9,
|
||||
0x22, 0x58, 0x35, 0x4e, 0xbf, 0xa3, 0xe8, 0x6f, 0x45, 0xbb, 0xb8, 0xa0, 0xa9, 0x72, 0x90, 0x43,
|
||||
0x19, 0x05, 0x7b, 0x9c, 0x2d, 0x6b, 0xc7, 0x55, 0x8a, 0xf9, 0x5d, 0x37, 0x0e, 0x90, 0x1f, 0xe0,
|
||||
0x08, 0x7a, 0x9f, 0x54, 0xd2, 0x75, 0x9e, 0xf4, 0x8d, 0xcd, 0x0a, 0xf9, 0x6e, 0x89, 0xc4, 0xbb,
|
||||
0x63, 0x09, 0xbf, 0x1e, 0xc1, 0x29, 0x26, 0xb0, 0x2f, 0x86, 0x7b, 0x9b, 0xc4, 0x1b, 0x7c, 0x53,
|
||||
0x89, 0xe8, 0xa4, 0x8b, 0xd6, 0xb5, 0x92, 0x7e, 0xb6, 0x68, 0x95, 0xb2, 0xdc, 0x7d, 0x54, 0xa8,
|
||||
0xcd, 0xcf, 0xba, 0x71, 0x10, 0xc2, 0xc0, 0x43, 0x81, 0xbf, 0xb9, 0xf5, 0xff, 0x7f, 0xde, 0xfa,
|
||||
0x4a, 0x26, 0x6d, 0xf5, 0x60, 0x18, 0xc1, 0xd1, 0x80, 0x40, 0xef, 0x54, 0x00, 0x24, 0x33, 0xa5,
|
||||
0x40, 0x7f, 0x9c, 0x51, 0x70, 0x97, 0x1f, 0x1d, 0xe6, 0xb5, 0x43, 0x3c, 0x45, 0x04, 0x4e, 0x43,
|
||||
0x72, 0xe9, 0x58, 0xba, 0xbb, 0x5f, 0xd0, 0xe2, 0xee, 0xeb, 0xf9, 0x95, 0xad, 0x2d, 0xaf, 0x6c,
|
||||
0x6d, 0xbe, 0xb2, 0xf5, 0xe5, 0xca, 0xd6, 0xbf, 0xac, 0x6d, 0xed, 0xdb, 0xda, 0xd6, 0x97, 0x6b,
|
||||
0x5b, 0xfb, 0xb9, 0xb6, 0xb5, 0x8f, 0x0f, 0x7d, 0x44, 0xc6, 0xc9, 0xb0, 0x33, 0xc2, 0xd3, 0xa3,
|
||||
0xf8, 0x32, 0x18, 0x91, 0x31, 0x0a, 0xfc, 0xdc, 0xd3, 0x9f, 0xaf, 0x77, 0x58, 0xe3, 0x9f, 0xea,
|
||||
0x93, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x97, 0x39, 0xe5, 0x72, 0xad, 0x04, 0x00, 0x00,
|
||||
// 654 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcd, 0x6e, 0xd3, 0x40,
|
||||
0x10, 0xc7, 0xed, 0xa6, 0x4d, 0xda, 0xed, 0x17, 0x32, 0x08, 0x5c, 0x3e, 0xbc, 0x61, 0x15, 0x50,
|
||||
0x41, 0xa5, 0x95, 0xca, 0x05, 0x71, 0x23, 0x44, 0x94, 0x0a, 0x24, 0x2a, 0xa3, 0x22, 0xe0, 0x82,
|
||||
0x92, 0x78, 0xeb, 0xae, 0x94, 0xd8, 0x96, 0xbd, 0xae, 0xda, 0x47, 0xe0, 0x86, 0x78, 0x02, 0x4e,
|
||||
0x48, 0xdc, 0x79, 0x88, 0xdc, 0x92, 0x23, 0xa7, 0x95, 0x9a, 0xdc, 0x7c, 0xf4, 0x91, 0x13, 0xda,
|
||||
0x0f, 0xbb, 0xb6, 0x6a, 0xe0, 0x64, 0xcf, 0xfc, 0xff, 0xf3, 0x9b, 0xd5, 0x78, 0xc7, 0xe0, 0xc6,
|
||||
0x80, 0xf4, 0x76, 0xfa, 0xbe, 0x77, 0x44, 0x5c, 0xf5, 0xd8, 0x0e, 0x42, 0x9f, 0xfa, 0x46, 0x5d,
|
||||
0x46, 0x37, 0x5b, 0x05, 0xc3, 0x91, 0x3f, 0x70, 0x70, 0x28, 0x83, 0x38, 0xec, 0x52, 0xe2, 0x7b,
|
||||
0xd2, 0x5d, 0x72, 0x39, 0xf8, 0x84, 0xf4, 0x71, 0x95, 0xeb, 0x6e, 0xc1, 0xe5, 0xc6, 0xa4, 0xca,
|
||||
0x82, 0x0a, 0x96, 0x81, 0xd3, 0x0d, 0xaa, 0x3c, 0xf7, 0x0a, 0x1e, 0x3f, 0xe0, 0x42, 0x54, 0x65,
|
||||
0xdb, 0x28, 0xda, 0x7a, 0x11, 0x0e, 0x4f, 0xb0, 0xa3, 0xa4, 0x25, 0x7c, 0x4a, 0xe5, 0x2b, 0xfa,
|
||||
0xde, 0x00, 0xab, 0xcf, 0x8b, 0xd5, 0x86, 0x0d, 0x1a, 0x27, 0x38, 0x8c, 0x88, 0xef, 0x99, 0x7a,
|
||||
0x53, 0xdf, 0x5c, 0x68, 0x3f, 0x49, 0x18, 0xcc, 0x52, 0x29, 0x83, 0xc6, 0xe9, 0x70, 0xf0, 0x14,
|
||||
0xa9, 0x78, 0xab, 0x4b, 0x69, 0x88, 0x7e, 0x33, 0x58, 0x23, 0x1e, 0x4d, 0xc6, 0xad, 0x95, 0x62,
|
||||
0xde, 0xce, 0xaa, 0x8c, 0x77, 0xa0, 0x21, 0x87, 0x17, 0x99, 0x73, 0xcd, 0xda, 0xe6, 0xf2, 0xee,
|
||||
0xad, 0x6d, 0x35, 0xed, 0x17, 0x22, 0x5d, 0x3a, 0x41, 0x1b, 0x8e, 0x18, 0xd4, 0x78, 0x53, 0x55,
|
||||
0x93, 0x32, 0xb8, 0x22, 0x9a, 0xca, 0x18, 0xd9, 0x99, 0xc0, 0xb9, 0x72, 0xdc, 0x91, 0x59, 0x2b,
|
||||
0x73, 0x3b, 0x22, 0xfd, 0x17, 0xae, 0xaa, 0xc9, 0xb9, 0x32, 0x46, 0x76, 0x26, 0x18, 0x36, 0xa8,
|
||||
0xb9, 0x31, 0x31, 0xe7, 0x9b, 0xfa, 0xe6, 0xf2, 0xae, 0x99, 0x31, 0xf7, 0x0e, 0xf7, 0xcb, 0xc0,
|
||||
0xfb, 0x1c, 0x38, 0x65, 0xb0, 0xb6, 0x77, 0xb8, 0x9f, 0x30, 0xc8, 0x6b, 0x52, 0x06, 0x97, 0x04,
|
||||
0xd3, 0x8d, 0x09, 0xfa, 0x3a, 0x69, 0x71, 0xc9, 0xe6, 0x82, 0xf1, 0x01, 0xcc, 0xf3, 0x2f, 0x6a,
|
||||
0x2e, 0x08, 0xe8, 0x46, 0x06, 0x7d, 0xdd, 0x79, 0x76, 0x50, 0xa6, 0x3e, 0x54, 0xd4, 0x79, 0x2e,
|
||||
0x25, 0x0c, 0x8a, 0xb2, 0x94, 0x41, 0x20, 0xb8, 0x3c, 0xe0, 0x60, 0xa1, 0xda, 0x42, 0x33, 0xde,
|
||||
0x83, 0x86, 0xba, 0x08, 0x66, 0x5d, 0xd0, 0x6f, 0x67, 0xf4, 0x37, 0x32, 0x5d, 0x6e, 0xd0, 0xcc,
|
||||
0xe6, 0xa0, 0x8a, 0x52, 0x06, 0x57, 0x05, 0x5b, 0xc5, 0xc8, 0xce, 0x14, 0xe3, 0x87, 0x0e, 0xd6,
|
||||
0x89, 0xeb, 0xf9, 0x21, 0x76, 0x3e, 0x65, 0x93, 0x6e, 0x88, 0x49, 0x5f, 0xcf, 0x5b, 0xa8, 0xbb,
|
||||
0x25, 0x27, 0xde, 0x3e, 0x56, 0xf0, 0x6b, 0x21, 0x1e, 0xfa, 0x14, 0xef, 0xcb, 0xe2, 0x4e, 0x3e,
|
||||
0xf1, 0x0d, 0xd1, 0xa9, 0x42, 0x44, 0xc9, 0xb8, 0x75, 0xb5, 0x22, 0x9f, 0x8e, 0x5b, 0x95, 0x2c,
|
||||
0x7b, 0x8d, 0x94, 0x62, 0xe3, 0xb3, 0x0e, 0xd6, 0x03, 0xec, 0x39, 0xc4, 0x73, 0xf3, 0xb3, 0x2e,
|
||||
0xfe, 0xf3, 0xac, 0x2f, 0xd5, 0xa4, 0xcd, 0x0e, 0x0e, 0x42, 0xdc, 0xef, 0x52, 0xec, 0x1c, 0x48,
|
||||
0x80, 0x62, 0x26, 0x0c, 0xea, 0x8f, 0x52, 0x06, 0xef, 0x88, 0x43, 0x07, 0x45, 0x6d, 0xcb, 0x1f,
|
||||
0x12, 0x8a, 0x87, 0x01, 0x3d, 0x43, 0xa6, 0x6e, 0xaf, 0x95, 0xb4, 0xc8, 0x38, 0x00, 0x8b, 0x0e,
|
||||
0x3e, 0xea, 0xc6, 0x03, 0x1a, 0x99, 0x4b, 0xe2, 0x93, 0x5c, 0xb9, 0xb8, 0x99, 0x32, 0xdf, 0x46,
|
||||
0x6a, 0x52, 0xb9, 0x33, 0x65, 0x70, 0x4d, 0xdd, 0x47, 0x99, 0x40, 0x76, 0xae, 0xa1, 0x9f, 0x3a,
|
||||
0x58, 0xcc, 0x4a, 0x8d, 0xb7, 0xa0, 0x2e, 0x57, 0x40, 0xac, 0xe8, 0x7f, 0xd6, 0xc9, 0x52, 0x7d,
|
||||
0x54, 0xc9, 0xa5, 0x6d, 0x52, 0x79, 0x0e, 0x95, 0x63, 0x33, 0xe7, 0xca, 0xd0, 0xaa, 0x5d, 0xca,
|
||||
0xa1, 0xb2, 0xe4, 0xd2, 0x2a, 0xa9, 0x7c, 0xfb, 0xd5, 0xe8, 0xdc, 0xd2, 0x26, 0xe7, 0x96, 0x36,
|
||||
0x9a, 0x5a, 0xfa, 0x64, 0x6a, 0xe9, 0x5f, 0x66, 0x96, 0xf6, 0x6d, 0x66, 0xe9, 0x93, 0x99, 0xa5,
|
||||
0xfd, 0x9a, 0x59, 0xda, 0xc7, 0x07, 0x2e, 0xa1, 0xc7, 0x71, 0x6f, 0xbb, 0xef, 0x0f, 0x77, 0xa2,
|
||||
0x33, 0xaf, 0x4f, 0x8f, 0x89, 0xe7, 0x16, 0xde, 0x2e, 0x7e, 0x63, 0xbd, 0xba, 0xf8, 0x67, 0x3d,
|
||||
0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x50, 0xe9, 0xd5, 0x50, 0xb6, 0x05, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Configuration) Marshal() (dAtA []byte, err error) {
|
||||
@@ -133,6 +178,16 @@ func (m *Configuration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
{
|
||||
size, err := m.Defaults.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintConfig(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x4a
|
||||
if len(m.DeprecatedPendingDevices) > 0 {
|
||||
for iNdEx := len(m.DeprecatedPendingDevices) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
@@ -227,6 +282,49 @@ func (m *Configuration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *Defaults) Marshal() (dAtA []byte, err error) {
|
||||
size := m.ProtoSize()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Defaults) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.ProtoSize()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Defaults) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
{
|
||||
size, err := m.Device.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintConfig(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
{
|
||||
size, err := m.Folder.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintConfig(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintConfig(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovConfig(v)
|
||||
base := offset
|
||||
@@ -277,6 +375,21 @@ func (m *Configuration) ProtoSize() (n int) {
|
||||
n += 1 + l + sovConfig(uint64(l))
|
||||
}
|
||||
}
|
||||
l = m.Defaults.ProtoSize()
|
||||
n += 1 + l + sovConfig(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Defaults) ProtoSize() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = m.Folder.ProtoSize()
|
||||
n += 1 + l + sovConfig(uint64(l))
|
||||
l = m.Device.ProtoSize()
|
||||
n += 1 + l + sovConfig(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -569,6 +682,158 @@ func (m *Configuration) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 9:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Defaults", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowConfig
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.Defaults.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipConfig(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Defaults) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowConfig
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Defaults: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Defaults: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Folder", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowConfig
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.Folder.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowConfig
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthConfig
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.Device.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipConfig(dAtA[iNdEx:])
|
||||
|
||||
@@ -8,9 +8,11 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -21,6 +23,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/d4l3k/messagediff"
|
||||
"github.com/thejerf/suture/v4"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
@@ -37,52 +40,93 @@ func init() {
|
||||
}
|
||||
|
||||
func TestDefaultValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
RawListenAddresses: []string{"default"},
|
||||
RawGlobalAnnServers: []string{"default"},
|
||||
GlobalAnnEnabled: true,
|
||||
LocalAnnEnabled: true,
|
||||
LocalAnnPort: 21027,
|
||||
LocalAnnMCAddr: "[ff12::8384]:21027",
|
||||
MaxSendKbps: 0,
|
||||
MaxRecvKbps: 0,
|
||||
ReconnectIntervalS: 60,
|
||||
RelaysEnabled: true,
|
||||
RelayReconnectIntervalM: 10,
|
||||
StartBrowser: true,
|
||||
NATEnabled: true,
|
||||
NATLeaseM: 60,
|
||||
NATRenewalM: 30,
|
||||
NATTimeoutS: 10,
|
||||
RestartOnWakeup: true,
|
||||
AutoUpgradeIntervalH: 12,
|
||||
KeepTemporariesH: 24,
|
||||
CacheIgnoredFiles: false,
|
||||
ProgressUpdateIntervalS: 5,
|
||||
LimitBandwidthInLan: false,
|
||||
MinHomeDiskFree: Size{1, "%"},
|
||||
URURL: "https://data.syncthing.net/newdata",
|
||||
URInitialDelayS: 1800,
|
||||
URPostInsecurely: false,
|
||||
ReleasesURL: "https://upgrades.syncthing.net/meta.json",
|
||||
AlwaysLocalNets: []string{},
|
||||
OverwriteRemoteDevNames: false,
|
||||
TempIndexMinBlocks: 10,
|
||||
UnackedNotificationIDs: []string{"authenticationUserAndPassword"},
|
||||
DefaultFolderPath: "~",
|
||||
SetLowPriority: true,
|
||||
CRURL: "https://crash.syncthing.net/newcrash",
|
||||
CREnabled: true,
|
||||
StunKeepaliveStartS: 180,
|
||||
StunKeepaliveMinS: 20,
|
||||
RawStunServers: []string{"default"},
|
||||
AnnounceLANAddresses: true,
|
||||
FeatureFlags: []string{},
|
||||
size, err := ParseSize("1%")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := Configuration{
|
||||
Version: CurrentVersion,
|
||||
Folders: []FolderConfiguration{},
|
||||
Options: OptionsConfiguration{
|
||||
RawListenAddresses: []string{"default"},
|
||||
RawGlobalAnnServers: []string{"default"},
|
||||
GlobalAnnEnabled: true,
|
||||
LocalAnnEnabled: true,
|
||||
LocalAnnPort: 21027,
|
||||
LocalAnnMCAddr: "[ff12::8384]:21027",
|
||||
MaxSendKbps: 0,
|
||||
MaxRecvKbps: 0,
|
||||
ReconnectIntervalS: 60,
|
||||
RelaysEnabled: true,
|
||||
RelayReconnectIntervalM: 10,
|
||||
StartBrowser: true,
|
||||
NATEnabled: true,
|
||||
NATLeaseM: 60,
|
||||
NATRenewalM: 30,
|
||||
NATTimeoutS: 10,
|
||||
RestartOnWakeup: true,
|
||||
AutoUpgradeIntervalH: 12,
|
||||
KeepTemporariesH: 24,
|
||||
CacheIgnoredFiles: false,
|
||||
ProgressUpdateIntervalS: 5,
|
||||
LimitBandwidthInLan: false,
|
||||
MinHomeDiskFree: Size{1, "%"},
|
||||
URURL: "https://data.syncthing.net/newdata",
|
||||
URInitialDelayS: 1800,
|
||||
URPostInsecurely: false,
|
||||
ReleasesURL: "https://upgrades.syncthing.net/meta.json",
|
||||
AlwaysLocalNets: []string{},
|
||||
OverwriteRemoteDevNames: false,
|
||||
TempIndexMinBlocks: 10,
|
||||
UnackedNotificationIDs: []string{"authenticationUserAndPassword"},
|
||||
SetLowPriority: true,
|
||||
CRURL: "https://crash.syncthing.net/newcrash",
|
||||
CREnabled: true,
|
||||
StunKeepaliveStartS: 180,
|
||||
StunKeepaliveMinS: 20,
|
||||
RawStunServers: []string{"default"},
|
||||
AnnounceLANAddresses: true,
|
||||
FeatureFlags: []string{},
|
||||
},
|
||||
Defaults: Defaults{
|
||||
Folder: FolderConfiguration{
|
||||
FilesystemType: fs.FilesystemTypeBasic,
|
||||
Path: "~",
|
||||
Type: FolderTypeSendReceive,
|
||||
Devices: []FolderDeviceConfiguration{{DeviceID: device1}},
|
||||
RescanIntervalS: 3600,
|
||||
FSWatcherEnabled: true,
|
||||
FSWatcherDelayS: 10,
|
||||
IgnorePerms: false,
|
||||
AutoNormalize: true,
|
||||
MinDiskFree: size,
|
||||
Versioning: VersioningConfiguration{
|
||||
CleanupIntervalS: 3600,
|
||||
Params: map[string]string{},
|
||||
},
|
||||
MaxConflicts: 10,
|
||||
WeakHashThresholdPct: 25,
|
||||
MarkerName: ".stfolder",
|
||||
MaxConcurrentWrites: 2,
|
||||
},
|
||||
Device: DeviceConfiguration{
|
||||
Addresses: []string{"dynamic"},
|
||||
AllowedNetworks: []string{},
|
||||
Compression: protocol.CompressionMetadata,
|
||||
IgnoredFolders: []ObservedFolder{},
|
||||
},
|
||||
},
|
||||
IgnoredDevices: []ObservedDevice{},
|
||||
}
|
||||
expected.Devices = []DeviceConfiguration{expected.Defaults.Device.Copy()}
|
||||
expected.Devices[0].DeviceID = device1
|
||||
expected.Devices[0].Name, _ = os.Hostname()
|
||||
|
||||
cfg := New(device1)
|
||||
cfg.GUI = GUIConfiguration{}
|
||||
cfg.LDAP = LDAPConfiguration{}
|
||||
|
||||
if diff, equal := messagediff.PrettyDiff(expected, cfg.Options); !equal {
|
||||
if diff, equal := messagediff.PrettyDiff(expected, cfg); !equal {
|
||||
t.Errorf("Default config differs. Diff:\n%s", diff)
|
||||
}
|
||||
}
|
||||
@@ -95,7 +139,8 @@ func TestDeviceConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
os.RemoveAll(filepath.Join("testdata", DefaultMarkerName))
|
||||
wr, err := load(cfgFile, device1)
|
||||
wr, wrCancel, err := copyAndLoad(cfgFile, device1)
|
||||
defer wrCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -107,7 +152,7 @@ func TestDeviceConfig(t *testing.T) {
|
||||
t.Fatal("Unexpected file")
|
||||
}
|
||||
|
||||
cfg := wr.(*wrapper).cfg
|
||||
cfg := wr.Wrapper.(*wrapper).cfg
|
||||
|
||||
expectedFolders := []FolderConfiguration{
|
||||
{
|
||||
@@ -170,7 +215,8 @@ func TestDeviceConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoListenAddresses(t *testing.T) {
|
||||
cfg, err := load("testdata/nolistenaddress.xml", device1)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/nolistenaddress.xml", device1)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -217,7 +263,6 @@ func TestOverriddenValues(t *testing.T) {
|
||||
OverwriteRemoteDevNames: true,
|
||||
TempIndexMinBlocks: 100,
|
||||
UnackedNotificationIDs: []string{"asdfasdf"},
|
||||
DefaultFolderPath: "/media/syncthing",
|
||||
SetLowPriority: false,
|
||||
CRURL: "https://localhost/newcrash",
|
||||
CREnabled: false,
|
||||
@@ -226,9 +271,11 @@ func TestOverriddenValues(t *testing.T) {
|
||||
RawStunServers: []string{"foo"},
|
||||
FeatureFlags: []string{"feature"},
|
||||
}
|
||||
expectedPath := "/media/syncthing"
|
||||
|
||||
os.Unsetenv("STNOUPGRADE")
|
||||
cfg, err := load("testdata/overridenvalues.xml", device1)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/overridenvalues.xml", device1)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -236,6 +283,10 @@ func TestOverriddenValues(t *testing.T) {
|
||||
if diff, equal := messagediff.PrettyDiff(expected, cfg.Options()); !equal {
|
||||
t.Errorf("Overridden config differs. Diff:\n%s", diff)
|
||||
}
|
||||
|
||||
if path := cfg.DefaultFolder().Path; path != expectedPath {
|
||||
t.Errorf("Default folder path is %v, expected %v", path, expectedPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceAddressesDynamic(t *testing.T) {
|
||||
@@ -269,7 +320,8 @@ func TestDeviceAddressesDynamic(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := load("testdata/deviceaddressesdynamic.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/deviceaddressesdynamic.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -314,7 +366,8 @@ func TestDeviceCompression(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := load("testdata/devicecompression.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/devicecompression.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -356,7 +409,8 @@ func TestDeviceAddressesStatic(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := load("testdata/deviceaddressesstatic.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/deviceaddressesstatic.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -368,7 +422,8 @@ func TestDeviceAddressesStatic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVersioningConfig(t *testing.T) {
|
||||
cfg, err := load("testdata/versioningconfig.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/versioningconfig.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -395,7 +450,8 @@ func TestIssue1262(t *testing.T) {
|
||||
t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux")
|
||||
}
|
||||
|
||||
cfg, err := load("testdata/issue-1262.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/issue-1262.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -409,7 +465,8 @@ func TestIssue1262(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue1750(t *testing.T) {
|
||||
cfg, err := load("testdata/issue-1750.xml", device4)
|
||||
cfg, cfgCancel, err := copyAndLoad("testdata/issue-1750.xml", device4)
|
||||
defer cfgCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -497,7 +554,7 @@ func TestFolderCheckPath(t *testing.T) {
|
||||
}
|
||||
|
||||
if err := cfg.CheckPath(); testcase.err != err {
|
||||
t.Errorf("unexpected error in case %s: %s != %s", testcase.path, err, testcase.err)
|
||||
t.Errorf("unexpected error in case %s: %s != %v", testcase.path, err, testcase.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,6 +562,7 @@ func TestFolderCheckPath(t *testing.T) {
|
||||
func TestNewSaveLoad(t *testing.T) {
|
||||
path := "testdata/temp.xml"
|
||||
os.Remove(path)
|
||||
defer os.Remove(path)
|
||||
|
||||
exists := func(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
@@ -513,6 +571,7 @@ func TestNewSaveLoad(t *testing.T) {
|
||||
|
||||
intCfg := New(device1)
|
||||
cfg := wrap(path, intCfg, device1)
|
||||
defer cfg.stop()
|
||||
|
||||
if exists(path) {
|
||||
t.Error(path, "exists")
|
||||
@@ -527,6 +586,7 @@ func TestNewSaveLoad(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg2, err := load(path, device1)
|
||||
defer cfg2.stop()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -534,8 +594,6 @@ func TestNewSaveLoad(t *testing.T) {
|
||||
if diff, equal := messagediff.PrettyDiff(cfg.RawCopy(), cfg2.RawCopy()); !equal {
|
||||
t.Errorf("Configs are not equal. Diff:\n%s", diff)
|
||||
}
|
||||
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
func TestPrepare(t *testing.T) {
|
||||
@@ -553,7 +611,8 @@ func TestPrepare(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
wrapper, err := load("testdata/example.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/example.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -592,7 +651,8 @@ func TestCopy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPullOrder(t *testing.T) {
|
||||
wrapper, err := load("testdata/pullorder.xml", device1)
|
||||
wrapper, wrapperCleanup, err := copyAndLoad("testdata/pullorder.xml", device1)
|
||||
defer wrapperCleanup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -632,8 +692,9 @@ func TestPullOrder(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wrapper = wrap("testdata/pullorder.xml", cfg, device1)
|
||||
folders = wrapper.Folders()
|
||||
wrapper2 := wrap(wrapper.ConfigPath(), cfg, device1)
|
||||
defer wrapper2.stop()
|
||||
folders = wrapper2.Folders()
|
||||
|
||||
for _, tc := range expected {
|
||||
if actual := folders[tc.name].Order; actual != tc.order {
|
||||
@@ -643,7 +704,8 @@ func TestPullOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLargeRescanInterval(t *testing.T) {
|
||||
wrapper, err := load("testdata/largeinterval.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/largeinterval.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -681,7 +743,8 @@ func TestGUIConfigURL(t *testing.T) {
|
||||
func TestDuplicateDevices(t *testing.T) {
|
||||
// Duplicate devices should be removed
|
||||
|
||||
wrapper, err := load("testdata/dupdevices.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/dupdevices.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -699,7 +762,8 @@ func TestDuplicateDevices(t *testing.T) {
|
||||
func TestDuplicateFolders(t *testing.T) {
|
||||
// Duplicate folders are a loading error
|
||||
|
||||
_, err := load("testdata/dupfolders.xml", device1)
|
||||
_, _Cancel, err := copyAndLoad("testdata/dupfolders.xml", device1)
|
||||
defer _Cancel()
|
||||
if err == nil || !strings.Contains(err.Error(), errFolderIDDuplicate.Error()) {
|
||||
t.Fatal(`Expected error to mention "duplicate folder ID":`, err)
|
||||
}
|
||||
@@ -710,7 +774,8 @@ func TestEmptyFolderPaths(t *testing.T) {
|
||||
// get messed up by the prepare steps (e.g., become the current dir or
|
||||
// get a slash added so that it becomes the root directory or similar).
|
||||
|
||||
_, err := load("testdata/nopath.xml", device1)
|
||||
_, _Cancel, err := copyAndLoad("testdata/nopath.xml", device1)
|
||||
defer _Cancel()
|
||||
if err == nil || !strings.Contains(err.Error(), errFolderPathEmpty.Error()) {
|
||||
t.Fatal("Expected error due to empty folder path, got", err)
|
||||
}
|
||||
@@ -779,7 +844,8 @@ func TestIgnoredDevices(t *testing.T) {
|
||||
// Verify that ignored devices that are also present in the
|
||||
// configuration are not in fact ignored.
|
||||
|
||||
wrapper, err := load("testdata/ignoreddevices.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoreddevices.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -797,7 +863,8 @@ func TestIgnoredFolders(t *testing.T) {
|
||||
// configuration are not in fact ignored.
|
||||
// Also, verify that folders that are shared with a device are not ignored.
|
||||
|
||||
wrapper, err := load("testdata/ignoredfolders.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoredfolders.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -833,7 +900,8 @@ func TestIgnoredFolders(t *testing.T) {
|
||||
func TestGetDevice(t *testing.T) {
|
||||
// Verify that the Device() call does the right thing
|
||||
|
||||
wrapper, err := load("testdata/ignoreddevices.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/ignoreddevices.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -860,7 +928,8 @@ func TestGetDevice(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
|
||||
wrapper, err := load("testdata/example.xml", device1)
|
||||
wrapper, wrapperCancel, err := copyAndLoad("testdata/example.xml", device1)
|
||||
defer wrapperCancel()
|
||||
if err != nil {
|
||||
t.Errorf("Failed: %s", err)
|
||||
}
|
||||
@@ -872,10 +941,7 @@ func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
|
||||
t.Error("Should have less devices")
|
||||
}
|
||||
|
||||
_, err = wrapper.Replace(raw)
|
||||
if err != nil {
|
||||
t.Errorf("Failed: %s", err)
|
||||
}
|
||||
replace(t, wrapper, raw)
|
||||
|
||||
raw = wrapper.RawCopy()
|
||||
if len(raw.Folders[0].Devices) > len(raw.Devices) {
|
||||
@@ -947,6 +1013,7 @@ func TestIssue4219(t *testing.T) {
|
||||
}
|
||||
|
||||
w := wrap("/tmp/cfg", cfg, myID)
|
||||
defer w.stop()
|
||||
if !w.IgnoredFolder(device2, "t1") {
|
||||
t.Error("Folder device2 t1 should be ignored")
|
||||
}
|
||||
@@ -1137,13 +1204,29 @@ func TestMaxConcurrentFolders(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func adjustDeviceConfiguration(cfg *DeviceConfiguration, id protocol.DeviceID, name string) {
|
||||
cfg.DeviceID = id
|
||||
cfg.Name = name
|
||||
}
|
||||
|
||||
func adjustFolderConfiguration(cfg *FolderConfiguration, id, label string, fsType fs.FilesystemType, path string) {
|
||||
cfg.ID = id
|
||||
cfg.Label = label
|
||||
cfg.FilesystemType = fsType
|
||||
cfg.Path = path
|
||||
}
|
||||
|
||||
// defaultConfigAsMap returns a valid default config as a JSON-decoded
|
||||
// map[string]interface{}. This is useful to override random elements and
|
||||
// re-encode into JSON.
|
||||
func defaultConfigAsMap() map[string]interface{} {
|
||||
cfg := New(device1)
|
||||
cfg.Devices = append(cfg.Devices, NewDeviceConfiguration(device2, "name"))
|
||||
cfg.Folders = append(cfg.Folders, NewFolderConfiguration(device1, "default", "default", fs.FilesystemTypeBasic, "/tmp"))
|
||||
dev := cfg.Defaults.Device.Copy()
|
||||
adjustDeviceConfiguration(&dev, device2, "name")
|
||||
cfg.Devices = append(cfg.Devices, dev)
|
||||
folder := cfg.Defaults.Folder.Copy()
|
||||
adjustFolderConfiguration(&folder, "default", "default", fs.FilesystemTypeBasic, "/tmp")
|
||||
cfg.Folders = append(cfg.Folders, folder)
|
||||
bs, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
// can't happen
|
||||
@@ -1157,13 +1240,80 @@ func defaultConfigAsMap() map[string]interface{} {
|
||||
return tmp
|
||||
}
|
||||
|
||||
func load(path string, myID protocol.DeviceID) (Wrapper, error) {
|
||||
cfg, _, err := Load(path, myID, events.NoopLogger)
|
||||
return cfg, err
|
||||
func copyToTmp(path string) (string, error) {
|
||||
orig, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer orig.Close()
|
||||
temp, err := ioutil.TempFile("", "syncthing-configTest-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer temp.Close()
|
||||
if _, err := io.Copy(temp, orig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return temp.Name(), nil
|
||||
}
|
||||
|
||||
func wrap(path string, cfg Configuration, myID protocol.DeviceID) Wrapper {
|
||||
return Wrap(path, cfg, myID, events.NoopLogger)
|
||||
func copyAndLoad(path string, myID protocol.DeviceID) (*testWrapper, func(), error) {
|
||||
temp, err := copyToTmp(path)
|
||||
if err != nil {
|
||||
return nil, func() {}, err
|
||||
}
|
||||
wrapper, err := load(temp, myID)
|
||||
if err != nil {
|
||||
return nil, func() {}, err
|
||||
}
|
||||
return wrapper, func() {
|
||||
wrapper.stop()
|
||||
os.Remove(temp)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func load(path string, myID protocol.DeviceID) (*testWrapper, error) {
|
||||
cfg, _, err := Load(path, myID, events.NoopLogger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return startWrapper(cfg), nil
|
||||
}
|
||||
|
||||
func wrap(path string, cfg Configuration, myID protocol.DeviceID) *testWrapper {
|
||||
wrapper := Wrap(path, cfg, myID, events.NoopLogger)
|
||||
return startWrapper(wrapper)
|
||||
}
|
||||
|
||||
type testWrapper struct {
|
||||
Wrapper
|
||||
cancel context.CancelFunc
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (w *testWrapper) stop() {
|
||||
w.cancel()
|
||||
<-w.done
|
||||
}
|
||||
|
||||
func startWrapper(wrapper Wrapper) *testWrapper {
|
||||
tw := &testWrapper{
|
||||
Wrapper: wrapper,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
s, ok := wrapper.(suture.Service)
|
||||
if !ok {
|
||||
tw.cancel = func() {}
|
||||
close(tw.done)
|
||||
return tw
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tw.cancel = cancel
|
||||
go func() {
|
||||
s.Serve(ctx)
|
||||
close(tw.done)
|
||||
}()
|
||||
return tw
|
||||
}
|
||||
|
||||
func TestInternalVersioningConfiguration(t *testing.T) {
|
||||
@@ -1171,7 +1321,9 @@ func TestInternalVersioningConfiguration(t *testing.T) {
|
||||
// reasonable.
|
||||
|
||||
cfg := New(device1)
|
||||
cfg.Folders = append(cfg.Folders, NewFolderConfiguration(device1, "default", "default", fs.FilesystemTypeBasic, "/tmp"))
|
||||
folder := cfg.Defaults.Folder.Copy()
|
||||
adjustFolderConfiguration(&folder, "default", "default", fs.FilesystemTypeBasic, "/tmp")
|
||||
cfg.Folders = append(cfg.Folders, folder)
|
||||
cfg.Folders[0].Versioning = VersioningConfiguration{
|
||||
Type: "foo",
|
||||
Params: map[string]string{"bar": "baz"},
|
||||
|
||||
@@ -8,23 +8,8 @@ package config
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/util"
|
||||
)
|
||||
|
||||
func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {
|
||||
d := DeviceConfiguration{
|
||||
DeviceID: id,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
util.SetDefaults(&d)
|
||||
|
||||
d.prepare(nil)
|
||||
return d
|
||||
}
|
||||
|
||||
func (cfg DeviceConfiguration) Copy() DeviceConfiguration {
|
||||
c := cfg
|
||||
c.Addresses = make([]string, len(cfg.Addresses))
|
||||
@@ -40,9 +25,6 @@ func (cfg *DeviceConfiguration) prepare(sharedFolders []string) {
|
||||
if len(cfg.Addresses) == 0 || len(cfg.Addresses) == 1 && cfg.Addresses[0] == "" {
|
||||
cfg.Addresses = []string{"dynamic"}
|
||||
}
|
||||
if len(cfg.AllowedNetworks) == 0 {
|
||||
cfg.AllowedNetworks = []string{}
|
||||
}
|
||||
|
||||
ignoredFolders := deduplicateObservedFoldersToMap(cfg.IgnoredFolders)
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ var _ = math.Inf
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type DeviceConfiguration struct {
|
||||
DeviceID github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"deviceID" xml:"id,attr"`
|
||||
DeviceID github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"deviceID" xml:"id,attr" nodefault:"true"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name" xml:"name,attr,omitempty"`
|
||||
Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses" xml:"address,omitempty" default:"dynamic"`
|
||||
Compression protocol.Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression" xml:"compression,attr"`
|
||||
CertName string `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"certName" xml:"certName,attr,omitempty"`
|
||||
Introducer bool `protobuf:"varint,6,opt,name=introducer,proto3" json:"introducer" xml:"introducer,attr"`
|
||||
SkipIntroductionRemovals bool `protobuf:"varint,7,opt,name=skip_introduction_removals,json=skipIntroductionRemovals,proto3" json:"skipIntroductionRemovals" xml:"skipIntroductionRemovals,attr"`
|
||||
IntroducedBy github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,8,opt,name=introduced_by,json=introducedBy,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"introducedBy" xml:"introducedBy,attr"`
|
||||
IntroducedBy github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,8,opt,name=introduced_by,json=introducedBy,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"introducedBy" xml:"introducedBy,attr" nodefault:"true"`
|
||||
Paused bool `protobuf:"varint,9,opt,name=paused,proto3" json:"paused" xml:"paused"`
|
||||
AllowedNetworks []string `protobuf:"bytes,10,rep,name=allowed_networks,json=allowedNetworks,proto3" json:"allowedNetworks" xml:"allowedNetwork,omitempty"`
|
||||
AutoAcceptFolders bool `protobuf:"varint,11,opt,name=auto_accept_folders,json=autoAcceptFolders,proto3" json:"autoAcceptFolders" xml:"autoAcceptFolders"`
|
||||
@@ -88,71 +88,72 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_744b782bd13071dd = []byte{
|
||||
// 1009 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbd, 0x6f, 0xdb, 0x46,
|
||||
0x1c, 0x15, 0xeb, 0xc4, 0xb6, 0xce, 0x96, 0x65, 0xd3, 0x88, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35,
|
||||
0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xa3, 0x2d, 0x50, 0xc6, 0x68, 0x63, 0x18, 0x4d, 0xdc, 0x2b,
|
||||
0xba, 0x78, 0x61, 0x49, 0xde, 0x59, 0x39, 0x58, 0xfc, 0x28, 0x79, 0x54, 0x2c, 0xa0, 0x43, 0xc7,
|
||||
0x16, 0xe8, 0x50, 0x64, 0xed, 0x52, 0x74, 0xe8, 0xd0, 0xff, 0xa3, 0x80, 0x37, 0x6b, 0x2c, 0x3a,
|
||||
0x1c, 0x10, 0x7b, 0xe3, 0xc8, 0x31, 0x53, 0x71, 0x47, 0x8a, 0x3a, 0xca, 0x71, 0x50, 0x20, 0xdb,
|
||||
0xdd, 0x7b, 0xef, 0xde, 0xef, 0x83, 0xbf, 0x3b, 0x82, 0xee, 0x90, 0x3a, 0x3b, 0x6e, 0xe0, 0x9f,
|
||||
0xd0, 0xc1, 0x0e, 0x26, 0x23, 0xea, 0x92, 0x7c, 0x93, 0x44, 0x36, 0xa3, 0x81, 0xdf, 0x0f, 0xa3,
|
||||
0x80, 0x05, 0xfa, 0x62, 0x0e, 0x6e, 0x6f, 0x09, 0xb5, 0x84, 0xdc, 0x60, 0xb8, 0xe3, 0x90, 0x30,
|
||||
0xe7, 0xb7, 0xef, 0x29, 0x2e, 0x81, 0x13, 0x93, 0x68, 0x44, 0x70, 0x41, 0xd5, 0xc9, 0x19, 0xcb,
|
||||
0x97, 0x9d, 0xbf, 0xd7, 0xc1, 0xe6, 0xbe, 0x8c, 0xf1, 0x58, 0x8d, 0xa1, 0xff, 0xa5, 0x81, 0x7a,
|
||||
0x1e, 0xdb, 0xa2, 0xd8, 0xd0, 0xda, 0x5a, 0x6f, 0xd5, 0xfc, 0x45, 0x3b, 0xe7, 0xb0, 0xf6, 0x2f,
|
||||
0x87, 0x1f, 0x0f, 0x28, 0x7b, 0x9e, 0x38, 0x7d, 0x37, 0xf0, 0x76, 0xe2, 0xb1, 0xef, 0xb2, 0xe7,
|
||||
0xd4, 0x1f, 0x28, 0x2b, 0x35, 0xa3, 0x7e, 0xee, 0x7e, 0xb0, 0x7f, 0xc9, 0xe1, 0xf2, 0x74, 0x9d,
|
||||
0x72, 0xb8, 0x8c, 0x8b, 0x75, 0xc6, 0x61, 0xe3, 0xcc, 0x1b, 0xee, 0x75, 0x28, 0x7e, 0x68, 0x33,
|
||||
0x16, 0x75, 0xd2, 0x8b, 0xee, 0x52, 0xb1, 0xce, 0x2e, 0xba, 0xa5, 0xee, 0xa7, 0x49, 0x57, 0x7b,
|
||||
0x39, 0xe9, 0x96, 0x1e, 0x68, 0xca, 0x60, 0xfd, 0x08, 0xdc, 0xf2, 0x6d, 0x8f, 0x18, 0xef, 0xb5,
|
||||
0xb5, 0x5e, 0xdd, 0xfc, 0x24, 0xe5, 0x50, 0xee, 0x33, 0x0e, 0xef, 0x49, 0x67, 0xb1, 0x91, 0x7e,
|
||||
0x0f, 0x03, 0x8f, 0x32, 0xe2, 0x85, 0x6c, 0x2c, 0xa2, 0x6c, 0xbe, 0x01, 0x47, 0xf2, 0xa4, 0x7e,
|
||||
0x06, 0xea, 0x36, 0xc6, 0x11, 0x89, 0x63, 0x12, 0x1b, 0x0b, 0xed, 0x85, 0x5e, 0xdd, 0x3c, 0x4e,
|
||||
0x39, 0x9c, 0x81, 0x19, 0x87, 0x0f, 0xa4, 0x77, 0x81, 0x28, 0xce, 0x6d, 0x4c, 0x4e, 0xec, 0x64,
|
||||
0xc8, 0xf6, 0x3a, 0x78, 0xec, 0xdb, 0x1e, 0x75, 0x45, 0xac, 0x8d, 0x6b, 0xba, 0xd7, 0x17, 0xdd,
|
||||
0xa5, 0x42, 0x80, 0x66, 0xbe, 0xfa, 0x08, 0xac, 0xb8, 0x81, 0x17, 0x8a, 0x1d, 0x0d, 0x7c, 0xe3,
|
||||
0x56, 0x5b, 0xeb, 0xad, 0xed, 0xde, 0xe9, 0x97, 0xed, 0x7c, 0x3c, 0x23, 0xcd, 0x4f, 0x53, 0x0e,
|
||||
0x55, 0x75, 0xc6, 0xe1, 0x96, 0x4c, 0x4a, 0xc1, 0xca, 0x9e, 0xae, 0xcf, 0x83, 0x48, 0x3d, 0xaa,
|
||||
0x13, 0x50, 0x77, 0x49, 0xc4, 0x2c, 0xd9, 0xc8, 0xdb, 0xb2, 0x91, 0x4f, 0xc4, 0x67, 0x12, 0xe0,
|
||||
0xd3, 0xbc, 0x99, 0xf7, 0x73, 0xef, 0x02, 0x78, 0x43, 0x43, 0xef, 0xde, 0xc0, 0xa1, 0xd2, 0x45,
|
||||
0x3f, 0x06, 0x80, 0xfa, 0x2c, 0x0a, 0x70, 0xe2, 0x92, 0xc8, 0x58, 0x6c, 0x6b, 0xbd, 0x65, 0x73,
|
||||
0x2f, 0xe5, 0x50, 0x41, 0x33, 0x0e, 0xef, 0xe4, 0x03, 0x51, 0x42, 0x65, 0x11, 0xcd, 0x39, 0x0c,
|
||||
0x29, 0xe7, 0xf4, 0x3f, 0x34, 0xb0, 0x1d, 0x9f, 0xd2, 0xd0, 0x9a, 0x62, 0x62, 0x92, 0xad, 0x88,
|
||||
0x78, 0xc1, 0xc8, 0x1e, 0xc6, 0xc6, 0x92, 0x0c, 0x86, 0x53, 0x0e, 0x0d, 0xa1, 0x3a, 0x50, 0x44,
|
||||
0xa8, 0xd0, 0x64, 0x1c, 0xbe, 0x2f, 0x43, 0xdf, 0x24, 0x28, 0x13, 0xb9, 0xff, 0x56, 0x05, 0xba,
|
||||
0x31, 0x82, 0xfe, 0xa7, 0x06, 0x1a, 0x65, 0xce, 0xd8, 0x72, 0xc6, 0xc6, 0xb2, 0xbc, 0x5c, 0x3f,
|
||||
0xbe, 0xd3, 0xe5, 0x4a, 0x39, 0x5c, 0x9d, 0xb9, 0x9a, 0xe3, 0x8c, 0xc3, 0xbb, 0xd5, 0x1e, 0x62,
|
||||
0x73, 0x5c, 0x26, 0xbf, 0x71, 0x0d, 0x15, 0x97, 0x0b, 0x55, 0x1c, 0xf4, 0x5d, 0xb0, 0x18, 0xda,
|
||||
0x49, 0x4c, 0xb0, 0x51, 0x97, 0x8d, 0xdb, 0x4e, 0x39, 0x2c, 0x90, 0x8c, 0xc3, 0x55, 0xe9, 0x9e,
|
||||
0x6f, 0x3b, 0xa8, 0xc0, 0xf5, 0x1f, 0xc0, 0xba, 0x3d, 0x1c, 0x06, 0x2f, 0x08, 0xb6, 0x7c, 0xc2,
|
||||
0x5e, 0x04, 0xd1, 0x69, 0x6c, 0x00, 0x79, 0x7b, 0xbe, 0x4e, 0x39, 0x6c, 0x16, 0xdc, 0xd3, 0x82,
|
||||
0xca, 0x38, 0x6c, 0xe5, 0x77, 0xa8, 0x82, 0x57, 0x67, 0xca, 0xb8, 0x89, 0x44, 0xf3, 0x76, 0xfa,
|
||||
0x77, 0x60, 0xd3, 0x4e, 0x58, 0x60, 0xd9, 0xae, 0x4b, 0x42, 0x66, 0x9d, 0x04, 0x43, 0x4c, 0xa2,
|
||||
0xd8, 0x58, 0x91, 0xe9, 0x7f, 0x98, 0x72, 0xb8, 0x21, 0xe8, 0xcf, 0x25, 0xfb, 0x45, 0x4e, 0x96,
|
||||
0x7d, 0xba, 0xc6, 0x74, 0xd0, 0x75, 0xb5, 0xfe, 0x0c, 0x34, 0x3c, 0xfb, 0xcc, 0x8a, 0x89, 0x8f,
|
||||
0xad, 0x53, 0x27, 0x8c, 0x8d, 0xd5, 0xb6, 0xd6, 0xbb, 0x6d, 0x7e, 0x20, 0xee, 0xa1, 0x67, 0x9f,
|
||||
0x7d, 0x43, 0x7c, 0x7c, 0xe8, 0x84, 0xc2, 0x75, 0x43, 0xba, 0x2a, 0x58, 0xe7, 0x35, 0x87, 0x0b,
|
||||
0xd4, 0x67, 0x48, 0x15, 0x4e, 0x0d, 0x23, 0xe2, 0x8e, 0x72, 0xc3, 0x46, 0xc5, 0x10, 0x11, 0x77,
|
||||
0x34, 0x6f, 0x38, 0xc5, 0x2a, 0x86, 0x53, 0x50, 0xf7, 0x41, 0x93, 0x0e, 0xfc, 0x20, 0x22, 0xb8,
|
||||
0xac, 0x7f, 0xad, 0xbd, 0xd0, 0x5b, 0xd9, 0xdd, 0xea, 0xe7, 0xff, 0x82, 0xfe, 0xb3, 0xe2, 0x5f,
|
||||
0x90, 0xd7, 0x64, 0x3e, 0x12, 0x63, 0x97, 0x72, 0xb8, 0x56, 0x1c, 0x9b, 0x35, 0x66, 0x33, 0x1f,
|
||||
0x20, 0x15, 0xee, 0xa0, 0x39, 0x99, 0xfe, 0xb3, 0x06, 0x9a, 0x21, 0xf1, 0x31, 0xf5, 0x07, 0x65,
|
||||
0xc0, 0xe6, 0x5b, 0x03, 0x3e, 0x11, 0x01, 0x2f, 0x39, 0x34, 0xf6, 0x49, 0x18, 0x11, 0xd7, 0x66,
|
||||
0x04, 0x1f, 0xe5, 0x06, 0x85, 0x67, 0xca, 0xa1, 0xf6, 0xa8, 0x7c, 0x6e, 0x42, 0x95, 0x53, 0x46,
|
||||
0xc3, 0xd0, 0xd0, 0x5a, 0x85, 0x8b, 0xf5, 0xdf, 0x34, 0xd0, 0xcc, 0xbb, 0xf9, 0x7d, 0x42, 0x62,
|
||||
0x66, 0x9d, 0x52, 0xc7, 0x58, 0x97, 0xfd, 0x8c, 0x2f, 0x39, 0x6c, 0x7c, 0x25, 0xda, 0x24, 0x99,
|
||||
0x43, 0x6a, 0xa6, 0x1c, 0x36, 0x3c, 0x15, 0x28, 0x0b, 0xae, 0xa0, 0xd3, 0x26, 0xa7, 0x17, 0xdd,
|
||||
0x39, 0xf9, 0x3c, 0xf0, 0x72, 0xd2, 0xad, 0x46, 0x40, 0x15, 0xde, 0xd1, 0x3f, 0x03, 0xf5, 0xc4,
|
||||
0x67, 0x51, 0x12, 0x33, 0x82, 0x8d, 0x0d, 0x39, 0x93, 0x6d, 0xf1, 0x4b, 0x29, 0xc1, 0x8c, 0xc3,
|
||||
0xa6, 0xcc, 0xa0, 0x44, 0x3a, 0x68, 0xc6, 0xca, 0xea, 0xc4, 0x5b, 0xc6, 0x88, 0x35, 0x48, 0xa8,
|
||||
0x15, 0x06, 0x11, 0x33, 0xf4, 0x59, 0x75, 0x48, 0x52, 0x5f, 0x7e, 0x7b, 0x70, 0x14, 0x44, 0x4c,
|
||||
0x54, 0x17, 0xa9, 0x40, 0x59, 0x5d, 0x05, 0x55, 0xab, 0xab, 0xca, 0xe7, 0x01, 0x51, 0x5d, 0x25,
|
||||
0x02, 0x9a, 0xf2, 0x09, 0x15, 0x5b, 0xf3, 0xf0, 0xfc, 0x55, 0xab, 0x36, 0x79, 0xd5, 0xaa, 0x9d,
|
||||
0x5f, 0xb6, 0xb4, 0xc9, 0x65, 0x4b, 0xfb, 0xf5, 0xaa, 0x55, 0xfb, 0xfd, 0xaa, 0xa5, 0x4d, 0xae,
|
||||
0x5a, 0xb5, 0x7f, 0xae, 0x5a, 0xb5, 0xe3, 0x07, 0xff, 0xe3, 0x5d, 0xcb, 0x27, 0xc6, 0x59, 0x94,
|
||||
0xef, 0xdb, 0x47, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x01, 0x84, 0xd4, 0x09, 0x09, 0x00,
|
||||
0x00,
|
||||
// 1026 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbf, 0x6f, 0xdb, 0x46,
|
||||
0x18, 0x15, 0xeb, 0xc4, 0xb6, 0xce, 0x3f, 0x64, 0xd3, 0x88, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35,
|
||||
0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xa3, 0x2d, 0x50, 0xc6, 0x68, 0x63, 0x18, 0x4d, 0x5c, 0x16,
|
||||
0x5d, 0xbc, 0xb0, 0x24, 0xef, 0xac, 0x1c, 0x2c, 0xf2, 0x58, 0xf2, 0xa8, 0x58, 0x40, 0xff, 0x80,
|
||||
0x76, 0x2b, 0x02, 0x74, 0xea, 0x92, 0xf6, 0xdf, 0xe8, 0xd0, 0xd5, 0x9b, 0x35, 0x16, 0x1d, 0x0e,
|
||||
0x88, 0xbd, 0x71, 0x29, 0xc0, 0x31, 0x53, 0x71, 0x77, 0x14, 0x45, 0xca, 0x51, 0x50, 0xa0, 0x1b,
|
||||
0xef, 0xbd, 0x77, 0xef, 0xdd, 0xf7, 0xe9, 0xbb, 0x13, 0xe8, 0x0c, 0x88, 0xbb, 0xeb, 0xd1, 0xe0,
|
||||
0x94, 0xf4, 0x77, 0x11, 0x1e, 0x12, 0x0f, 0xab, 0x45, 0x12, 0x39, 0x8c, 0xd0, 0xa0, 0x17, 0x46,
|
||||
0x94, 0x51, 0x7d, 0x51, 0x81, 0x3b, 0xdb, 0x42, 0x2d, 0x21, 0x8f, 0x0e, 0x76, 0x5d, 0x1c, 0x2a,
|
||||
0x7e, 0xe7, 0x5e, 0xc9, 0x85, 0xba, 0x31, 0x8e, 0x86, 0x18, 0xe5, 0x54, 0x1d, 0x9f, 0x33, 0xf5,
|
||||
0xd9, 0xfe, 0x67, 0x03, 0x6c, 0x1d, 0xc8, 0x8c, 0xc7, 0xe5, 0x0c, 0xfd, 0x4f, 0x0d, 0xd4, 0x55,
|
||||
0xb6, 0x4d, 0x90, 0xa1, 0xb5, 0xb4, 0xee, 0xaa, 0xf9, 0x9b, 0x76, 0xc1, 0x61, 0xed, 0x6f, 0x0e,
|
||||
0x3f, 0xee, 0x13, 0xf6, 0x3c, 0x71, 0x7b, 0x1e, 0xf5, 0x77, 0xe3, 0x51, 0xe0, 0xb1, 0xe7, 0x24,
|
||||
0xe8, 0x97, 0xbe, 0xca, 0x27, 0xea, 0x29, 0xf7, 0xc3, 0x83, 0x2b, 0x0e, 0x97, 0x27, 0xdf, 0x29,
|
||||
0x87, 0xcb, 0x28, 0xff, 0xce, 0x38, 0x6c, 0x9e, 0xfb, 0x83, 0xfd, 0x36, 0x41, 0x0f, 0x1d, 0xc6,
|
||||
0xa2, 0x76, 0x2b, 0xa0, 0x08, 0x9f, 0x3a, 0xc9, 0x80, 0xed, 0xb7, 0x59, 0x94, 0xe0, 0x76, 0x7a,
|
||||
0xd9, 0x59, 0xca, 0xc9, 0xec, 0xb2, 0x53, 0x6c, 0xfc, 0x71, 0xdc, 0xd1, 0x5e, 0x8e, 0x3b, 0x85,
|
||||
0xe9, 0xab, 0x71, 0x47, 0xb3, 0x26, 0x2c, 0xd2, 0x8f, 0xc1, 0xad, 0xc0, 0xf1, 0xb1, 0xf1, 0x5e,
|
||||
0x4b, 0xeb, 0xd6, 0xcd, 0x4f, 0x52, 0x0e, 0xe5, 0x3a, 0xe3, 0xf0, 0x9e, 0x8c, 0x13, 0x0b, 0xe9,
|
||||
0xf9, 0x90, 0xfa, 0x84, 0x61, 0x3f, 0x64, 0x23, 0x91, 0xb4, 0xf5, 0x16, 0xdc, 0x92, 0x3b, 0xf5,
|
||||
0x73, 0x50, 0x77, 0x10, 0x8a, 0x70, 0x1c, 0xe3, 0xd8, 0x58, 0x68, 0x2d, 0x74, 0xeb, 0xe6, 0x49,
|
||||
0xca, 0xe1, 0x14, 0xcc, 0x38, 0x7c, 0x20, 0xbd, 0x73, 0xa4, 0xe4, 0xdc, 0x2a, 0x4a, 0x42, 0xa3,
|
||||
0xc0, 0xf1, 0x89, 0x27, 0xb2, 0x36, 0x6f, 0xe8, 0xde, 0x5c, 0x76, 0x96, 0x72, 0x81, 0x35, 0xf5,
|
||||
0xd5, 0x87, 0x60, 0xc5, 0xa3, 0x7e, 0x28, 0x56, 0x84, 0x06, 0xc6, 0xad, 0x96, 0xd6, 0x5d, 0xdf,
|
||||
0xbb, 0xd3, 0x2b, 0x7a, 0xfc, 0x78, 0x4a, 0x9a, 0x9f, 0xa6, 0x1c, 0x96, 0xd5, 0x19, 0x87, 0xdb,
|
||||
0xf2, 0x50, 0x25, 0x4c, 0x35, 0x3a, 0xbd, 0xec, 0x6c, 0xcc, 0x82, 0x56, 0x79, 0xab, 0x8e, 0x41,
|
||||
0xdd, 0xc3, 0x11, 0xb3, 0x65, 0x23, 0x6f, 0xcb, 0x46, 0x3e, 0x11, 0xbf, 0x9d, 0x00, 0x9f, 0xaa,
|
||||
0x66, 0xde, 0x57, 0xde, 0x39, 0xf0, 0x96, 0x86, 0xde, 0x9d, 0xc3, 0x59, 0x85, 0x8b, 0x7e, 0x02,
|
||||
0x00, 0x09, 0x58, 0x44, 0x51, 0xe2, 0xe1, 0xc8, 0x58, 0x6c, 0x69, 0xdd, 0x65, 0x73, 0x3f, 0xe5,
|
||||
0xb0, 0x84, 0x66, 0x1c, 0xde, 0x51, 0x53, 0x52, 0x40, 0x45, 0x11, 0x8d, 0x19, 0xcc, 0x2a, 0xed,
|
||||
0xd3, 0x7f, 0xd7, 0xc0, 0x4e, 0x7c, 0x46, 0x42, 0x7b, 0x82, 0x89, 0xf1, 0xb6, 0x23, 0xec, 0xd3,
|
||||
0xa1, 0x33, 0x88, 0x8d, 0x25, 0x19, 0x86, 0x52, 0x0e, 0x0d, 0xa1, 0x3a, 0x2c, 0x89, 0xac, 0x5c,
|
||||
0x93, 0x71, 0xf8, 0xbe, 0x8c, 0x9e, 0x27, 0x28, 0x0e, 0x72, 0xff, 0x9d, 0x0a, 0x6b, 0x6e, 0x82,
|
||||
0xfe, 0x87, 0x06, 0xd6, 0x8a, 0x33, 0x23, 0xdb, 0x1d, 0x19, 0xcb, 0xf2, 0xc6, 0xfd, 0xf2, 0xbf,
|
||||
0x6e, 0x5c, 0xca, 0xe1, 0xea, 0xd4, 0xd5, 0x1c, 0x65, 0x1c, 0x76, 0xab, 0x3d, 0x44, 0xe6, 0x68,
|
||||
0xfe, 0x9d, 0xdb, 0xbc, 0x21, 0x13, 0x37, 0x4e, 0xde, 0xb2, 0x8a, 0xad, 0xbe, 0x07, 0x16, 0x43,
|
||||
0x27, 0x89, 0x31, 0x32, 0xea, 0xb2, 0x9b, 0x3b, 0x29, 0x87, 0x39, 0x92, 0x71, 0xb8, 0x2a, 0x23,
|
||||
0xd5, 0xb2, 0x6d, 0xe5, 0xb8, 0xfe, 0x03, 0xd8, 0x70, 0x06, 0x03, 0xfa, 0x02, 0x23, 0x3b, 0xc0,
|
||||
0xec, 0x05, 0x8d, 0xce, 0x62, 0x03, 0xc8, 0x2b, 0xf5, 0x75, 0xca, 0x61, 0x23, 0xe7, 0x9e, 0xe6,
|
||||
0x54, 0xf1, 0x46, 0x54, 0xf1, 0xea, 0xa0, 0x19, 0xf3, 0x48, 0x6b, 0xd6, 0x4e, 0xff, 0x0e, 0x6c,
|
||||
0x39, 0x09, 0xa3, 0xb6, 0xe3, 0x79, 0x38, 0x64, 0xf6, 0x29, 0x1d, 0x20, 0x1c, 0xc5, 0xc6, 0x8a,
|
||||
0x3c, 0xfe, 0x87, 0x29, 0x87, 0x9b, 0x82, 0xfe, 0x5c, 0xb2, 0x5f, 0x28, 0x32, 0xe3, 0xf0, 0xae,
|
||||
0x3a, 0xc2, 0x2c, 0xd3, 0xb6, 0x6e, 0xaa, 0xf5, 0x67, 0x60, 0xcd, 0x77, 0xce, 0xed, 0x18, 0x07,
|
||||
0xc8, 0x3e, 0x73, 0xc3, 0xd8, 0x58, 0x6d, 0x69, 0xdd, 0xdb, 0xe6, 0x07, 0xe2, 0x72, 0xfa, 0xce,
|
||||
0xf9, 0x37, 0x38, 0x40, 0x47, 0x6e, 0x28, 0x5c, 0x37, 0xa5, 0x6b, 0x09, 0x6b, 0xbf, 0xe1, 0x70,
|
||||
0x81, 0x04, 0xcc, 0x2a, 0x0b, 0x27, 0x86, 0x11, 0xf6, 0x86, 0xca, 0x70, 0xad, 0x62, 0x68, 0x61,
|
||||
0x6f, 0x38, 0x6b, 0x38, 0xc1, 0x2a, 0x86, 0x13, 0x50, 0x0f, 0x40, 0x83, 0xf4, 0x03, 0x1a, 0x61,
|
||||
0x54, 0xd4, 0xbf, 0xde, 0x5a, 0xe8, 0xae, 0xec, 0x6d, 0xf7, 0xd4, 0xbf, 0x46, 0xef, 0x59, 0xfe,
|
||||
0xaf, 0xa1, 0x6a, 0x32, 0x1f, 0x89, 0x59, 0x4c, 0x39, 0x5c, 0xcf, 0xb7, 0x4d, 0x1b, 0xb3, 0xa5,
|
||||
0xa6, 0xaa, 0x0c, 0xb7, 0xad, 0x19, 0x99, 0xfe, 0x93, 0x06, 0x1a, 0x21, 0x0e, 0x10, 0x09, 0xfa,
|
||||
0x45, 0x60, 0xe3, 0x9d, 0x81, 0x4f, 0x44, 0xe0, 0x15, 0x87, 0xc6, 0x01, 0x0e, 0x23, 0xec, 0x39,
|
||||
0x0c, 0xa3, 0x63, 0x65, 0x90, 0x7b, 0xa6, 0x1c, 0x6a, 0x8f, 0x8a, 0x37, 0x28, 0x2c, 0x73, 0xa5,
|
||||
0xd1, 0x30, 0x34, 0x6b, 0xbd, 0xc2, 0xc5, 0xfa, 0xaf, 0x1a, 0x68, 0xa8, 0x6e, 0x7e, 0x9f, 0xe0,
|
||||
0x98, 0xd9, 0x67, 0xc4, 0x35, 0x36, 0x64, 0x3f, 0xe3, 0x2b, 0x0e, 0xd7, 0xbe, 0x12, 0x6d, 0x92,
|
||||
0xcc, 0x11, 0x31, 0x53, 0x0e, 0xd7, 0xfc, 0x32, 0x50, 0x14, 0x5c, 0x41, 0x27, 0x4d, 0x4e, 0x2f,
|
||||
0x3b, 0x33, 0xf2, 0x59, 0xe0, 0xe5, 0xb8, 0x53, 0x4d, 0xb0, 0x2a, 0xbc, 0xab, 0x7f, 0x06, 0xea,
|
||||
0x49, 0xc0, 0xa2, 0x24, 0x66, 0x18, 0x19, 0x9b, 0x72, 0x26, 0x5b, 0xe2, 0x7f, 0xa6, 0x00, 0x33,
|
||||
0x0e, 0x1b, 0xf2, 0x04, 0x05, 0xd2, 0xb6, 0xa6, 0xac, 0xac, 0x4e, 0x3c, 0x70, 0x0c, 0xdb, 0xfd,
|
||||
0x84, 0xd8, 0x21, 0x8d, 0x98, 0xa1, 0x4f, 0xab, 0xb3, 0x24, 0xf5, 0xe5, 0xb7, 0x87, 0xc7, 0x34,
|
||||
0x62, 0xa2, 0xba, 0xa8, 0x0c, 0x14, 0xd5, 0x55, 0xd0, 0x72, 0x75, 0x55, 0xf9, 0x2c, 0x20, 0xaa,
|
||||
0xab, 0x24, 0x58, 0x13, 0x3e, 0x21, 0x62, 0x69, 0x1e, 0x5d, 0xbc, 0x6e, 0xd6, 0xc6, 0xaf, 0x9b,
|
||||
0xb5, 0x8b, 0xab, 0xa6, 0x36, 0xbe, 0x6a, 0x6a, 0x3f, 0x5f, 0x37, 0x6b, 0xaf, 0xae, 0x9b, 0xda,
|
||||
0xf8, 0xba, 0x59, 0xfb, 0xeb, 0xba, 0x59, 0x3b, 0x79, 0xf0, 0x1f, 0x1e, 0x3b, 0x35, 0x31, 0xee,
|
||||
0xa2, 0x7c, 0xf4, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x4a, 0x4f, 0x60, 0x33, 0x09,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *DeviceConfiguration) Marshal() (dAtA []byte, err error) {
|
||||
|
||||
@@ -33,21 +33,6 @@ const (
|
||||
maxConcurrentWritesLimit = 64
|
||||
)
|
||||
|
||||
func NewFolderConfiguration(myID protocol.DeviceID, id, label string, fsType fs.FilesystemType, path string) FolderConfiguration {
|
||||
f := FolderConfiguration{
|
||||
ID: id,
|
||||
Label: label,
|
||||
Devices: []FolderDeviceConfiguration{{DeviceID: myID}},
|
||||
FilesystemType: fsType,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
util.SetDefaults(&f)
|
||||
|
||||
f.prepare(myID, nil)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f FolderConfiguration) Copy() FolderConfiguration {
|
||||
c := f
|
||||
c.Devices = make([]FolderDeviceConfiguration, len(f.Devices))
|
||||
@@ -65,7 +50,7 @@ func (f FolderConfiguration) Filesystem() fs.Filesystem {
|
||||
}
|
||||
filesystem := fs.NewFilesystem(f.FilesystemType, f.Path, opts...)
|
||||
if !f.CaseSensitiveFS {
|
||||
filesystem = fs.NewCaseFilesystem(filesystem)
|
||||
filesystem = fs.NewCaseFilesystem(filesystem, opts...)
|
||||
}
|
||||
return filesystem
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ func (m *FolderDeviceConfiguration) XXX_DiscardUnknown() {
|
||||
var xxx_messageInfo_FolderDeviceConfiguration proto.InternalMessageInfo
|
||||
|
||||
type FolderConfiguration struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id" xml:"id,attr"`
|
||||
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id" xml:"id,attr" nodefault:"true"`
|
||||
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label" xml:"label,attr" restart:"false"`
|
||||
FilesystemType fs.FilesystemType `protobuf:"varint,3,opt,name=filesystem_type,json=filesystemType,proto3,enum=fs.FilesystemType" json:"filesystemType" xml:"filesystemType"`
|
||||
Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path" xml:"path,attr"`
|
||||
Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path" xml:"path,attr" default:"~"`
|
||||
Type FolderType `protobuf:"varint,5,opt,name=type,proto3,enum=config.FolderType" json:"type" xml:"type,attr"`
|
||||
Devices []FolderDeviceConfiguration `protobuf:"bytes,6,rep,name=devices,proto3" json:"devices" xml:"device"`
|
||||
RescanIntervalS int `protobuf:"varint,7,opt,name=rescan_interval_s,json=rescanIntervalS,proto3,casttype=int" json:"rescanIntervalS" xml:"rescanIntervalS,attr" default:"3600"`
|
||||
@@ -77,7 +77,7 @@ type FolderConfiguration struct {
|
||||
FSWatcherDelayS int `protobuf:"varint,9,opt,name=fs_watcher_delay_s,json=fsWatcherDelayS,proto3,casttype=int" json:"fsWatcherDelayS" xml:"fsWatcherDelayS,attr" default:"10"`
|
||||
IgnorePerms bool `protobuf:"varint,10,opt,name=ignore_perms,json=ignorePerms,proto3" json:"ignorePerms" xml:"ignorePerms,attr"`
|
||||
AutoNormalize bool `protobuf:"varint,11,opt,name=auto_normalize,json=autoNormalize,proto3" json:"autoNormalize" xml:"autoNormalize,attr" default:"true"`
|
||||
MinDiskFree Size `protobuf:"bytes,12,opt,name=min_disk_free,json=minDiskFree,proto3" json:"minDiskFree" xml:"minDiskFree"`
|
||||
MinDiskFree Size `protobuf:"bytes,12,opt,name=min_disk_free,json=minDiskFree,proto3" json:"minDiskFree" xml:"minDiskFree" default:"1 %"`
|
||||
Versioning VersioningConfiguration `protobuf:"bytes,13,opt,name=versioning,proto3" json:"versioning" xml:"versioning"`
|
||||
Copiers int `protobuf:"varint,14,opt,name=copiers,proto3,casttype=int" json:"copiers" xml:"copiers"`
|
||||
PullerMaxPendingKiB int `protobuf:"varint,15,opt,name=puller_max_pending_kib,json=pullerMaxPendingKib,proto3,casttype=int" json:"pullerMaxPendingKiB" xml:"pullerMaxPendingKiB"`
|
||||
@@ -86,7 +86,7 @@ type FolderConfiguration struct {
|
||||
IgnoreDelete bool `protobuf:"varint,18,opt,name=ignore_delete,json=ignoreDelete,proto3" json:"ignoreDelete" xml:"ignoreDelete"`
|
||||
ScanProgressIntervalS int `protobuf:"varint,19,opt,name=scan_progress_interval_s,json=scanProgressIntervalS,proto3,casttype=int" json:"scanProgressIntervalS" xml:"scanProgressIntervalS"`
|
||||
PullerPauseS int `protobuf:"varint,20,opt,name=puller_pause_s,json=pullerPauseS,proto3,casttype=int" json:"pullerPauseS" xml:"pullerPauseS"`
|
||||
MaxConflicts int `protobuf:"varint,21,opt,name=max_conflicts,json=maxConflicts,proto3,casttype=int" json:"maxConflicts" xml:"maxConflicts" default:"-1"`
|
||||
MaxConflicts int `protobuf:"varint,21,opt,name=max_conflicts,json=maxConflicts,proto3,casttype=int" json:"maxConflicts" xml:"maxConflicts" default:"10"`
|
||||
DisableSparseFiles bool `protobuf:"varint,22,opt,name=disable_sparse_files,json=disableSparseFiles,proto3" json:"disableSparseFiles" xml:"disableSparseFiles"`
|
||||
DisableTempIndexes bool `protobuf:"varint,23,opt,name=disable_temp_indexes,json=disableTempIndexes,proto3" json:"disableTempIndexes" xml:"disableTempIndexes"`
|
||||
Paused bool `protobuf:"varint,24,opt,name=paused,proto3" json:"paused" xml:"paused"`
|
||||
@@ -149,134 +149,135 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_44a9785876ed3afa = []byte{
|
||||
// 2018 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6f, 0x1c, 0x49,
|
||||
0x15, 0x77, 0x3b, 0x5f, 0x76, 0xf9, 0xbb, 0x1c, 0x27, 0x15, 0x67, 0xd7, 0xe5, 0x6d, 0x86, 0xc5,
|
||||
0xbb, 0xda, 0x38, 0x89, 0x17, 0x71, 0x88, 0x76, 0x17, 0x76, 0xe2, 0xb5, 0x08, 0x21, 0x9b, 0x51,
|
||||
0x3b, 0x10, 0x11, 0x90, 0x9a, 0x76, 0x77, 0xcd, 0x4c, 0xad, 0xfb, 0x8b, 0xaa, 0x9e, 0xd8, 0x93,
|
||||
0x53, 0xb8, 0x20, 0x10, 0x7b, 0x40, 0xe6, 0xc0, 0x15, 0x09, 0x84, 0x60, 0xff, 0x01, 0x24, 0xfe,
|
||||
0x82, 0x5c, 0x90, 0xe7, 0x84, 0x10, 0x87, 0x92, 0xd6, 0xbe, 0xcd, 0xb1, 0x8f, 0x39, 0xa1, 0xaa,
|
||||
0xea, 0xee, 0xe9, 0xee, 0x99, 0x45, 0x48, 0x7b, 0x9b, 0xfa, 0xfd, 0x7e, 0xf5, 0xde, 0xeb, 0x57,
|
||||
0xf5, 0x5e, 0xbf, 0x1e, 0xd0, 0xf0, 0xe9, 0xc1, 0x6d, 0x37, 0x0a, 0xdb, 0xb4, 0x73, 0xbb, 0x1d,
|
||||
0xf9, 0x1e, 0x61, 0x7a, 0xd1, 0x63, 0x4e, 0x42, 0xa3, 0x70, 0x3b, 0x66, 0x51, 0x12, 0xc1, 0xcb,
|
||||
0x1a, 0x5c, 0xbf, 0x39, 0xa6, 0x4e, 0xfa, 0x31, 0xd1, 0xa2, 0xf5, 0xb5, 0x12, 0xc9, 0xe9, 0x8b,
|
||||
0x1c, 0x5e, 0x2f, 0xc1, 0x71, 0xcf, 0xf7, 0x23, 0xe6, 0x11, 0x96, 0x71, 0x5b, 0x25, 0xee, 0x39,
|
||||
0x61, 0x9c, 0x46, 0x21, 0x0d, 0x3b, 0x13, 0x22, 0x58, 0xc7, 0x25, 0xe5, 0x81, 0x1f, 0xb9, 0x87,
|
||||
0x75, 0x53, 0x50, 0x0a, 0xda, 0xfc, 0xb6, 0x0c, 0x88, 0x67, 0xd8, 0x1b, 0x19, 0xe6, 0x46, 0x71,
|
||||
0x9f, 0x39, 0x61, 0x87, 0x04, 0x24, 0xe9, 0x46, 0x5e, 0xc6, 0xce, 0x92, 0xe3, 0x44, 0xff, 0x34,
|
||||
0xff, 0x75, 0x01, 0xdc, 0xd8, 0x53, 0xcf, 0xb3, 0x4b, 0x9e, 0x53, 0x97, 0xdc, 0x2f, 0x47, 0x00,
|
||||
0xbf, 0x30, 0xc0, 0xac, 0xa7, 0x70, 0x9b, 0x7a, 0xc8, 0xd8, 0x34, 0xb6, 0xe6, 0x9b, 0x9f, 0x1b,
|
||||
0xaf, 0x04, 0x9e, 0xfa, 0x8f, 0xc0, 0xdf, 0xee, 0xd0, 0xa4, 0xdb, 0x3b, 0xd8, 0x76, 0xa3, 0xe0,
|
||||
0x36, 0xef, 0x87, 0x6e, 0xd2, 0xa5, 0x61, 0xa7, 0xf4, 0x4b, 0x86, 0xa0, 0x9c, 0xb8, 0x91, 0xbf,
|
||||
0xad, 0xad, 0x3f, 0xd8, 0x3d, 0x13, 0x78, 0x26, 0xff, 0x3d, 0x14, 0x78, 0xc6, 0xcb, 0x7e, 0xa7,
|
||||
0x02, 0x2f, 0x1c, 0x07, 0xfe, 0x3d, 0x93, 0x7a, 0xef, 0x39, 0x49, 0xc2, 0xcc, 0xe1, 0x69, 0xe3,
|
||||
0x4a, 0xf6, 0x3b, 0x3d, 0x6d, 0x14, 0xba, 0x5f, 0x0f, 0x1a, 0xc6, 0xc9, 0xa0, 0x51, 0xd8, 0xb0,
|
||||
0x72, 0xc6, 0x83, 0x7f, 0x31, 0xc0, 0x02, 0x0d, 0x13, 0x16, 0x79, 0x3d, 0x97, 0x78, 0xf6, 0x41,
|
||||
0x1f, 0x4d, 0xab, 0x80, 0x5f, 0x7e, 0xad, 0x80, 0x87, 0x02, 0xcf, 0x8f, 0xac, 0x36, 0xfb, 0xa9,
|
||||
0xc0, 0xd7, 0x75, 0xa0, 0x25, 0xb0, 0x08, 0x79, 0x65, 0x0c, 0x95, 0x01, 0x5b, 0x15, 0x0b, 0xd0,
|
||||
0x05, 0xab, 0x24, 0x74, 0x59, 0x3f, 0x96, 0x39, 0xb6, 0x63, 0x87, 0xf3, 0xa3, 0x88, 0x79, 0xe8,
|
||||
0xc2, 0xa6, 0xb1, 0x35, 0xdb, 0xdc, 0x19, 0x0a, 0x0c, 0x47, 0x74, 0x2b, 0x63, 0x53, 0x81, 0x91,
|
||||
0x72, 0x3b, 0x4e, 0x99, 0xd6, 0x04, 0xbd, 0x79, 0x8e, 0xc1, 0xaa, 0x3e, 0xd8, 0xea, 0x91, 0x7e,
|
||||
0x04, 0xa6, 0xb3, 0xa3, 0x9c, 0x6d, 0x6e, 0x9f, 0x09, 0x3c, 0xad, 0x1e, 0x71, 0x9a, 0x7a, 0xff,
|
||||
0xeb, 0x04, 0x4e, 0x06, 0x8d, 0xe9, 0x07, 0xbb, 0xd6, 0x34, 0xf5, 0xe0, 0x8f, 0xc0, 0x25, 0xdf,
|
||||
0x39, 0x20, 0xbe, 0x4a, 0xee, 0x6c, 0xf3, 0xbb, 0x43, 0x81, 0x35, 0x90, 0x0a, 0xbc, 0xa9, 0xf6,
|
||||
0xab, 0x95, 0x36, 0xb1, 0xc9, 0x08, 0x4f, 0x1c, 0x96, 0xdc, 0x33, 0xdb, 0x8e, 0xcf, 0x89, 0x34,
|
||||
0x09, 0x46, 0xf4, 0xcb, 0x41, 0x63, 0xca, 0xd2, 0x9b, 0x61, 0x07, 0x2c, 0xb5, 0xa9, 0x4f, 0x78,
|
||||
0x9f, 0x27, 0x24, 0xb0, 0xe5, 0x55, 0x56, 0xf9, 0x58, 0xdc, 0x81, 0xdb, 0x6d, 0xbe, 0xbd, 0x57,
|
||||
0x50, 0x4f, 0xfa, 0x31, 0x69, 0xbe, 0x3b, 0x14, 0x78, 0xb1, 0x5d, 0xc1, 0x52, 0x81, 0xaf, 0x2a,
|
||||
0xef, 0x55, 0xd8, 0xb4, 0x6a, 0x3a, 0xf8, 0x01, 0xb8, 0x18, 0x3b, 0x49, 0x17, 0x5d, 0x54, 0xe1,
|
||||
0x6f, 0x0d, 0x05, 0x56, 0xeb, 0x54, 0xe0, 0x25, 0xb5, 0x5f, 0x2e, 0x8a, 0xe7, 0x9f, 0x2d, 0x56,
|
||||
0x96, 0x52, 0xc1, 0x16, 0xb8, 0xa8, 0x62, 0xbb, 0x94, 0xc5, 0xa6, 0xeb, 0x72, 0x5b, 0x27, 0x5a,
|
||||
0xc5, 0xa6, 0x2c, 0x26, 0x3a, 0x22, 0x6d, 0x51, 0x2e, 0x46, 0x16, 0x8b, 0x95, 0xa5, 0x54, 0xf0,
|
||||
0x67, 0xe0, 0x8a, 0xbe, 0xc1, 0x1c, 0x5d, 0xde, 0xbc, 0xb0, 0x35, 0xb7, 0xf3, 0x56, 0xd5, 0xe8,
|
||||
0x84, 0xb2, 0x6c, 0x62, 0x79, 0xa1, 0x87, 0x02, 0xe7, 0x3b, 0x53, 0x81, 0xe7, 0x95, 0x2b, 0xbd,
|
||||
0x36, 0xad, 0x9c, 0x80, 0xbf, 0x37, 0xc0, 0x0a, 0x23, 0xdc, 0x75, 0x42, 0x9b, 0x86, 0x09, 0x61,
|
||||
0xcf, 0x1d, 0xdf, 0xe6, 0xe8, 0xca, 0xa6, 0xb1, 0x75, 0xa9, 0xd9, 0x19, 0x0a, 0xbc, 0xa4, 0xc9,
|
||||
0x07, 0x19, 0xb7, 0x9f, 0x0a, 0xfc, 0x8e, 0xb2, 0x54, 0xc3, 0xb3, 0xe3, 0xf4, 0x48, 0xdb, 0xe9,
|
||||
0xf9, 0xc9, 0x3d, 0xf3, 0xfd, 0xef, 0xdc, 0xb9, 0x63, 0xbe, 0x16, 0xf8, 0x02, 0x0d, 0x93, 0xe1,
|
||||
0x69, 0xe3, 0xea, 0x24, 0xf9, 0xeb, 0xd3, 0xc6, 0x45, 0xa9, 0xb3, 0xea, 0x4e, 0xe0, 0x3f, 0x0c,
|
||||
0x00, 0xdb, 0xdc, 0x3e, 0x72, 0x12, 0xb7, 0x4b, 0x98, 0x4d, 0x42, 0xe7, 0xc0, 0x27, 0x1e, 0x9a,
|
||||
0xd9, 0x34, 0xb6, 0x66, 0x9a, 0xbf, 0x35, 0xce, 0x04, 0x5e, 0xde, 0xdb, 0x7f, 0xaa, 0xd9, 0x4f,
|
||||
0x34, 0x39, 0x14, 0x78, 0xb9, 0xcd, 0xab, 0x58, 0x2a, 0xf0, 0xbb, 0xfa, 0xcc, 0x6b, 0x44, 0x3d,
|
||||
0xda, 0x84, 0xf5, 0xd4, 0xdd, 0x5b, 0x9b, 0x28, 0x94, 0x71, 0x4a, 0xc5, 0xc9, 0xa0, 0x31, 0xe6,
|
||||
0xd6, 0x1a, 0x73, 0x0a, 0xff, 0x5e, 0x0d, 0xde, 0x23, 0xbe, 0xd3, 0xb7, 0x39, 0x9a, 0x55, 0x39,
|
||||
0xfd, 0x8d, 0x0c, 0x7e, 0xa9, 0xb0, 0xb2, 0x2b, 0xc9, 0x7d, 0x99, 0xe7, 0xc2, 0x8c, 0x86, 0x52,
|
||||
0x81, 0xbf, 0x55, 0x0d, 0x5d, 0xe3, 0xf5, 0xc8, 0xef, 0x56, 0xb2, 0x3c, 0x49, 0xfc, 0xfa, 0xb4,
|
||||
0x31, 0x7d, 0xf7, 0xce, 0xc9, 0xa0, 0x51, 0xf7, 0x6a, 0xd5, 0x7d, 0xc2, 0x9f, 0x83, 0x79, 0xda,
|
||||
0x09, 0x23, 0x46, 0xec, 0x98, 0xb0, 0x80, 0x23, 0xa0, 0xf2, 0xfd, 0xe1, 0x50, 0xe0, 0x39, 0x8d,
|
||||
0xb7, 0x24, 0x9c, 0x0a, 0x7c, 0x4d, 0xf7, 0x81, 0x11, 0x56, 0x5c, 0xdf, 0xe5, 0x3a, 0x68, 0x95,
|
||||
0xb7, 0xc2, 0x5f, 0x1a, 0x60, 0xd1, 0xe9, 0x25, 0x91, 0x1d, 0x46, 0x2c, 0x70, 0x7c, 0xfa, 0x82,
|
||||
0xa0, 0x39, 0xe5, 0xe4, 0xd9, 0x50, 0xe0, 0x05, 0xc9, 0x7c, 0x9a, 0x13, 0x45, 0x06, 0x2a, 0xe8,
|
||||
0x57, 0x9d, 0x1c, 0x1c, 0x57, 0xe5, 0xc7, 0x66, 0x55, 0xed, 0xc2, 0x67, 0x60, 0x21, 0xa0, 0xa1,
|
||||
0xed, 0x51, 0x7e, 0x68, 0xb7, 0x19, 0x21, 0x68, 0x7e, 0xd3, 0xd8, 0x9a, 0xdb, 0x99, 0xcf, 0xcb,
|
||||
0x6a, 0x9f, 0xbe, 0x20, 0xcd, 0xad, 0xac, 0x82, 0xe6, 0x02, 0x1a, 0xee, 0x52, 0x7e, 0xb8, 0xc7,
|
||||
0x88, 0x8c, 0x68, 0x45, 0x45, 0x54, 0xc2, 0x4c, 0xab, 0xac, 0x80, 0x1d, 0x00, 0x46, 0x2f, 0x6b,
|
||||
0xb4, 0xa0, 0x0c, 0xe3, 0xdc, 0xf0, 0x8f, 0x0b, 0xa6, 0x5a, 0xad, 0x6f, 0x67, 0xbe, 0x4a, 0x5b,
|
||||
0x53, 0x81, 0x97, 0x95, 0xab, 0x11, 0x64, 0x5a, 0x25, 0x1e, 0x7e, 0x08, 0xae, 0xb8, 0x51, 0x4c,
|
||||
0x09, 0xe3, 0x68, 0x51, 0x5d, 0xac, 0x6f, 0xc8, 0x72, 0xcf, 0xa0, 0xa2, 0x53, 0x67, 0xeb, 0xfc,
|
||||
0x8a, 0x58, 0xb9, 0x00, 0xfe, 0xd3, 0x00, 0xd7, 0xe4, 0x98, 0x40, 0x98, 0x1d, 0x38, 0xc7, 0x76,
|
||||
0x4c, 0x42, 0x8f, 0x86, 0x1d, 0xfb, 0x90, 0x1e, 0xa0, 0x25, 0x65, 0xee, 0x0f, 0xf2, 0x9e, 0xae,
|
||||
0xb6, 0x94, 0xe4, 0x91, 0x73, 0xdc, 0xd2, 0x82, 0x87, 0xb4, 0x39, 0x14, 0x78, 0x35, 0x1e, 0x87,
|
||||
0x53, 0x81, 0x6f, 0xe8, 0xf6, 0x38, 0xce, 0x95, 0x6e, 0xe8, 0xc4, 0xad, 0x93, 0xe1, 0x93, 0x41,
|
||||
0x63, 0x92, 0x7f, 0x6b, 0x82, 0xf6, 0x40, 0xa6, 0xa3, 0xeb, 0xf0, 0xae, 0x4c, 0xc7, 0xf2, 0x28,
|
||||
0x1d, 0x19, 0x54, 0xa4, 0x23, 0x5b, 0x8f, 0xd2, 0x91, 0x01, 0xf0, 0x63, 0x70, 0x49, 0x0d, 0x4c,
|
||||
0x68, 0x45, 0xb5, 0xed, 0x95, 0xfc, 0xc4, 0xa4, 0xff, 0xc7, 0x92, 0x68, 0x22, 0xf9, 0x1a, 0x53,
|
||||
0x9a, 0x54, 0xe0, 0x39, 0x65, 0x4d, 0xad, 0x4c, 0x4b, 0xa3, 0xf0, 0x21, 0x58, 0xc8, 0x6a, 0xc7,
|
||||
0x23, 0x3e, 0x49, 0x08, 0x82, 0xea, 0x5e, 0xbf, 0xad, 0xc6, 0x03, 0x45, 0xec, 0x2a, 0x3c, 0x15,
|
||||
0x18, 0x96, 0xaa, 0x47, 0x83, 0xa6, 0x55, 0xd1, 0xc0, 0x63, 0x80, 0x54, 0x4b, 0x8e, 0x59, 0xd4,
|
||||
0x61, 0x84, 0xf3, 0x72, 0x6f, 0x5e, 0x55, 0xcf, 0x27, 0x5f, 0xab, 0x6b, 0x52, 0xd3, 0xca, 0x24,
|
||||
0xe5, 0x0e, 0x7d, 0x53, 0x39, 0x98, 0xc8, 0x16, 0xcf, 0x3e, 0x79, 0x33, 0xdc, 0x07, 0x8b, 0xd9,
|
||||
0xbd, 0x88, 0x9d, 0x1e, 0x27, 0x36, 0x47, 0x57, 0x95, 0xbf, 0x5b, 0xf2, 0x39, 0x34, 0xd3, 0x92,
|
||||
0xc4, 0x7e, 0xf1, 0x1c, 0x65, 0xb0, 0xb0, 0x5e, 0x91, 0x42, 0x02, 0x16, 0xe4, 0x2d, 0x93, 0x49,
|
||||
0xf5, 0xa9, 0x9b, 0x70, 0xb4, 0xa6, 0x6c, 0x7e, 0x4f, 0xda, 0x0c, 0x9c, 0xe3, 0xfb, 0x39, 0x9e,
|
||||
0x0a, 0x8c, 0x75, 0x81, 0x95, 0xc0, 0x52, 0xb1, 0xdf, 0xba, 0x9b, 0x3b, 0x90, 0x4d, 0xed, 0xd6,
|
||||
0x5d, 0xab, 0xb2, 0x1b, 0x7a, 0xe0, 0xaa, 0x47, 0xb9, 0x6c, 0xc2, 0x36, 0x8f, 0x1d, 0xc6, 0x89,
|
||||
0xad, 0x5e, 0xed, 0xe8, 0x9a, 0x3a, 0x09, 0x35, 0x37, 0x65, 0xfc, 0xbe, 0xa2, 0xd5, 0xd0, 0x50,
|
||||
0xcc, 0x4d, 0xe3, 0x94, 0x69, 0x4d, 0xd0, 0x97, 0xbd, 0x24, 0x24, 0x88, 0x6d, 0x1a, 0x7a, 0xe4,
|
||||
0x98, 0x70, 0x74, 0x7d, 0xcc, 0xcb, 0x13, 0x12, 0xc4, 0x0f, 0x34, 0x5b, 0xf7, 0x52, 0xa2, 0x46,
|
||||
0x5e, 0x4a, 0x20, 0xdc, 0x01, 0x97, 0xd5, 0x01, 0x78, 0x08, 0x29, 0xbb, 0xeb, 0x43, 0x81, 0x33,
|
||||
0xa4, 0x78, 0x99, 0xeb, 0xa5, 0x69, 0x65, 0x38, 0x4c, 0xc0, 0xf5, 0x23, 0xe2, 0x1c, 0xda, 0xf2,
|
||||
0x56, 0xdb, 0x49, 0x97, 0x11, 0xde, 0x8d, 0x7c, 0xcf, 0x8e, 0xdd, 0x04, 0xdd, 0x50, 0x09, 0x97,
|
||||
0x9d, 0xfc, 0xaa, 0x94, 0x7c, 0xdf, 0xe1, 0xdd, 0x27, 0xb9, 0xa0, 0xe5, 0x26, 0xa9, 0xc0, 0xeb,
|
||||
0xca, 0xe4, 0x24, 0xb2, 0x38, 0xd4, 0x89, 0x5b, 0xe1, 0x7d, 0x30, 0x17, 0x38, 0xec, 0x90, 0x30,
|
||||
0x3b, 0x74, 0x02, 0x82, 0xd6, 0xd5, 0xd8, 0x64, 0xca, 0x76, 0xa6, 0xe1, 0x4f, 0x9d, 0x80, 0x14,
|
||||
0xed, 0x6c, 0x04, 0x99, 0x56, 0x89, 0x87, 0x7d, 0xb0, 0x2e, 0xbf, 0x44, 0xec, 0xe8, 0x28, 0x24,
|
||||
0x8c, 0x77, 0x69, 0x6c, 0xb7, 0x59, 0x14, 0xd8, 0xb1, 0xc3, 0x48, 0x98, 0xa0, 0x9b, 0x2a, 0x05,
|
||||
0x1f, 0x0c, 0x05, 0xbe, 0x2e, 0x55, 0x8f, 0x73, 0xd1, 0x1e, 0x8b, 0x82, 0x96, 0x92, 0xa4, 0x02,
|
||||
0xbf, 0x99, 0x77, 0xbc, 0x49, 0xbc, 0x69, 0x7d, 0xd5, 0x4e, 0xf8, 0x2b, 0x03, 0xac, 0x04, 0x91,
|
||||
0x67, 0x27, 0x34, 0x20, 0xf6, 0x11, 0x0d, 0xbd, 0xe8, 0xc8, 0xe6, 0xe8, 0x0d, 0x95, 0xb0, 0x9f,
|
||||
0x9e, 0x09, 0xbc, 0x62, 0x39, 0x47, 0x8f, 0x22, 0xef, 0x09, 0x0d, 0xc8, 0x53, 0xc5, 0xca, 0xd7,
|
||||
0xf5, 0x62, 0x50, 0x41, 0x8a, 0xe1, 0xb2, 0x0a, 0xe7, 0x99, 0x3b, 0x19, 0x34, 0xc6, 0xad, 0x58,
|
||||
0x35, 0x1b, 0xf0, 0xa5, 0x01, 0xd6, 0xb2, 0x32, 0x71, 0x7b, 0x4c, 0xc6, 0x66, 0x1f, 0x31, 0x9a,
|
||||
0x10, 0x8e, 0xde, 0x54, 0xc1, 0xfc, 0x50, 0xb6, 0x5e, 0x7d, 0xe1, 0x33, 0xfe, 0xa9, 0xa2, 0x53,
|
||||
0x81, 0xbf, 0x59, 0xaa, 0x9a, 0x0a, 0x57, 0x2a, 0x9e, 0x9d, 0x52, 0xed, 0x18, 0x3b, 0xd6, 0x24,
|
||||
0x4b, 0xb2, 0x89, 0xe5, 0x77, 0xbb, 0x2d, 0x3f, 0x7b, 0xd0, 0xc6, 0xa8, 0x89, 0x65, 0xc4, 0x9e,
|
||||
0xc4, 0x8b, 0xe2, 0x2f, 0x83, 0xa6, 0x55, 0xd1, 0x40, 0x1f, 0x2c, 0xab, 0xcf, 0x51, 0x5b, 0xf6,
|
||||
0x02, 0x5b, 0xf7, 0x57, 0xac, 0xfa, 0xeb, 0xb5, 0xbc, 0xbf, 0x36, 0x25, 0x3f, 0x6a, 0xb2, 0x6a,
|
||||
0x6c, 0x3f, 0xa8, 0x60, 0x45, 0x66, 0xab, 0xb0, 0x69, 0xd5, 0x74, 0xf0, 0x73, 0x03, 0xac, 0xa8,
|
||||
0x2b, 0xa4, 0xbe, 0x66, 0x6d, 0xfd, 0x39, 0x8b, 0x36, 0x95, 0xbf, 0x55, 0xf9, 0x89, 0x70, 0x3f,
|
||||
0x8a, 0xfb, 0x96, 0xe4, 0x1e, 0x29, 0xaa, 0xf9, 0x50, 0x4e, 0x5d, 0x6e, 0x15, 0x4c, 0x05, 0xde,
|
||||
0x2a, 0xae, 0x51, 0x09, 0x2f, 0xa5, 0x91, 0x27, 0x4e, 0xe8, 0x39, 0xcc, 0x33, 0x5f, 0x9f, 0x36,
|
||||
0x66, 0xf2, 0x85, 0x55, 0x37, 0x04, 0xff, 0x2c, 0xc3, 0x71, 0x64, 0x03, 0x25, 0x21, 0xa7, 0x09,
|
||||
0x7d, 0x2e, 0x33, 0x8a, 0xde, 0x52, 0xe9, 0x3c, 0x96, 0x23, 0xe0, 0x7d, 0x87, 0x93, 0xfd, 0x9c,
|
||||
0xdb, 0x53, 0x23, 0xa0, 0x5b, 0x85, 0x52, 0x81, 0xd7, 0x74, 0x30, 0x55, 0x5c, 0x8e, 0x3b, 0x63,
|
||||
0xda, 0x71, 0x48, 0x4e, 0x7c, 0x35, 0x27, 0x56, 0x4d, 0xc3, 0xe1, 0x9f, 0x0c, 0xb0, 0xdc, 0x8e,
|
||||
0x7c, 0x3f, 0x3a, 0xb2, 0x3f, 0xeb, 0x85, 0xae, 0x1c, 0x47, 0x38, 0x32, 0x47, 0x51, 0xfe, 0x20,
|
||||
0x07, 0x3f, 0xe6, 0xbb, 0x94, 0x71, 0x19, 0xe5, 0x67, 0x55, 0xa8, 0x88, 0xb2, 0x86, 0xab, 0x28,
|
||||
0xeb, 0xda, 0x71, 0x48, 0x46, 0x59, 0x73, 0x62, 0x2d, 0xe9, 0x88, 0x0a, 0x18, 0x1e, 0x82, 0x59,
|
||||
0x46, 0x1c, 0xcf, 0x8e, 0x42, 0xbf, 0x8f, 0xfe, 0xba, 0xa7, 0xc2, 0x7b, 0x74, 0x26, 0x30, 0xdc,
|
||||
0x25, 0x31, 0x23, 0xae, 0x93, 0x10, 0xcf, 0x22, 0x8e, 0xf7, 0x38, 0xf4, 0xfb, 0x43, 0x81, 0x8d,
|
||||
0x5b, 0xc5, 0x27, 0x38, 0x8b, 0xd4, 0x24, 0xf8, 0x5e, 0x14, 0x50, 0xd9, 0xab, 0x93, 0xbe, 0xfa,
|
||||
0x04, 0x1f, 0x43, 0x91, 0x61, 0xcd, 0xb0, 0xcc, 0x00, 0xfc, 0x05, 0x58, 0xa9, 0x8c, 0x87, 0xaa,
|
||||
0x7f, 0xfe, 0x4d, 0x3a, 0x35, 0x9a, 0x9f, 0x9c, 0x09, 0x8c, 0x46, 0x4e, 0x1f, 0x8d, 0x26, 0xbf,
|
||||
0x96, 0x9b, 0xe4, 0xae, 0x37, 0xea, 0x33, 0x62, 0xcb, 0x4d, 0x4a, 0x11, 0x20, 0xc3, 0x5a, 0xac,
|
||||
0x92, 0xf0, 0x27, 0xe0, 0x8a, 0x7e, 0x5f, 0x72, 0xf4, 0xc5, 0x9e, 0xaa, 0xf5, 0x8f, 0x64, 0xe3,
|
||||
0x19, 0x39, 0xd2, 0x73, 0x10, 0xaf, 0x3e, 0x5c, 0xb6, 0xa5, 0x64, 0x3a, 0x2b, 0x70, 0x64, 0x58,
|
||||
0xb9, 0xbd, 0xe6, 0xc3, 0x57, 0x5f, 0x6e, 0x4c, 0x0d, 0xbe, 0xdc, 0x98, 0x7a, 0x75, 0xb6, 0x61,
|
||||
0x0c, 0xce, 0x36, 0x8c, 0xdf, 0x9d, 0x6f, 0x4c, 0xfd, 0xf1, 0x7c, 0xc3, 0x18, 0x9c, 0x6f, 0x4c,
|
||||
0xfd, 0xfb, 0x7c, 0x63, 0xea, 0xd9, 0x3b, 0xff, 0xc7, 0x9f, 0x1e, 0xba, 0x5c, 0x0f, 0x2e, 0xab,
|
||||
0x3f, 0x3f, 0xde, 0xff, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe5, 0x04, 0x99, 0x1a, 0x13,
|
||||
0x00, 0x00,
|
||||
// 2043 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6f, 0x24, 0x47,
|
||||
0x15, 0x77, 0x7b, 0xbf, 0xec, 0xf2, 0x77, 0x79, 0xbd, 0xdb, 0xf1, 0x26, 0x53, 0x93, 0x66, 0x36,
|
||||
0x38, 0x51, 0xe2, 0xdd, 0x75, 0x10, 0x12, 0x2b, 0x16, 0xc8, 0xd8, 0xb1, 0x58, 0x16, 0x67, 0x47,
|
||||
0xed, 0x85, 0x15, 0x01, 0xa9, 0xe9, 0xe9, 0xae, 0x99, 0xa9, 0xb8, 0xbf, 0xa8, 0xea, 0x59, 0x7b,
|
||||
0xf6, 0x10, 0x2d, 0x17, 0x04, 0x22, 0x07, 0x64, 0x0e, 0xdc, 0x50, 0x24, 0x10, 0x82, 0xfc, 0x03,
|
||||
0x48, 0xfc, 0x05, 0x7b, 0x41, 0x9e, 0x13, 0x42, 0x1c, 0x4a, 0x8a, 0xf7, 0x36, 0xc7, 0x3e, 0xfa,
|
||||
0x84, 0xaa, 0xaa, 0xbb, 0xa7, 0xbb, 0x67, 0x22, 0x21, 0x71, 0x9b, 0xfa, 0xfd, 0x5e, 0xbd, 0xf7,
|
||||
0xeb, 0x57, 0xaf, 0x5e, 0xbf, 0x1e, 0xd0, 0xf0, 0x48, 0xfb, 0x8e, 0x13, 0x06, 0x1d, 0xd2, 0xbd,
|
||||
0xd3, 0x09, 0x3d, 0x17, 0x53, 0xb5, 0xe8, 0x53, 0x3b, 0x26, 0x61, 0xb0, 0x1d, 0xd1, 0x30, 0x0e,
|
||||
0xe1, 0x55, 0x05, 0x6e, 0xde, 0x9a, 0xb0, 0x8e, 0x07, 0x11, 0x56, 0x46, 0x9b, 0x1b, 0x05, 0x92,
|
||||
0x91, 0xe7, 0x19, 0xbc, 0x59, 0x80, 0xa3, 0xbe, 0xe7, 0x85, 0xd4, 0xc5, 0x34, 0xe5, 0xb6, 0x0a,
|
||||
0xdc, 0x33, 0x4c, 0x19, 0x09, 0x03, 0x12, 0x74, 0xa7, 0x28, 0xd8, 0x44, 0x05, 0xcb, 0xb6, 0x17,
|
||||
0x3a, 0x47, 0x55, 0x57, 0x50, 0x18, 0x74, 0xd8, 0x1d, 0x21, 0x88, 0xa5, 0xd8, 0xeb, 0x29, 0xe6,
|
||||
0x84, 0xd1, 0x80, 0xda, 0x41, 0x17, 0xfb, 0x38, 0xee, 0x85, 0x6e, 0xca, 0xce, 0xe3, 0x93, 0x58,
|
||||
0xfd, 0x34, 0xfe, 0x75, 0x09, 0xbc, 0xb6, 0x2f, 0x9f, 0x67, 0x0f, 0x3f, 0x23, 0x0e, 0xde, 0x2d,
|
||||
0x2a, 0x80, 0x5f, 0x68, 0x60, 0xde, 0x95, 0xb8, 0x45, 0x5c, 0x5d, 0xab, 0x6b, 0x5b, 0x8b, 0xcd,
|
||||
0xcf, 0xb4, 0x97, 0x1c, 0xcd, 0xfc, 0x87, 0xa3, 0x6f, 0x74, 0x49, 0xdc, 0xeb, 0xb7, 0xb7, 0x9d,
|
||||
0xd0, 0xbf, 0xc3, 0x06, 0x81, 0x13, 0xf7, 0x48, 0xd0, 0x2d, 0xfc, 0x12, 0x12, 0x64, 0x10, 0x27,
|
||||
0xf4, 0xb6, 0x95, 0xf7, 0x87, 0x7b, 0xe7, 0x1c, 0xcd, 0x65, 0xbf, 0x47, 0x1c, 0xcd, 0xb9, 0xe9,
|
||||
0xef, 0x84, 0xa3, 0xa5, 0x13, 0xdf, 0xbb, 0x6f, 0x10, 0xf7, 0x5d, 0x3b, 0x8e, 0xa9, 0x31, 0x3a,
|
||||
0x6b, 0x5c, 0x4b, 0x7f, 0x27, 0x67, 0x8d, 0xdc, 0xee, 0xd7, 0xc3, 0x86, 0x76, 0x3a, 0x6c, 0xe4,
|
||||
0x3e, 0xcc, 0x8c, 0x71, 0xe1, 0x5f, 0x34, 0xb0, 0x44, 0x82, 0x98, 0x86, 0x6e, 0xdf, 0xc1, 0xae,
|
||||
0xd5, 0x1e, 0xe8, 0xb3, 0x52, 0xf0, 0x8b, 0xff, 0x4b, 0xf0, 0x88, 0xa3, 0xc5, 0xb1, 0xd7, 0xe6,
|
||||
0x20, 0xe1, 0xe8, 0xa6, 0x12, 0x5a, 0x00, 0x73, 0xc9, 0x6b, 0x13, 0xa8, 0x10, 0x6c, 0x96, 0x3c,
|
||||
0x40, 0x07, 0xac, 0xe3, 0xc0, 0xa1, 0x83, 0x48, 0xe4, 0xd8, 0x8a, 0x6c, 0xc6, 0x8e, 0x43, 0xea,
|
||||
0xea, 0x97, 0xea, 0xda, 0xd6, 0x7c, 0x73, 0x67, 0xc4, 0x11, 0x1c, 0xd3, 0xad, 0x94, 0x4d, 0x38,
|
||||
0xd2, 0x65, 0xd8, 0x49, 0xca, 0x30, 0xa7, 0xd8, 0x1b, 0x7f, 0xac, 0x83, 0x75, 0x75, 0xb0, 0xe5,
|
||||
0x23, 0x3d, 0x04, 0xb3, 0xe9, 0x51, 0xce, 0x37, 0x77, 0xcf, 0x39, 0x9a, 0x95, 0x8f, 0x38, 0x4b,
|
||||
0x44, 0x84, 0x5a, 0xe9, 0x04, 0xea, 0x41, 0xe8, 0xe2, 0x8e, 0xdd, 0xf7, 0xe2, 0xfb, 0x46, 0x4c,
|
||||
0xfb, 0xb8, 0x78, 0x24, 0xa7, 0xc3, 0xc6, 0xec, 0xc3, 0xbd, 0xcf, 0xc5, 0xb3, 0xcd, 0x12, 0x17,
|
||||
0xfe, 0x08, 0x5c, 0xf1, 0xec, 0x36, 0xf6, 0x64, 0xc6, 0xe7, 0x9b, 0xdf, 0x1d, 0x71, 0xa4, 0x80,
|
||||
0x84, 0xa3, 0xba, 0x74, 0x2a, 0x57, 0xa9, 0x5f, 0x8a, 0x59, 0x6c, 0xd3, 0xf8, 0xbe, 0xd1, 0xb1,
|
||||
0x3d, 0x26, 0xdd, 0x82, 0x31, 0xfd, 0x62, 0xd8, 0x98, 0x31, 0xd5, 0x66, 0xd8, 0x05, 0x2b, 0x1d,
|
||||
0xe2, 0x61, 0x36, 0x60, 0x31, 0xf6, 0x2d, 0x51, 0xdf, 0x32, 0x49, 0xcb, 0x3b, 0x70, 0xbb, 0xc3,
|
||||
0xb6, 0xf7, 0x73, 0xea, 0xc9, 0x20, 0xc2, 0xcd, 0x77, 0x46, 0x1c, 0x2d, 0x77, 0x4a, 0x58, 0xc2,
|
||||
0xd1, 0x75, 0x19, 0xbd, 0x0c, 0x1b, 0x66, 0xc5, 0x0e, 0x1e, 0x80, 0xcb, 0x91, 0x1d, 0xf7, 0xf4,
|
||||
0xcb, 0x52, 0xfe, 0xb7, 0x46, 0x1c, 0xc9, 0x75, 0xc2, 0xd1, 0x2d, 0xb9, 0x5f, 0x2c, 0x52, 0xf1,
|
||||
0x79, 0x4a, 0x3e, 0x15, 0xc2, 0xe7, 0x73, 0xe6, 0xe2, 0xac, 0xa1, 0x7d, 0x6a, 0xca, 0x6d, 0xb0,
|
||||
0x05, 0x2e, 0x4b, 0xb1, 0x57, 0x52, 0xb1, 0xea, 0xf6, 0x6e, 0xab, 0xe3, 0x90, 0x62, 0xb7, 0x44,
|
||||
0x88, 0x58, 0x49, 0x5c, 0x91, 0x21, 0xc4, 0x22, 0x2f, 0xa3, 0xf9, 0x7c, 0x65, 0x4a, 0x2b, 0xf8,
|
||||
0x33, 0x70, 0x4d, 0xd5, 0x39, 0xd3, 0xaf, 0xd6, 0x2f, 0x6d, 0x2d, 0xec, 0xbc, 0x59, 0x76, 0x3a,
|
||||
0xe5, 0xf2, 0x36, 0x91, 0x28, 0xfb, 0x11, 0x47, 0xd9, 0xce, 0x84, 0xa3, 0x45, 0x19, 0x4a, 0xad,
|
||||
0x0d, 0x33, 0x23, 0xe0, 0xef, 0x35, 0xb0, 0x46, 0x31, 0x73, 0xec, 0xc0, 0x22, 0x41, 0x8c, 0xe9,
|
||||
0x33, 0xdb, 0xb3, 0x98, 0x7e, 0xad, 0xae, 0x6d, 0x5d, 0x69, 0x76, 0x47, 0x1c, 0xad, 0x28, 0xf2,
|
||||
0x61, 0xca, 0x1d, 0x26, 0x1c, 0xbd, 0x2d, 0x3d, 0x55, 0xf0, 0x6a, 0x8a, 0xde, 0xff, 0xe6, 0xdd,
|
||||
0xbb, 0xc6, 0x05, 0x47, 0x97, 0x48, 0x10, 0x8f, 0xce, 0x1a, 0xd7, 0xa7, 0x99, 0x5f, 0x9c, 0x35,
|
||||
0x2e, 0x0b, 0x3b, 0xb3, 0x1a, 0x04, 0xfe, 0x43, 0x03, 0xb0, 0xc3, 0xac, 0x63, 0x3b, 0x76, 0x7a,
|
||||
0x98, 0x5a, 0x38, 0xb0, 0xdb, 0x1e, 0x76, 0xf5, 0xb9, 0xba, 0xb6, 0x35, 0xd7, 0xfc, 0xad, 0x76,
|
||||
0xce, 0xd1, 0xea, 0xfe, 0xe1, 0x53, 0xc5, 0x7e, 0xa8, 0xc8, 0x11, 0x47, 0xab, 0x1d, 0x56, 0xc6,
|
||||
0x12, 0x8e, 0xde, 0x51, 0x45, 0x50, 0x21, 0xaa, 0x6a, 0xb3, 0x1a, 0xdf, 0x98, 0x6a, 0x28, 0x74,
|
||||
0x0a, 0x8b, 0xd3, 0x61, 0x63, 0x22, 0xac, 0x39, 0x11, 0x14, 0xfe, 0xbd, 0x2c, 0xde, 0xc5, 0x9e,
|
||||
0x3d, 0xb0, 0x98, 0x3e, 0x2f, 0x73, 0xfa, 0x1b, 0x21, 0x7e, 0x25, 0xf7, 0xb2, 0x27, 0xc8, 0x43,
|
||||
0x91, 0xe7, 0xdc, 0x8d, 0x82, 0x12, 0x8e, 0xbe, 0x5e, 0x96, 0xae, 0xf0, 0xaa, 0xf2, 0x7b, 0xa5,
|
||||
0x2c, 0x4f, 0x33, 0xbe, 0x38, 0x6b, 0xcc, 0xde, 0xbb, 0x7b, 0x3a, 0x6c, 0x54, 0xa3, 0x9a, 0xd5,
|
||||
0x98, 0xf0, 0xe7, 0x60, 0x91, 0x74, 0x83, 0x90, 0x62, 0x2b, 0xc2, 0xd4, 0x67, 0x3a, 0x90, 0xf9,
|
||||
0x7e, 0x30, 0xe2, 0x68, 0x41, 0xe1, 0x2d, 0x01, 0x27, 0x1c, 0xdd, 0x50, 0xdd, 0x62, 0x8c, 0xe5,
|
||||
0xe5, 0xbb, 0x5a, 0x05, 0xcd, 0xe2, 0x56, 0xf8, 0x4b, 0x0d, 0x2c, 0xdb, 0xfd, 0x38, 0xb4, 0x82,
|
||||
0x90, 0xfa, 0xb6, 0x47, 0x9e, 0x63, 0x7d, 0x41, 0x06, 0xf9, 0x78, 0xc4, 0xd1, 0x92, 0x60, 0x3e,
|
||||
0xca, 0x88, 0x3c, 0x03, 0x25, 0xf4, 0xab, 0x4e, 0x0e, 0x4e, 0x5a, 0x65, 0xc7, 0x66, 0x96, 0xfd,
|
||||
0xc2, 0x10, 0x2c, 0xf9, 0x24, 0xb0, 0x5c, 0xc2, 0x8e, 0xac, 0x0e, 0xc5, 0x58, 0x5f, 0xac, 0x6b,
|
||||
0x5b, 0x0b, 0x3b, 0x8b, 0xd9, 0xb5, 0x3a, 0x24, 0xcf, 0x71, 0xf3, 0x41, 0x7a, 0x83, 0x16, 0x7c,
|
||||
0x12, 0xec, 0x11, 0x76, 0xb4, 0x4f, 0xb1, 0x50, 0x84, 0xa4, 0xa2, 0x02, 0x56, 0x3c, 0x8a, 0xfa,
|
||||
0x6d, 0xe3, 0xe2, 0xac, 0x71, 0xe9, 0x5e, 0xfd, 0xb6, 0x59, 0xdc, 0x06, 0xbb, 0x00, 0x8c, 0xdf,
|
||||
0xf3, 0xfa, 0x92, 0x8c, 0x86, 0xb2, 0x68, 0x3f, 0xce, 0x99, 0xf2, 0x15, 0x7e, 0x2b, 0x15, 0x50,
|
||||
0xd8, 0x9a, 0x70, 0xb4, 0x2a, 0xe3, 0x8f, 0x21, 0xc3, 0x2c, 0xf0, 0xf0, 0x01, 0xb8, 0xe6, 0x84,
|
||||
0x11, 0xc1, 0x94, 0xe9, 0xcb, 0xb2, 0xda, 0xbe, 0x26, 0x7a, 0x40, 0x0a, 0xe5, 0xaf, 0xd9, 0x74,
|
||||
0x9d, 0xd5, 0x8d, 0x99, 0x19, 0xc0, 0x7f, 0x6a, 0xe0, 0x86, 0x98, 0x30, 0x30, 0xb5, 0x7c, 0xfb,
|
||||
0xc4, 0x8a, 0x70, 0xe0, 0x92, 0xa0, 0x6b, 0x1d, 0x91, 0xb6, 0xbe, 0x22, 0xdd, 0xfd, 0x41, 0x14,
|
||||
0xef, 0x7a, 0x4b, 0x9a, 0x1c, 0xd8, 0x27, 0x2d, 0x65, 0xf0, 0x88, 0x34, 0x47, 0x1c, 0xad, 0x47,
|
||||
0x93, 0x70, 0xc2, 0xd1, 0x6b, 0xaa, 0x89, 0x4e, 0x72, 0x85, 0xb2, 0x9d, 0xba, 0x75, 0x3a, 0x7c,
|
||||
0x3a, 0x6c, 0x4c, 0x8b, 0x6f, 0x4e, 0xb1, 0x6d, 0x8b, 0x74, 0xf4, 0x6c, 0xd6, 0x13, 0xe9, 0x58,
|
||||
0x1d, 0xa7, 0x23, 0x85, 0xf2, 0x74, 0xa4, 0xeb, 0x71, 0x3a, 0x52, 0x00, 0x7e, 0x00, 0xae, 0xc8,
|
||||
0x59, 0x4b, 0x5f, 0x93, 0xbd, 0x7c, 0x2d, 0x3b, 0x31, 0x11, 0xff, 0xb1, 0x20, 0x9a, 0xba, 0x78,
|
||||
0xd9, 0x49, 0x9b, 0x84, 0xa3, 0x05, 0xe9, 0x4d, 0xae, 0x0c, 0x53, 0xa1, 0xf0, 0x11, 0x58, 0x4a,
|
||||
0x2f, 0x94, 0x8b, 0x3d, 0x1c, 0x63, 0x1d, 0xca, 0x62, 0x7f, 0x4b, 0x4e, 0x16, 0x92, 0xd8, 0x93,
|
||||
0x78, 0xc2, 0x11, 0x2c, 0x5c, 0x29, 0x05, 0x1a, 0x66, 0xc9, 0x06, 0x9e, 0x00, 0x5d, 0xf6, 0xe9,
|
||||
0x88, 0x86, 0x5d, 0x8a, 0x19, 0x2b, 0x36, 0xec, 0x75, 0xf9, 0x7c, 0xe2, 0xe5, 0xbb, 0x21, 0x6c,
|
||||
0x5a, 0xa9, 0x49, 0xb1, 0x6d, 0xab, 0xd7, 0xd9, 0x54, 0x36, 0x7f, 0xf6, 0xe9, 0x9b, 0xe1, 0x21,
|
||||
0x58, 0x4e, 0xeb, 0x22, 0xb2, 0xfb, 0x0c, 0x5b, 0x4c, 0xbf, 0x2e, 0xe3, 0xbd, 0x27, 0x9e, 0x43,
|
||||
0x31, 0x2d, 0x41, 0x1c, 0xe6, 0xcf, 0x51, 0x04, 0x73, 0xef, 0x25, 0x53, 0x88, 0xc1, 0x92, 0xa8,
|
||||
0x32, 0x91, 0x54, 0x8f, 0x38, 0x31, 0xd3, 0x37, 0xa4, 0xcf, 0xef, 0x09, 0x9f, 0xbe, 0x7d, 0xb2,
|
||||
0x9b, 0xe1, 0xe3, 0x5b, 0x57, 0x00, 0xa7, 0x76, 0x40, 0xd5, 0xe9, 0xcc, 0xd2, 0x6e, 0xe8, 0x82,
|
||||
0xeb, 0x2e, 0x61, 0xa2, 0x33, 0x5b, 0x2c, 0xb2, 0x29, 0xc3, 0x96, 0x1c, 0x00, 0xf4, 0x1b, 0xf2,
|
||||
0x24, 0xe4, 0xc8, 0x95, 0xf2, 0x87, 0x92, 0x96, 0xa3, 0x45, 0x3e, 0x72, 0x4d, 0x52, 0x86, 0x39,
|
||||
0xc5, 0xbe, 0x18, 0x25, 0xc6, 0x7e, 0x64, 0x91, 0xc0, 0xc5, 0x27, 0x98, 0xe9, 0x37, 0x27, 0xa2,
|
||||
0x3c, 0xc1, 0x7e, 0xf4, 0x50, 0xb1, 0xd5, 0x28, 0x05, 0x6a, 0x1c, 0xa5, 0x00, 0xc2, 0x1d, 0x70,
|
||||
0x55, 0x1e, 0x80, 0xab, 0xeb, 0xd2, 0xef, 0xe6, 0x88, 0xa3, 0x14, 0xc9, 0xdf, 0xf0, 0x6a, 0x69,
|
||||
0x98, 0x29, 0x0e, 0x63, 0x70, 0xf3, 0x18, 0xdb, 0x47, 0x96, 0xa8, 0x6a, 0x2b, 0xee, 0x51, 0xcc,
|
||||
0x7a, 0xa1, 0xe7, 0x5a, 0x91, 0x13, 0xeb, 0xaf, 0xc9, 0x84, 0x8b, 0xf6, 0x7e, 0x5d, 0x98, 0x7c,
|
||||
0xdf, 0x66, 0xbd, 0x27, 0x99, 0x41, 0xcb, 0x89, 0x13, 0x8e, 0x36, 0xa5, 0xcb, 0x69, 0x64, 0x7e,
|
||||
0xa8, 0x53, 0xb7, 0xc2, 0x5d, 0xb0, 0xe0, 0xdb, 0xf4, 0x08, 0x53, 0x2b, 0xb0, 0x7d, 0xac, 0x6f,
|
||||
0xca, 0xe1, 0xca, 0x10, 0xed, 0x4c, 0xc1, 0x1f, 0xd9, 0x3e, 0xce, 0xdb, 0xd9, 0x18, 0x32, 0xcc,
|
||||
0x02, 0x0f, 0x07, 0x60, 0x53, 0x7c, 0xc4, 0x58, 0xe1, 0x71, 0x80, 0x29, 0xeb, 0x91, 0xc8, 0xea,
|
||||
0xd0, 0xd0, 0xb7, 0x22, 0x9b, 0xe2, 0x20, 0xd6, 0x6f, 0xc9, 0x14, 0x7c, 0x7b, 0xc4, 0xd1, 0x4d,
|
||||
0x61, 0xf5, 0x38, 0x33, 0xda, 0xa7, 0xa1, 0xdf, 0x92, 0x26, 0x09, 0x47, 0x6f, 0x64, 0x1d, 0x6f,
|
||||
0x1a, 0x6f, 0x98, 0x5f, 0xb5, 0x13, 0xfe, 0x4a, 0x03, 0x6b, 0x7e, 0xe8, 0x5a, 0x31, 0xf1, 0xb1,
|
||||
0x75, 0x4c, 0x02, 0x37, 0x3c, 0xb6, 0x98, 0xfe, 0xba, 0x4c, 0xd8, 0x4f, 0xcf, 0x39, 0x5a, 0x33,
|
||||
0xed, 0xe3, 0x83, 0xd0, 0x7d, 0x42, 0x7c, 0xfc, 0x54, 0xb2, 0xe2, 0x1d, 0xbe, 0xec, 0x97, 0x90,
|
||||
0x7c, 0x04, 0x2d, 0xc3, 0x59, 0xe6, 0x4e, 0x87, 0x8d, 0x49, 0x2f, 0x66, 0xc5, 0x07, 0x7c, 0xa1,
|
||||
0x81, 0x8d, 0xf4, 0x9a, 0x38, 0x7d, 0x2a, 0xb4, 0x59, 0xc7, 0x94, 0xc4, 0x98, 0xe9, 0x6f, 0x48,
|
||||
0x31, 0x3f, 0x14, 0xad, 0x57, 0x15, 0x7c, 0xca, 0x3f, 0x95, 0x74, 0xc2, 0xd1, 0xed, 0xc2, 0xad,
|
||||
0x29, 0x71, 0x85, 0xcb, 0xb3, 0x53, 0xb8, 0x3b, 0xda, 0x8e, 0x39, 0xcd, 0x93, 0x68, 0x62, 0x59,
|
||||
0x6d, 0x77, 0xc4, 0x17, 0x93, 0x5e, 0x1b, 0x37, 0xb1, 0x94, 0xd8, 0x17, 0x78, 0x7e, 0xf9, 0x8b,
|
||||
0xa0, 0x61, 0x96, 0x6c, 0xa0, 0x07, 0x56, 0xe5, 0x97, 0xac, 0x25, 0x7a, 0x81, 0xa5, 0xfa, 0x2b,
|
||||
0x92, 0xfd, 0xf5, 0x46, 0xd6, 0x5f, 0x9b, 0x82, 0x1f, 0x37, 0x59, 0x39, 0xdc, 0xb7, 0x4b, 0x58,
|
||||
0x9e, 0xd9, 0x32, 0x6c, 0x98, 0x15, 0x3b, 0xf8, 0x99, 0x06, 0xd6, 0x64, 0x09, 0xc9, 0x0f, 0x61,
|
||||
0x4b, 0x7d, 0x09, 0xeb, 0x75, 0x19, 0x6f, 0x5d, 0x7c, 0x48, 0xec, 0x86, 0xd1, 0xc0, 0x14, 0xdc,
|
||||
0x81, 0xa4, 0x9a, 0x8f, 0xc4, 0x28, 0xe6, 0x94, 0xc1, 0x84, 0xa3, 0xad, 0xbc, 0x8c, 0x0a, 0x78,
|
||||
0x21, 0x8d, 0x2c, 0xb6, 0x03, 0xd7, 0xa6, 0xae, 0x78, 0xff, 0xcf, 0x65, 0x0b, 0xb3, 0xea, 0x08,
|
||||
0xfe, 0x59, 0xc8, 0xb1, 0x45, 0x03, 0xc5, 0x01, 0x23, 0x31, 0x79, 0x26, 0x32, 0xaa, 0xbf, 0x29,
|
||||
0xd3, 0x79, 0x22, 0xe6, 0xc2, 0x5d, 0x9b, 0xe1, 0xc3, 0x8c, 0xdb, 0x97, 0x73, 0xa1, 0x53, 0x86,
|
||||
0x12, 0x8e, 0x36, 0x94, 0x98, 0x32, 0x2e, 0x66, 0xa0, 0x09, 0xdb, 0x49, 0x48, 0x8c, 0x81, 0x95,
|
||||
0x20, 0x66, 0xc5, 0x86, 0xc1, 0x3f, 0x69, 0x60, 0xb5, 0x13, 0x7a, 0x5e, 0x78, 0x6c, 0x7d, 0xd2,
|
||||
0x0f, 0x1c, 0x31, 0x8e, 0x30, 0xdd, 0x18, 0xab, 0xfc, 0x41, 0x06, 0x7e, 0xc0, 0xf6, 0x08, 0x65,
|
||||
0x42, 0xe5, 0x27, 0x65, 0x28, 0x57, 0x59, 0xc1, 0xa5, 0xca, 0xaa, 0xed, 0x24, 0x24, 0x54, 0x56,
|
||||
0x82, 0x98, 0x2b, 0x4a, 0x51, 0x0e, 0xc3, 0x23, 0x30, 0x4f, 0xb1, 0xed, 0x5a, 0x61, 0xe0, 0x0d,
|
||||
0xf4, 0xbf, 0xee, 0x4b, 0x79, 0x07, 0xe7, 0x1c, 0xc1, 0x3d, 0x1c, 0x51, 0xec, 0xd8, 0x31, 0x76,
|
||||
0x4d, 0x6c, 0xbb, 0x8f, 0x03, 0x6f, 0x30, 0xe2, 0x48, 0x7b, 0x2f, 0xff, 0x7a, 0xa7, 0xa1, 0x1c,
|
||||
0x0f, 0xdf, 0x0d, 0x7d, 0x22, 0x7a, 0x75, 0x3c, 0x90, 0x5f, 0xef, 0x13, 0xa8, 0xae, 0x99, 0x73,
|
||||
0x34, 0x75, 0x00, 0x7f, 0x01, 0xd6, 0x4a, 0x33, 0xa3, 0xec, 0x9f, 0x7f, 0x13, 0x41, 0xb5, 0xe6,
|
||||
0x87, 0xe7, 0x1c, 0xe9, 0xe3, 0xa0, 0x07, 0xe3, 0xc9, 0xaf, 0xe5, 0xc4, 0x59, 0xe8, 0x5a, 0x75,
|
||||
0x70, 0x6c, 0x39, 0x71, 0x41, 0x81, 0xae, 0x99, 0xcb, 0x65, 0x12, 0xfe, 0x04, 0x5c, 0x53, 0xef,
|
||||
0x4b, 0xa6, 0x7f, 0xb1, 0x2f, 0xef, 0xfa, 0x77, 0x44, 0xe3, 0x19, 0x07, 0x52, 0x73, 0x10, 0x2b,
|
||||
0x3f, 0x5c, 0xba, 0xa5, 0xe0, 0x3a, 0xbd, 0xe0, 0xba, 0x66, 0x66, 0xfe, 0x9a, 0x8f, 0x5e, 0x7e,
|
||||
0x59, 0x9b, 0x19, 0x7e, 0x59, 0x9b, 0x79, 0x79, 0x5e, 0xd3, 0x86, 0xe7, 0x35, 0xed, 0x77, 0xaf,
|
||||
0x6a, 0x33, 0x9f, 0xbf, 0xaa, 0x69, 0xc3, 0x57, 0xb5, 0x99, 0x7f, 0xbf, 0xaa, 0xcd, 0x7c, 0xfc,
|
||||
0xf6, 0xff, 0xf0, 0x7f, 0x89, 0xba, 0xae, 0xed, 0xab, 0xf2, 0x7f, 0x93, 0xf7, 0xff, 0x1b, 0x00,
|
||||
0x00, 0xff, 0xff, 0x3e, 0xb6, 0x85, 0xe6, 0x55, 0x13, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *FolderDeviceConfiguration) Marshal() (dAtA []byte, err error) {
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
// put the newest on top for readability.
|
||||
var (
|
||||
migrations = migrationSet{
|
||||
{35, migrateToConfigV35},
|
||||
{34, migrateToConfigV34},
|
||||
{33, migrateToConfigV33},
|
||||
{32, migrateToConfigV32},
|
||||
{31, migrateToConfigV31},
|
||||
@@ -92,6 +94,29 @@ func (m migration) apply(cfg *Configuration) {
|
||||
cfg.Version = m.targetVersion
|
||||
}
|
||||
|
||||
func migrateToConfigV35(cfg *Configuration) {
|
||||
for i, fcfg := range cfg.Folders {
|
||||
params := fcfg.Versioning.Params
|
||||
if params["fsType"] != "" {
|
||||
var fsType fs.FilesystemType
|
||||
_ = fsType.UnmarshalText([]byte(params["fsType"]))
|
||||
cfg.Folders[i].Versioning.FSType = fsType
|
||||
}
|
||||
if params["versionsPath"] != "" && params["fsPath"] == "" {
|
||||
params["fsPath"] = params["versionsPath"]
|
||||
}
|
||||
cfg.Folders[i].Versioning.FSPath = params["fsPath"]
|
||||
delete(cfg.Folders[i].Versioning.Params, "fsType")
|
||||
delete(cfg.Folders[i].Versioning.Params, "fsPath")
|
||||
delete(cfg.Folders[i].Versioning.Params, "versionsPath")
|
||||
}
|
||||
}
|
||||
|
||||
func migrateToConfigV34(cfg *Configuration) {
|
||||
cfg.Defaults.Folder.Path = cfg.Options.DeprecatedDefaultFolderPath
|
||||
cfg.Options.DeprecatedDefaultFolderPath = ""
|
||||
}
|
||||
|
||||
func migrateToConfigV33(cfg *Configuration) {
|
||||
for i := range cfg.Devices {
|
||||
cfg.Devices[i].DeprecatedPendingFolders = nil
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
github_com_gogo_protobuf_types "github.com/gogo/protobuf/types"
|
||||
_ "github.com/golang/protobuf/ptypes/timestamp"
|
||||
github_com_syncthing_syncthing_lib_protocol "github.com/syncthing/syncthing/lib/protocol"
|
||||
_ "github.com/syncthing/syncthing/proto/ext"
|
||||
_ "google.golang.org/protobuf/types/known/timestamppb"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
|
||||
@@ -25,55 +25,55 @@ var _ = math.Inf
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type OptionsConfiguration struct {
|
||||
RawListenAddresses []string `protobuf:"bytes,1,rep,name=listen_addresses,json=listenAddresses,proto3" json:"listenAddresses" xml:"listenAddress" default:"default"`
|
||||
RawGlobalAnnServers []string `protobuf:"bytes,2,rep,name=global_discovery_servers,json=globalDiscoveryServers,proto3" json:"globalAnnounceServers" xml:"globalAnnounceServer" default:"default"`
|
||||
GlobalAnnEnabled bool `protobuf:"varint,3,opt,name=global_discovery_enabled,json=globalDiscoveryEnabled,proto3" json:"globalAnnounceEnabled" xml:"globalAnnounceEnabled" default:"true"`
|
||||
LocalAnnEnabled bool `protobuf:"varint,4,opt,name=local_discovery_enabled,json=localDiscoveryEnabled,proto3" json:"localAnnounceEnabled" xml:"localAnnounceEnabled" default:"true"`
|
||||
LocalAnnPort int `protobuf:"varint,5,opt,name=local_announce_port,json=localAnnouncePort,proto3,casttype=int" json:"localAnnouncePort" xml:"localAnnouncePort" default:"21027"`
|
||||
LocalAnnMCAddr string `protobuf:"bytes,6,opt,name=local_announce_multicast_address,json=localAnnounceMulticastAddress,proto3" json:"localAnnounceMCAddr" xml:"localAnnounceMCAddr" default:"[ff12::8384]:21027"`
|
||||
MaxSendKbps int `protobuf:"varint,7,opt,name=max_send_kbps,json=maxSendKbps,proto3,casttype=int" json:"maxSendKbps" xml:"maxSendKbps"`
|
||||
MaxRecvKbps int `protobuf:"varint,8,opt,name=max_recv_kbps,json=maxRecvKbps,proto3,casttype=int" json:"maxRecvKbps" xml:"maxRecvKbps"`
|
||||
ReconnectIntervalS int `protobuf:"varint,9,opt,name=reconnection_interval_s,json=reconnectionIntervalS,proto3,casttype=int" json:"reconnectionIntervalS" xml:"reconnectionIntervalS" default:"60"`
|
||||
RelaysEnabled bool `protobuf:"varint,10,opt,name=relays_enabled,json=relaysEnabled,proto3" json:"relaysEnabled" xml:"relaysEnabled" default:"true"`
|
||||
RelayReconnectIntervalM int `protobuf:"varint,11,opt,name=relays_reconnect_interval_m,json=relaysReconnectIntervalM,proto3,casttype=int" json:"relayReconnectIntervalM" xml:"relayReconnectIntervalM" default:"10"`
|
||||
StartBrowser bool `protobuf:"varint,12,opt,name=start_browser,json=startBrowser,proto3" json:"startBrowser" xml:"startBrowser" default:"true"`
|
||||
NATEnabled bool `protobuf:"varint,14,opt,name=nat_traversal_enabled,json=natTraversalEnabled,proto3" json:"natEnabled" xml:"natEnabled" default:"true"`
|
||||
NATLeaseM int `protobuf:"varint,15,opt,name=nat_traversal_lease_m,json=natTraversalLeaseM,proto3,casttype=int" json:"natLeaseMinutes" xml:"natLeaseMinutes" default:"60"`
|
||||
NATRenewalM int `protobuf:"varint,16,opt,name=nat_traversal_renewal_m,json=natTraversalRenewalM,proto3,casttype=int" json:"natRenewalMinutes" xml:"natRenewalMinutes" default:"30"`
|
||||
NATTimeoutS int `protobuf:"varint,17,opt,name=nat_traversal_timeout_s,json=natTraversalTimeoutS,proto3,casttype=int" json:"natTimeoutSeconds" xml:"natTimeoutSeconds" default:"10"`
|
||||
URAccepted int `protobuf:"varint,18,opt,name=usage_reporting_accepted,json=usageReportingAccepted,proto3,casttype=int" json:"urAccepted" xml:"urAccepted"`
|
||||
URSeen int `protobuf:"varint,19,opt,name=usage_reporting_seen,json=usageReportingSeen,proto3,casttype=int" json:"urSeen" xml:"urSeen"`
|
||||
URUniqueID string `protobuf:"bytes,20,opt,name=usage_reporting_unique_id,json=usageReportingUniqueId,proto3" json:"urUniqueId" xml:"urUniqueID"`
|
||||
URURL string `protobuf:"bytes,21,opt,name=usage_reporting_url,json=usageReportingUrl,proto3" json:"urURL" xml:"urURL" default:"https://data.syncthing.net/newdata"`
|
||||
URPostInsecurely bool `protobuf:"varint,22,opt,name=usage_reporting_post_insecurely,json=usageReportingPostInsecurely,proto3" json:"urPostInsecurely" xml:"urPostInsecurely" default:"false"`
|
||||
URInitialDelayS int `protobuf:"varint,23,opt,name=usage_reporting_initial_delay_s,json=usageReportingInitialDelayS,proto3,casttype=int" json:"urInitialDelayS" xml:"urInitialDelayS" default:"1800"`
|
||||
RestartOnWakeup bool `protobuf:"varint,24,opt,name=restart_on_wakeup,json=restartOnWakeup,proto3" json:"restartOnWakeup" xml:"restartOnWakeup" default:"true"`
|
||||
AutoUpgradeIntervalH int `protobuf:"varint,25,opt,name=auto_upgrade_interval_h,json=autoUpgradeIntervalH,proto3,casttype=int" json:"autoUpgradeIntervalH" xml:"autoUpgradeIntervalH" default:"12"`
|
||||
UpgradeToPreReleases bool `protobuf:"varint,26,opt,name=upgrade_to_pre_releases,json=upgradeToPreReleases,proto3" json:"upgradeToPreReleases" xml:"upgradeToPreReleases"`
|
||||
KeepTemporariesH int `protobuf:"varint,27,opt,name=keep_temporaries_h,json=keepTemporariesH,proto3,casttype=int" json:"keepTemporariesH" xml:"keepTemporariesH" default:"24"`
|
||||
CacheIgnoredFiles bool `protobuf:"varint,28,opt,name=cache_ignored_files,json=cacheIgnoredFiles,proto3" json:"cacheIgnoredFiles" xml:"cacheIgnoredFiles" default:"false"`
|
||||
ProgressUpdateIntervalS int `protobuf:"varint,29,opt,name=progress_update_interval_s,json=progressUpdateIntervalS,proto3,casttype=int" json:"progressUpdateIntervalS" xml:"progressUpdateIntervalS" default:"5"`
|
||||
LimitBandwidthInLan bool `protobuf:"varint,30,opt,name=limit_bandwidth_in_lan,json=limitBandwidthInLan,proto3" json:"limitBandwidthInLan" xml:"limitBandwidthInLan" default:"false"`
|
||||
MinHomeDiskFree Size `protobuf:"bytes,31,opt,name=min_home_disk_free,json=minHomeDiskFree,proto3" json:"minHomeDiskFree" xml:"minHomeDiskFree" default:"1 %"`
|
||||
ReleasesURL string `protobuf:"bytes,32,opt,name=releases_url,json=releasesUrl,proto3" json:"releasesURL" xml:"releasesURL" default:"https://upgrades.syncthing.net/meta.json"`
|
||||
AlwaysLocalNets []string `protobuf:"bytes,33,rep,name=always_local_nets,json=alwaysLocalNets,proto3" json:"alwaysLocalNets" xml:"alwaysLocalNet"`
|
||||
OverwriteRemoteDevNames bool `protobuf:"varint,34,opt,name=overwrite_remote_device_names_on_connect,json=overwriteRemoteDeviceNamesOnConnect,proto3" json:"overwriteRemoteDeviceNamesOnConnect" xml:"overwriteRemoteDeviceNamesOnConnect" default:"false"`
|
||||
TempIndexMinBlocks int `protobuf:"varint,35,opt,name=temp_index_min_blocks,json=tempIndexMinBlocks,proto3,casttype=int" json:"tempIndexMinBlocks" xml:"tempIndexMinBlocks" default:"10"`
|
||||
UnackedNotificationIDs []string `protobuf:"bytes,36,rep,name=unacked_notification_ids,json=unackedNotificationIds,proto3" json:"unackedNotificationIDs" xml:"unackedNotificationID"`
|
||||
TrafficClass int `protobuf:"varint,37,opt,name=traffic_class,json=trafficClass,proto3,casttype=int" json:"trafficClass" xml:"trafficClass"`
|
||||
DefaultFolderPath string `protobuf:"bytes,38,opt,name=default_folder_path,json=defaultFolderPath,proto3" json:"defaultFolderPath" xml:"defaultFolderPath" default:"~"`
|
||||
SetLowPriority bool `protobuf:"varint,39,opt,name=set_low_priority,json=setLowPriority,proto3" json:"setLowPriority" xml:"setLowPriority" default:"true"`
|
||||
RawMaxFolderConcurrency int `protobuf:"varint,40,opt,name=max_folder_concurrency,json=maxFolderConcurrency,proto3,casttype=int" json:"maxFolderConcurrency" xml:"maxFolderConcurrency"`
|
||||
CRURL string `protobuf:"bytes,41,opt,name=crash_reporting_url,json=crashReportingUrl,proto3" json:"crURL" xml:"crashReportingURL" default:"https://crash.syncthing.net/newcrash"`
|
||||
CREnabled bool `protobuf:"varint,42,opt,name=crash_reporting_enabled,json=crashReportingEnabled,proto3" json:"crashReportingEnabled" xml:"crashReportingEnabled" default:"true"`
|
||||
StunKeepaliveStartS int `protobuf:"varint,43,opt,name=stun_keepalive_start_s,json=stunKeepaliveStartS,proto3,casttype=int" json:"stunKeepaliveStartS" xml:"stunKeepaliveStartS" default:"180"`
|
||||
StunKeepaliveMinS int `protobuf:"varint,44,opt,name=stun_keepalive_min_s,json=stunKeepaliveMinS,proto3,casttype=int" json:"stunKeepaliveMinS" xml:"stunKeepaliveMinS" default:"20"`
|
||||
RawStunServers []string `protobuf:"bytes,45,rep,name=stun_servers,json=stunServers,proto3" json:"stunServers" xml:"stunServer" default:"default"`
|
||||
DatabaseTuning Tuning `protobuf:"varint,46,opt,name=database_tuning,json=databaseTuning,proto3,enum=config.Tuning" json:"databaseTuning" xml:"databaseTuning" restart:"true"`
|
||||
RawMaxCIRequestKiB int `protobuf:"varint,47,opt,name=max_concurrent_incoming_request_kib,json=maxConcurrentIncomingRequestKib,proto3,casttype=int" json:"maxConcurrentIncomingRequestKiB" xml:"maxConcurrentIncomingRequestKiB"`
|
||||
AnnounceLANAddresses bool `protobuf:"varint,48,opt,name=announce_lan_addresses,json=announceLanAddresses,proto3" json:"announceLANAddresses" xml:"announceLANAddresses" default:"true"`
|
||||
SendFullIndexOnUpgrade bool `protobuf:"varint,49,opt,name=send_full_index_on_upgrade,json=sendFullIndexOnUpgrade,proto3" json:"sendFullIndexOnUpgrade" xml:"sendFullIndexOnUpgrade"`
|
||||
FeatureFlags []string `protobuf:"bytes,50,rep,name=feature_flags,json=featureFlags,proto3" json:"featureFlags" xml:"featureFlag"`
|
||||
RawListenAddresses []string `protobuf:"bytes,1,rep,name=listen_addresses,json=listenAddresses,proto3" json:"listenAddresses" xml:"listenAddress" default:"default"`
|
||||
RawGlobalAnnServers []string `protobuf:"bytes,2,rep,name=global_discovery_servers,json=globalDiscoveryServers,proto3" json:"globalAnnounceServers" xml:"globalAnnounceServer" default:"default"`
|
||||
GlobalAnnEnabled bool `protobuf:"varint,3,opt,name=global_discovery_enabled,json=globalDiscoveryEnabled,proto3" json:"globalAnnounceEnabled" xml:"globalAnnounceEnabled" default:"true"`
|
||||
LocalAnnEnabled bool `protobuf:"varint,4,opt,name=local_discovery_enabled,json=localDiscoveryEnabled,proto3" json:"localAnnounceEnabled" xml:"localAnnounceEnabled" default:"true"`
|
||||
LocalAnnPort int `protobuf:"varint,5,opt,name=local_announce_port,json=localAnnouncePort,proto3,casttype=int" json:"localAnnouncePort" xml:"localAnnouncePort" default:"21027"`
|
||||
LocalAnnMCAddr string `protobuf:"bytes,6,opt,name=local_announce_multicast_address,json=localAnnounceMulticastAddress,proto3" json:"localAnnounceMCAddr" xml:"localAnnounceMCAddr" default:"[ff12::8384]:21027"`
|
||||
MaxSendKbps int `protobuf:"varint,7,opt,name=max_send_kbps,json=maxSendKbps,proto3,casttype=int" json:"maxSendKbps" xml:"maxSendKbps"`
|
||||
MaxRecvKbps int `protobuf:"varint,8,opt,name=max_recv_kbps,json=maxRecvKbps,proto3,casttype=int" json:"maxRecvKbps" xml:"maxRecvKbps"`
|
||||
ReconnectIntervalS int `protobuf:"varint,9,opt,name=reconnection_interval_s,json=reconnectionIntervalS,proto3,casttype=int" json:"reconnectionIntervalS" xml:"reconnectionIntervalS" default:"60"`
|
||||
RelaysEnabled bool `protobuf:"varint,10,opt,name=relays_enabled,json=relaysEnabled,proto3" json:"relaysEnabled" xml:"relaysEnabled" default:"true"`
|
||||
RelayReconnectIntervalM int `protobuf:"varint,11,opt,name=relays_reconnect_interval_m,json=relaysReconnectIntervalM,proto3,casttype=int" json:"relayReconnectIntervalM" xml:"relayReconnectIntervalM" default:"10"`
|
||||
StartBrowser bool `protobuf:"varint,12,opt,name=start_browser,json=startBrowser,proto3" json:"startBrowser" xml:"startBrowser" default:"true"`
|
||||
NATEnabled bool `protobuf:"varint,14,opt,name=nat_traversal_enabled,json=natTraversalEnabled,proto3" json:"natEnabled" xml:"natEnabled" default:"true"`
|
||||
NATLeaseM int `protobuf:"varint,15,opt,name=nat_traversal_lease_m,json=natTraversalLeaseM,proto3,casttype=int" json:"natLeaseMinutes" xml:"natLeaseMinutes" default:"60"`
|
||||
NATRenewalM int `protobuf:"varint,16,opt,name=nat_traversal_renewal_m,json=natTraversalRenewalM,proto3,casttype=int" json:"natRenewalMinutes" xml:"natRenewalMinutes" default:"30"`
|
||||
NATTimeoutS int `protobuf:"varint,17,opt,name=nat_traversal_timeout_s,json=natTraversalTimeoutS,proto3,casttype=int" json:"natTimeoutSeconds" xml:"natTimeoutSeconds" default:"10"`
|
||||
URAccepted int `protobuf:"varint,18,opt,name=usage_reporting_accepted,json=usageReportingAccepted,proto3,casttype=int" json:"urAccepted" xml:"urAccepted"`
|
||||
URSeen int `protobuf:"varint,19,opt,name=usage_reporting_seen,json=usageReportingSeen,proto3,casttype=int" json:"urSeen" xml:"urSeen"`
|
||||
URUniqueID string `protobuf:"bytes,20,opt,name=usage_reporting_unique_id,json=usageReportingUniqueId,proto3" json:"urUniqueId" xml:"urUniqueID"`
|
||||
URURL string `protobuf:"bytes,21,opt,name=usage_reporting_url,json=usageReportingUrl,proto3" json:"urURL" xml:"urURL" default:"https://data.syncthing.net/newdata"`
|
||||
URPostInsecurely bool `protobuf:"varint,22,opt,name=usage_reporting_post_insecurely,json=usageReportingPostInsecurely,proto3" json:"urPostInsecurely" xml:"urPostInsecurely" default:"false"`
|
||||
URInitialDelayS int `protobuf:"varint,23,opt,name=usage_reporting_initial_delay_s,json=usageReportingInitialDelayS,proto3,casttype=int" json:"urInitialDelayS" xml:"urInitialDelayS" default:"1800"`
|
||||
RestartOnWakeup bool `protobuf:"varint,24,opt,name=restart_on_wakeup,json=restartOnWakeup,proto3" json:"restartOnWakeup" xml:"restartOnWakeup" default:"true"`
|
||||
AutoUpgradeIntervalH int `protobuf:"varint,25,opt,name=auto_upgrade_interval_h,json=autoUpgradeIntervalH,proto3,casttype=int" json:"autoUpgradeIntervalH" xml:"autoUpgradeIntervalH" default:"12"`
|
||||
UpgradeToPreReleases bool `protobuf:"varint,26,opt,name=upgrade_to_pre_releases,json=upgradeToPreReleases,proto3" json:"upgradeToPreReleases" xml:"upgradeToPreReleases"`
|
||||
KeepTemporariesH int `protobuf:"varint,27,opt,name=keep_temporaries_h,json=keepTemporariesH,proto3,casttype=int" json:"keepTemporariesH" xml:"keepTemporariesH" default:"24"`
|
||||
CacheIgnoredFiles bool `protobuf:"varint,28,opt,name=cache_ignored_files,json=cacheIgnoredFiles,proto3" json:"cacheIgnoredFiles" xml:"cacheIgnoredFiles" default:"false"`
|
||||
ProgressUpdateIntervalS int `protobuf:"varint,29,opt,name=progress_update_interval_s,json=progressUpdateIntervalS,proto3,casttype=int" json:"progressUpdateIntervalS" xml:"progressUpdateIntervalS" default:"5"`
|
||||
LimitBandwidthInLan bool `protobuf:"varint,30,opt,name=limit_bandwidth_in_lan,json=limitBandwidthInLan,proto3" json:"limitBandwidthInLan" xml:"limitBandwidthInLan" default:"false"`
|
||||
MinHomeDiskFree Size `protobuf:"bytes,31,opt,name=min_home_disk_free,json=minHomeDiskFree,proto3" json:"minHomeDiskFree" xml:"minHomeDiskFree" default:"1 %"`
|
||||
ReleasesURL string `protobuf:"bytes,32,opt,name=releases_url,json=releasesUrl,proto3" json:"releasesURL" xml:"releasesURL" default:"https://upgrades.syncthing.net/meta.json"`
|
||||
AlwaysLocalNets []string `protobuf:"bytes,33,rep,name=always_local_nets,json=alwaysLocalNets,proto3" json:"alwaysLocalNets" xml:"alwaysLocalNet"`
|
||||
OverwriteRemoteDevNames bool `protobuf:"varint,34,opt,name=overwrite_remote_device_names_on_connect,json=overwriteRemoteDeviceNamesOnConnect,proto3" json:"overwriteRemoteDeviceNamesOnConnect" xml:"overwriteRemoteDeviceNamesOnConnect" default:"false"`
|
||||
TempIndexMinBlocks int `protobuf:"varint,35,opt,name=temp_index_min_blocks,json=tempIndexMinBlocks,proto3,casttype=int" json:"tempIndexMinBlocks" xml:"tempIndexMinBlocks" default:"10"`
|
||||
UnackedNotificationIDs []string `protobuf:"bytes,36,rep,name=unacked_notification_ids,json=unackedNotificationIds,proto3" json:"unackedNotificationIDs" xml:"unackedNotificationID"`
|
||||
TrafficClass int `protobuf:"varint,37,opt,name=traffic_class,json=trafficClass,proto3,casttype=int" json:"trafficClass" xml:"trafficClass"`
|
||||
DeprecatedDefaultFolderPath string `protobuf:"bytes,38,opt,name=default_folder_path,json=defaultFolderPath,proto3" json:"-" xml:"defaultFolderPath,omitempty"` // Deprecated: Do not use.
|
||||
SetLowPriority bool `protobuf:"varint,39,opt,name=set_low_priority,json=setLowPriority,proto3" json:"setLowPriority" xml:"setLowPriority" default:"true"`
|
||||
RawMaxFolderConcurrency int `protobuf:"varint,40,opt,name=max_folder_concurrency,json=maxFolderConcurrency,proto3,casttype=int" json:"maxFolderConcurrency" xml:"maxFolderConcurrency"`
|
||||
CRURL string `protobuf:"bytes,41,opt,name=crash_reporting_url,json=crashReportingUrl,proto3" json:"crURL" xml:"crashReportingURL" default:"https://crash.syncthing.net/newcrash"`
|
||||
CREnabled bool `protobuf:"varint,42,opt,name=crash_reporting_enabled,json=crashReportingEnabled,proto3" json:"crashReportingEnabled" xml:"crashReportingEnabled" default:"true"`
|
||||
StunKeepaliveStartS int `protobuf:"varint,43,opt,name=stun_keepalive_start_s,json=stunKeepaliveStartS,proto3,casttype=int" json:"stunKeepaliveStartS" xml:"stunKeepaliveStartS" default:"180"`
|
||||
StunKeepaliveMinS int `protobuf:"varint,44,opt,name=stun_keepalive_min_s,json=stunKeepaliveMinS,proto3,casttype=int" json:"stunKeepaliveMinS" xml:"stunKeepaliveMinS" default:"20"`
|
||||
RawStunServers []string `protobuf:"bytes,45,rep,name=stun_servers,json=stunServers,proto3" json:"stunServers" xml:"stunServer" default:"default"`
|
||||
DatabaseTuning Tuning `protobuf:"varint,46,opt,name=database_tuning,json=databaseTuning,proto3,enum=config.Tuning" json:"databaseTuning" xml:"databaseTuning" restart:"true"`
|
||||
RawMaxCIRequestKiB int `protobuf:"varint,47,opt,name=max_concurrent_incoming_request_kib,json=maxConcurrentIncomingRequestKib,proto3,casttype=int" json:"maxConcurrentIncomingRequestKiB" xml:"maxConcurrentIncomingRequestKiB"`
|
||||
AnnounceLANAddresses bool `protobuf:"varint,48,opt,name=announce_lan_addresses,json=announceLanAddresses,proto3" json:"announceLANAddresses" xml:"announceLANAddresses" default:"true"`
|
||||
SendFullIndexOnUpgrade bool `protobuf:"varint,49,opt,name=send_full_index_on_upgrade,json=sendFullIndexOnUpgrade,proto3" json:"sendFullIndexOnUpgrade" xml:"sendFullIndexOnUpgrade"`
|
||||
FeatureFlags []string `protobuf:"bytes,50,rep,name=feature_flags,json=featureFlags,proto3" json:"featureFlags" xml:"featureFlag"`
|
||||
// The number of connections at which we stop trying to connect to more
|
||||
// devices, zero meaning no limit. Does not affect incoming connections.
|
||||
ConnectionLimitEnough int `protobuf:"varint,51,opt,name=connection_limit_enough,json=connectionLimitEnough,proto3,casttype=int" json:"connectionLimitEnough" xml:"connectionLimitEnough"`
|
||||
@@ -133,208 +133,209 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_d09882599506ca03 = []byte{
|
||||
// 3211 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x5a, 0x5b, 0x6c, 0xdc, 0xc6,
|
||||
0xd5, 0x36, 0xed, 0xd8, 0x89, 0x69, 0x59, 0xb6, 0x28, 0x59, 0x62, 0x6c, 0x47, 0x54, 0xd6, 0xeb,
|
||||
0x44, 0xb9, 0xd9, 0x92, 0xec, 0xf8, 0x77, 0x0c, 0xfc, 0xc8, 0xaf, 0x4b, 0xf4, 0x5b, 0xb1, 0x64,
|
||||
0x0b, 0x23, 0x09, 0xf9, 0x91, 0x1f, 0x05, 0x31, 0xcb, 0x9d, 0x95, 0x58, 0x71, 0x87, 0x6b, 0x72,
|
||||
0xa8, 0x95, 0xd2, 0x22, 0x0d, 0x52, 0xf4, 0xf2, 0xd6, 0x56, 0xe8, 0x05, 0x68, 0x81, 0x22, 0x45,
|
||||
0x5b, 0xa0, 0x69, 0x9a, 0xa2, 0x40, 0x81, 0x02, 0xed, 0x4b, 0x8b, 0x02, 0x05, 0x82, 0x16, 0xa8,
|
||||
0xf4, 0x58, 0xa0, 0x29, 0x8b, 0xc8, 0x79, 0xda, 0x87, 0x3e, 0xec, 0xa3, 0xfa, 0x52, 0x9c, 0xe1,
|
||||
0x6d, 0x48, 0xce, 0xc6, 0x7e, 0x5b, 0x9e, 0xef, 0xcc, 0x99, 0xef, 0xcc, 0xe5, 0xcc, 0x39, 0x33,
|
||||
0xab, 0x5e, 0x76, 0xec, 0xda, 0x55, 0xcb, 0xa5, 0x0d, 0x7b, 0xfd, 0xaa, 0xdb, 0x62, 0xb6, 0x4b,
|
||||
0xfd, 0xe8, 0x2b, 0xf0, 0x30, 0x7c, 0x5d, 0x69, 0x79, 0x2e, 0x73, 0xb5, 0x13, 0x91, 0xf0, 0xfc,
|
||||
0x88, 0xa0, 0xce, 0x02, 0x6a, 0xd3, 0xf5, 0x48, 0xe1, 0xfc, 0x39, 0x01, 0xf0, 0xed, 0xb7, 0x48,
|
||||
0x2c, 0x3e, 0x49, 0xb6, 0x59, 0xf4, 0xb3, 0xf2, 0xd7, 0xdb, 0xea, 0xd0, 0xbd, 0xa8, 0x87, 0x59,
|
||||
0xb1, 0x07, 0xed, 0x87, 0x8a, 0x7a, 0xd6, 0xb1, 0x7d, 0x46, 0xa8, 0x89, 0xeb, 0x75, 0x8f, 0xf8,
|
||||
0x3e, 0xf1, 0x75, 0x65, 0xec, 0xd8, 0xf8, 0xc9, 0x19, 0xff, 0x20, 0x34, 0x34, 0x84, 0xdb, 0x8b,
|
||||
0x1c, 0x9e, 0x4e, 0xd0, 0x4e, 0x68, 0x9c, 0x71, 0xf2, 0xa2, 0x6e, 0x68, 0x5c, 0xde, 0x6e, 0x3a,
|
||||
0xb7, 0x2a, 0x39, 0x79, 0x65, 0xac, 0x4e, 0x1a, 0x38, 0x70, 0xd8, 0xad, 0x4a, 0xfc, 0xa3, 0x72,
|
||||
0xb8, 0x57, 0x7d, 0x3c, 0xfe, 0xbd, 0xbb, 0x5f, 0x95, 0x18, 0x47, 0x45, 0xd3, 0xda, 0xbf, 0x14,
|
||||
0x55, 0x5f, 0x77, 0xdc, 0x1a, 0x76, 0xcc, 0xba, 0xed, 0x5b, 0xee, 0x16, 0xf1, 0x76, 0x4c, 0x9f,
|
||||
0x78, 0x5b, 0xc4, 0xf3, 0xf5, 0xa3, 0x9c, 0xe8, 0xaf, 0x95, 0x83, 0xd0, 0x18, 0x44, 0xb8, 0xfd,
|
||||
0xbf, 0x5c, 0x6f, 0x9a, 0xd2, 0x95, 0x08, 0xef, 0x84, 0xc6, 0xb9, 0xf5, 0x44, 0xe6, 0x06, 0xd4,
|
||||
0x22, 0x31, 0xd0, 0x0d, 0x8d, 0x17, 0x39, 0x61, 0x19, 0x2a, 0xe1, 0xdd, 0xd9, 0xab, 0x0e, 0xc9,
|
||||
0x54, 0xbb, 0x7b, 0x55, 0x79, 0x07, 0x79, 0x47, 0x65, 0xdc, 0xd0, 0x70, 0xd4, 0x70, 0x2e, 0x71,
|
||||
0x2a, 0x96, 0x6b, 0x9f, 0xca, 0x1c, 0x26, 0x14, 0xd7, 0x1c, 0x52, 0xd7, 0x8f, 0x8d, 0x29, 0xe3,
|
||||
0x4f, 0xcc, 0xbc, 0x0f, 0x0e, 0x9f, 0x4d, 0x2d, 0xbe, 0x16, 0x81, 0x65, 0x6f, 0x63, 0xa0, 0x1b,
|
||||
0x1a, 0xcf, 0x4b, 0xbc, 0x8d, 0x51, 0xc1, 0x5d, 0xe6, 0x05, 0x04, 0x7c, 0xed, 0x61, 0xa6, 0x17,
|
||||
0x70, 0xb8, 0x57, 0x7d, 0x0c, 0x9a, 0xee, 0xee, 0x57, 0x4b, 0xa4, 0x4a, 0x6e, 0xc6, 0x72, 0xed,
|
||||
0x63, 0x45, 0x1d, 0x71, 0x5c, 0x4b, 0xea, 0xe5, 0x63, 0xdc, 0xcb, 0x1f, 0x83, 0x97, 0x67, 0x16,
|
||||
0x41, 0x27, 0xe7, 0xe4, 0x90, 0x13, 0x8b, 0x0a, 0x3e, 0x3e, 0x17, 0x2d, 0x41, 0x09, 0x28, 0x71,
|
||||
0x51, 0x6e, 0xa4, 0x87, 0x5c, 0x70, 0xb0, 0xc8, 0x07, 0x9d, 0xe3, 0x0d, 0x4a, 0xee, 0xfd, 0x45,
|
||||
0x51, 0x07, 0x23, 0xf7, 0x70, 0x6c, 0xcb, 0x6c, 0xb9, 0x1e, 0xd3, 0x8f, 0x8f, 0x29, 0xe3, 0xc7,
|
||||
0x67, 0xbe, 0x0f, 0xae, 0xf5, 0x25, 0xa6, 0x96, 0x5d, 0x8f, 0x75, 0x42, 0x63, 0x20, 0xd7, 0x35,
|
||||
0x08, 0xbb, 0xa1, 0xf1, 0x6c, 0xd9, 0x29, 0x40, 0x04, 0x8f, 0xa6, 0x26, 0x27, 0xa6, 0xfe, 0xab,
|
||||
0x72, 0x18, 0x1a, 0xc7, 0x6c, 0xca, 0x3a, 0x7b, 0x55, 0x89, 0x19, 0x99, 0xf0, 0x70, 0xaf, 0x7a,
|
||||
0x9c, 0x37, 0xdd, 0xdd, 0xaf, 0xe6, 0x98, 0xa0, 0xb2, 0xae, 0xf6, 0xe5, 0xa3, 0xea, 0x58, 0xc1,
|
||||
0x9b, 0x66, 0xe0, 0x30, 0xdb, 0xc2, 0x3e, 0x4b, 0xe2, 0x86, 0x7e, 0x62, 0x4c, 0x19, 0x3f, 0x39,
|
||||
0xf3, 0x5b, 0x70, 0xad, 0x3f, 0x31, 0xb8, 0x34, 0x0b, 0x3b, 0xb9, 0x13, 0x1a, 0x83, 0x39, 0xa3,
|
||||
0x91, 0xb8, 0x1b, 0x1a, 0x37, 0xca, 0xee, 0x45, 0x98, 0xe0, 0xe0, 0xff, 0x37, 0x1a, 0x93, 0x53,
|
||||
0xb7, 0x6e, 0xdd, 0xbc, 0x76, 0xf3, 0xfa, 0xe7, 0x6e, 0x45, 0xde, 0x76, 0xf6, 0xaa, 0x52, 0x83,
|
||||
0x72, 0xf1, 0xe1, 0x5e, 0x55, 0x2b, 0x1b, 0xd9, 0xdd, 0xaf, 0x16, 0x68, 0xa2, 0xa7, 0xf2, 0x8d,
|
||||
0x13, 0x0f, 0xe3, 0x60, 0xa4, 0xdd, 0x53, 0x4f, 0x37, 0xf1, 0xb6, 0xe9, 0x13, 0x5a, 0x37, 0x37,
|
||||
0x6b, 0x2d, 0x5f, 0x7f, 0x9c, 0x4f, 0xe6, 0x0b, 0x9d, 0xd0, 0x38, 0xd5, 0xc4, 0xdb, 0x2b, 0x84,
|
||||
0xd6, 0xef, 0xd4, 0x5a, 0x10, 0x5c, 0x06, 0xb8, 0x5b, 0x82, 0x2c, 0x99, 0x1f, 0x24, 0x2a, 0x26,
|
||||
0x06, 0x3d, 0x62, 0x6d, 0x45, 0x06, 0x9f, 0xc8, 0x19, 0x44, 0xc4, 0xda, 0x2a, 0x1a, 0x4c, 0x64,
|
||||
0x39, 0x83, 0x89, 0x50, 0xfb, 0x8d, 0xa2, 0x8e, 0x78, 0xc4, 0x72, 0x29, 0x25, 0x16, 0x84, 0x77,
|
||||
0xd3, 0xa6, 0x8c, 0x78, 0x5b, 0xd8, 0x31, 0x7d, 0xfd, 0x24, 0xb7, 0xfd, 0x36, 0x0f, 0xea, 0x89,
|
||||
0xca, 0x42, 0x0c, 0xaf, 0x40, 0xec, 0x10, 0x1b, 0xa6, 0x40, 0x37, 0x34, 0xc6, 0x79, 0xdf, 0x52,
|
||||
0x54, 0x98, 0xa5, 0x1b, 0x13, 0x09, 0xa5, 0xc3, 0xbd, 0xea, 0xd1, 0x1b, 0x13, 0x3c, 0xbe, 0x97,
|
||||
0xfa, 0x41, 0xf2, 0x5e, 0xb4, 0x86, 0xda, 0xef, 0x11, 0x07, 0xef, 0xf8, 0x69, 0x0c, 0x50, 0x79,
|
||||
0x0c, 0x78, 0xb5, 0x13, 0x1a, 0xa7, 0x23, 0x24, 0xdb, 0xe8, 0x95, 0x98, 0x90, 0x20, 0x2d, 0xee,
|
||||
0xf0, 0x64, 0xc7, 0xa2, 0x7c, 0x63, 0xed, 0xdd, 0xa3, 0xea, 0x85, 0xb8, 0xa3, 0x94, 0x48, 0x36,
|
||||
0x48, 0x4d, 0xfd, 0x14, 0x1f, 0xa4, 0x3f, 0xc2, 0x1a, 0x1e, 0x41, 0xa0, 0x57, 0x72, 0x61, 0xa9,
|
||||
0x13, 0x1a, 0x23, 0x9e, 0x1c, 0x4a, 0x03, 0x6d, 0x0f, 0x5c, 0x60, 0x39, 0x39, 0x21, 0x6c, 0xd9,
|
||||
0x9e, 0xf6, 0x7a, 0x43, 0x30, 0xc8, 0x93, 0x30, 0xc8, 0xbd, 0x68, 0x22, 0x3d, 0xf2, 0xb3, 0x8c,
|
||||
0x68, 0x35, 0xf5, 0xb4, 0xcf, 0xb0, 0xc7, 0xcc, 0x9a, 0xe7, 0xb6, 0x7d, 0xe2, 0xe9, 0x7d, 0x7c,
|
||||
0xac, 0xff, 0xbb, 0x13, 0x1a, 0x7d, 0x1c, 0x98, 0x89, 0xe4, 0xdd, 0xd0, 0x78, 0x9a, 0xbb, 0x23,
|
||||
0x0a, 0x7b, 0x8e, 0x74, 0xae, 0xa9, 0xf6, 0x53, 0x45, 0x3d, 0x47, 0x31, 0x33, 0x99, 0x87, 0xe1,
|
||||
0x54, 0xc3, 0x4e, 0x3a, 0xb1, 0xfd, 0xbc, 0xb3, 0xfb, 0x07, 0xa1, 0xa1, 0xde, 0x9d, 0x5e, 0xcd,
|
||||
0xc2, 0xba, 0x4a, 0x31, 0xcb, 0xe6, 0xd8, 0xe0, 0x1d, 0x67, 0x22, 0x49, 0x08, 0x17, 0x1b, 0xe4,
|
||||
0xbe, 0x84, 0x70, 0x2d, 0x74, 0x81, 0x06, 0x29, 0x66, 0xab, 0x09, 0x9d, 0x64, 0x41, 0xfc, 0xae,
|
||||
0xc4, 0xd3, 0x21, 0xd8, 0x27, 0x66, 0x53, 0x3f, 0xc3, 0x97, 0xc2, 0x57, 0x61, 0x29, 0x9c, 0xbc,
|
||||
0x3b, 0xbd, 0xba, 0x08, 0x62, 0x98, 0xfc, 0x33, 0x14, 0xb3, 0xe8, 0xc3, 0xa6, 0x01, 0xe3, 0xc9,
|
||||
0x4f, 0x25, 0x21, 0x2b, 0xca, 0xa5, 0x7b, 0xa3, 0xb3, 0x57, 0x2d, 0xb5, 0x2f, 0x8b, 0xd2, 0x1d,
|
||||
0x94, 0x75, 0x8c, 0x34, 0x91, 0x7d, 0x24, 0xd3, 0xfe, 0xac, 0xa8, 0x23, 0x79, 0xf2, 0x1e, 0xa1,
|
||||
0xa4, 0xcd, 0x57, 0xf2, 0x59, 0x4e, 0x7f, 0x17, 0xe8, 0x9f, 0xba, 0x3b, 0xbd, 0x8a, 0x22, 0x00,
|
||||
0x1c, 0x18, 0xa0, 0x98, 0x25, 0x9f, 0xa9, 0x0b, 0xd5, 0xc4, 0x85, 0x3c, 0x22, 0x38, 0x71, 0x4d,
|
||||
0x74, 0x42, 0x62, 0x43, 0x26, 0x04, 0x47, 0xae, 0x81, 0x23, 0x22, 0x05, 0x34, 0x24, 0xba, 0x92,
|
||||
0x48, 0x25, 0xce, 0x30, 0xbb, 0x49, 0xdc, 0x80, 0x99, 0xbe, 0x3e, 0x90, 0x77, 0x66, 0x35, 0x02,
|
||||
0x56, 0x62, 0x67, 0x92, 0x4f, 0x58, 0xe9, 0xf5, 0x9c, 0x33, 0x79, 0xa4, 0xd7, 0xf6, 0x93, 0xd8,
|
||||
0x90, 0x09, 0xd3, 0x2d, 0x27, 0x52, 0xc8, 0x3b, 0x93, 0x48, 0xb5, 0x1f, 0x28, 0xaa, 0x1e, 0xf8,
|
||||
0x78, 0x9d, 0x98, 0x1e, 0x81, 0x73, 0xdf, 0xa6, 0xeb, 0x26, 0xb6, 0x2c, 0xd2, 0x62, 0xa4, 0xae,
|
||||
0x6b, 0xdc, 0x1b, 0x0c, 0x3b, 0x60, 0x0d, 0x4d, 0xc7, 0x52, 0xd8, 0x01, 0x81, 0x97, 0x7c, 0x75,
|
||||
0x43, 0xe3, 0x2c, 0x77, 0x22, 0x13, 0x09, 0x84, 0x45, 0xc5, 0xdc, 0x17, 0xac, 0xf8, 0xcc, 0x24,
|
||||
0x1a, 0xe6, 0x14, 0x50, 0xc2, 0x20, 0x91, 0x6b, 0x5f, 0x50, 0x87, 0x8a, 0xe4, 0x7c, 0x42, 0xa8,
|
||||
0x3e, 0xc8, 0x89, 0x2d, 0x1c, 0x84, 0xc6, 0x89, 0x35, 0xb4, 0x42, 0x08, 0xed, 0x84, 0xc6, 0x89,
|
||||
0xc0, 0x83, 0x5f, 0xdd, 0xd0, 0xe8, 0x8b, 0x09, 0xc1, 0xa7, 0x40, 0x26, 0x51, 0x48, 0x7f, 0xed,
|
||||
0xee, 0x57, 0xe3, 0xe6, 0x48, 0xcb, 0x13, 0x00, 0x99, 0xf6, 0x1d, 0x45, 0x7d, 0xb2, 0xd8, 0x7b,
|
||||
0x40, 0xed, 0xfb, 0x01, 0x31, 0xed, 0xba, 0x3e, 0xc4, 0x93, 0x88, 0x37, 0xa3, 0xb1, 0x59, 0xe3,
|
||||
0xe2, 0x85, 0xb9, 0x68, 0x6c, 0xe2, 0x2f, 0x71, 0x6c, 0x12, 0x85, 0x4a, 0x34, 0x28, 0xc9, 0x67,
|
||||
0x57, 0xfc, 0x8a, 0x07, 0x25, 0xc1, 0x8a, 0x83, 0x92, 0x68, 0x69, 0x7f, 0x50, 0xd4, 0xc1, 0x12,
|
||||
0x2f, 0xcf, 0xd1, 0xcf, 0x71, 0x46, 0xdf, 0x80, 0xb5, 0x77, 0x7c, 0x0d, 0xad, 0xa1, 0xc5, 0x4e,
|
||||
0x68, 0x1c, 0x0f, 0xbc, 0x35, 0xb4, 0xd8, 0x0d, 0x8d, 0x9b, 0x09, 0x11, 0xb4, 0x28, 0xac, 0xae,
|
||||
0x0d, 0xc6, 0x5a, 0xfe, 0xad, 0xab, 0x57, 0xeb, 0x98, 0xe1, 0x2b, 0xfe, 0x0e, 0xb5, 0xd8, 0x06,
|
||||
0x14, 0x6b, 0x94, 0xb0, 0xab, 0x94, 0xb4, 0x41, 0x0a, 0x84, 0x63, 0x23, 0xc9, 0x8f, 0xc3, 0xbd,
|
||||
0xea, 0x23, 0x34, 0xdc, 0xdd, 0xaf, 0x46, 0x2c, 0xd0, 0x40, 0xc1, 0x0f, 0xcf, 0xd1, 0xfe, 0xa9,
|
||||
0xa8, 0x46, 0xd1, 0x85, 0x96, 0xeb, 0xc3, 0x09, 0xe7, 0x13, 0x2b, 0xf0, 0x88, 0xb3, 0xa3, 0x0f,
|
||||
0xf3, 0xf0, 0xfb, 0x3d, 0x5e, 0x41, 0xac, 0xa1, 0x65, 0xd7, 0x67, 0x0b, 0x29, 0xd8, 0x09, 0x8d,
|
||||
0xb3, 0x81, 0x97, 0x97, 0x75, 0x43, 0xe3, 0x99, 0xd8, 0xc9, 0x3c, 0x20, 0xf8, 0xdb, 0xc0, 0x8e,
|
||||
0xcf, 0x43, 0x72, 0xb9, 0xb5, 0x44, 0x06, 0x99, 0x27, 0x6f, 0x01, 0xf5, 0x42, 0x91, 0x02, 0xba,
|
||||
0x98, 0x77, 0x2b, 0x8f, 0x6a, 0xff, 0x90, 0x78, 0x68, 0x53, 0x9b, 0xd9, 0x50, 0x47, 0xc0, 0x79,
|
||||
0x67, 0xfa, 0xfa, 0x08, 0x5f, 0xc5, 0xdf, 0xe5, 0xd5, 0xc3, 0x1a, 0x5a, 0x88, 0xd0, 0x39, 0x00,
|
||||
0x21, 0x60, 0x9c, 0x09, 0xbc, 0x9c, 0x28, 0x0d, 0x17, 0x05, 0xb9, 0x18, 0x2c, 0x6e, 0x4e, 0xe4,
|
||||
0x02, 0x78, 0xd1, 0x42, 0x59, 0x04, 0x27, 0x10, 0xb4, 0x82, 0x82, 0xa1, 0x40, 0x01, 0x5d, 0xc8,
|
||||
0x3b, 0x98, 0x03, 0x35, 0x57, 0x1d, 0xf0, 0x48, 0x74, 0x38, 0xbb, 0xd4, 0x6c, 0xe3, 0x4d, 0x12,
|
||||
0xb4, 0x74, 0x9d, 0x4f, 0xd9, 0x2c, 0x90, 0x8f, 0xc1, 0x7b, 0xf4, 0x0d, 0x0e, 0xa5, 0xe4, 0x0b,
|
||||
0xf2, 0x9e, 0x87, 0x74, 0xd1, 0x80, 0xf6, 0x35, 0x45, 0x1d, 0xc1, 0x01, 0x73, 0xcd, 0xa0, 0xb5,
|
||||
0xee, 0xe1, 0x3a, 0xc9, 0x92, 0xa1, 0x0d, 0xfd, 0x49, 0x3e, 0x90, 0xcb, 0x50, 0x72, 0x81, 0xca,
|
||||
0x5a, 0xa4, 0x91, 0xe4, 0x11, 0xb7, 0xd3, 0xea, 0x44, 0x06, 0x8a, 0xc3, 0x37, 0x25, 0x66, 0x86,
|
||||
0x93, 0x53, 0x48, 0x6a, 0x4d, 0x6b, 0xaa, 0x23, 0x09, 0x07, 0xe6, 0x9a, 0x2d, 0x0f, 0xa6, 0x98,
|
||||
0x9f, 0xc5, 0xbe, 0x7e, 0x9e, 0x0f, 0xc0, 0x0d, 0x20, 0x12, 0xab, 0xac, 0xba, 0xcb, 0x1e, 0x41,
|
||||
0x31, 0xde, 0x0d, 0x8d, 0xf3, 0xd1, 0x14, 0x4a, 0xc0, 0x0a, 0x92, 0xb6, 0xd1, 0xb6, 0x54, 0x6d,
|
||||
0x93, 0x90, 0x96, 0xc9, 0x48, 0xb3, 0xe5, 0x7a, 0xd8, 0xb3, 0x89, 0x6f, 0x6e, 0xe8, 0x17, 0xb8,
|
||||
0xcb, 0xb7, 0x61, 0x23, 0x00, 0xba, 0x9a, 0x81, 0xe0, 0xee, 0x25, 0xde, 0x4b, 0x11, 0x10, 0x6b,
|
||||
0xb1, 0xeb, 0xa2, 0xab, 0x53, 0xd7, 0x51, 0xc9, 0x8a, 0xb6, 0xa3, 0x0e, 0x5a, 0xd8, 0xda, 0x20,
|
||||
0xa6, 0xbd, 0x4e, 0x5d, 0x8f, 0xd4, 0xcd, 0x86, 0xed, 0x10, 0x5f, 0xbf, 0xc8, 0x5d, 0x5c, 0x80,
|
||||
0x13, 0x8d, 0xc3, 0x0b, 0x11, 0x3a, 0x0f, 0x60, 0x3a, 0xd0, 0x25, 0xa4, 0xb4, 0x07, 0xd3, 0xbd,
|
||||
0x85, 0xca, 0x66, 0xb4, 0x6f, 0x29, 0xea, 0xf9, 0x96, 0xe7, 0xae, 0x43, 0x31, 0x63, 0x06, 0xad,
|
||||
0x3a, 0x66, 0x44, 0x2c, 0x10, 0x9e, 0xe2, 0xbe, 0xaf, 0x42, 0x7e, 0x9b, 0x68, 0xad, 0x71, 0x25,
|
||||
0xb1, 0x18, 0x88, 0x8a, 0xec, 0x1e, 0xb8, 0x40, 0xe7, 0x65, 0x61, 0x20, 0x94, 0x97, 0x51, 0x2f,
|
||||
0x8b, 0xda, 0xbb, 0x8a, 0x3a, 0xec, 0xd8, 0x4d, 0x9b, 0x99, 0x35, 0x4c, 0xeb, 0x6d, 0xbb, 0xce,
|
||||
0x36, 0x4c, 0x9b, 0x9a, 0x0e, 0xa6, 0xfa, 0x28, 0x1f, 0x92, 0x25, 0x5e, 0x3c, 0x82, 0xc6, 0x4c,
|
||||
0xa2, 0xb0, 0x40, 0x17, 0x31, 0xcd, 0x0a, 0xfe, 0x32, 0xf6, 0x19, 0xc3, 0x22, 0x33, 0xa5, 0xbd,
|
||||
0xa3, 0xa8, 0x5a, 0xd3, 0xa6, 0xe6, 0x86, 0xdb, 0x24, 0x66, 0xdd, 0xf6, 0x37, 0xcd, 0x86, 0x47,
|
||||
0x88, 0x6e, 0x8c, 0x29, 0xe3, 0xa7, 0xa6, 0xfa, 0xae, 0x44, 0x37, 0x6b, 0x57, 0x56, 0xec, 0xb7,
|
||||
0xc8, 0xcc, 0x6b, 0x1f, 0x85, 0xc6, 0x11, 0xd8, 0x89, 0x4d, 0x9b, 0xde, 0x76, 0x9b, 0x64, 0xce,
|
||||
0xf6, 0x37, 0xe7, 0x3d, 0x42, 0xd2, 0xd5, 0x51, 0x90, 0x8b, 0xfb, 0x60, 0xec, 0x32, 0x10, 0x39,
|
||||
0x36, 0x39, 0x76, 0x19, 0x15, 0x9b, 0x6b, 0x0f, 0x14, 0xb5, 0x2f, 0x59, 0xef, 0xfc, 0xd8, 0x19,
|
||||
0xe3, 0xc7, 0xce, 0xef, 0x79, 0xca, 0x93, 0x2c, 0xda, 0xe8, 0xf0, 0x39, 0xe5, 0x65, 0x9f, 0xdd,
|
||||
0xd0, 0x98, 0x4b, 0x2a, 0x8e, 0x44, 0x26, 0x39, 0x88, 0xe2, 0x1d, 0xe0, 0x17, 0xce, 0x94, 0x26,
|
||||
0x61, 0xf8, 0xca, 0xe7, 0x7d, 0x97, 0x42, 0xec, 0xce, 0x99, 0xcd, 0x7f, 0x1e, 0xee, 0x55, 0xc7,
|
||||
0x1f, 0xd5, 0x14, 0xe4, 0x47, 0x02, 0x5f, 0x94, 0xd9, 0xf1, 0x1c, 0xed, 0x0d, 0x75, 0x00, 0x3b,
|
||||
0x6d, 0xa8, 0xbe, 0xa2, 0xdb, 0x04, 0x4a, 0x98, 0xaf, 0x3f, 0xcd, 0x2f, 0xf1, 0xa0, 0xe8, 0x3d,
|
||||
0x13, 0x81, 0xbc, 0x2a, 0xbf, 0x4b, 0x18, 0x2c, 0xfc, 0xa1, 0x28, 0xc2, 0xe4, 0xe4, 0x15, 0x54,
|
||||
0x54, 0xd4, 0xfe, 0xad, 0xa8, 0xe3, 0xee, 0x16, 0xf1, 0xda, 0x9e, 0xcd, 0x20, 0x70, 0x34, 0x5d,
|
||||
0x46, 0xcc, 0x3a, 0xd9, 0xb2, 0x2d, 0x62, 0x52, 0xdc, 0x24, 0x3e, 0x84, 0xd3, 0xb8, 0x10, 0xd2,
|
||||
0x2b, 0xd9, 0xf5, 0xd2, 0xc8, 0xbd, 0xa4, 0x11, 0xe2, 0x6d, 0xe6, 0xc8, 0xd6, 0x5d, 0x50, 0xef,
|
||||
0x84, 0xc6, 0x25, 0xb7, 0x04, 0xd9, 0x16, 0xe1, 0xe8, 0x3d, 0x3a, 0x1b, 0x99, 0xea, 0x86, 0xc6,
|
||||
0x2b, 0x9c, 0xe0, 0x23, 0xe8, 0xf6, 0x5e, 0x94, 0x50, 0xc5, 0xf5, 0xe0, 0x81, 0x1e, 0x85, 0x85,
|
||||
0xf6, 0x25, 0xf5, 0x1c, 0x84, 0x31, 0xd3, 0xa6, 0x75, 0xb2, 0x6d, 0xc2, 0x4a, 0xae, 0x39, 0xae,
|
||||
0xb5, 0xe9, 0xeb, 0x97, 0xf8, 0x96, 0x86, 0x45, 0xa3, 0x81, 0xc2, 0x02, 0xe0, 0x4b, 0x36, 0x9d,
|
||||
0xe1, 0x68, 0x7a, 0x6b, 0x5b, 0x86, 0xa4, 0x99, 0x72, 0x94, 0xff, 0x22, 0x89, 0x25, 0xed, 0xef,
|
||||
0x90, 0xee, 0x52, 0x6c, 0x6d, 0x92, 0xba, 0x49, 0x5d, 0x66, 0x37, 0x6c, 0x0b, 0x47, 0xf7, 0x0f,
|
||||
0x75, 0x5f, 0xaf, 0xf2, 0xf9, 0x7d, 0x0f, 0x86, 0x7b, 0x78, 0x2d, 0x52, 0xba, 0x2b, 0xe8, 0x2c,
|
||||
0xcc, 0xc1, 0x68, 0x0f, 0x07, 0x52, 0xa4, 0x1b, 0x1a, 0x17, 0xa2, 0xd0, 0x2e, 0x83, 0xf9, 0x5d,
|
||||
0xa5, 0x14, 0xe9, 0xee, 0x55, 0x7b, 0x58, 0xdc, 0xdd, 0xaf, 0xf6, 0x60, 0x81, 0xa4, 0x2d, 0xea,
|
||||
0xbe, 0x86, 0xd4, 0xd3, 0xcc, 0xc3, 0x8d, 0x86, 0x6d, 0x99, 0x96, 0x83, 0x7d, 0x5f, 0xbf, 0xcc,
|
||||
0x87, 0xf5, 0x25, 0xa8, 0x97, 0x63, 0x60, 0x16, 0xe4, 0xdd, 0xd0, 0xd0, 0xa2, 0x01, 0x15, 0x84,
|
||||
0xe9, 0x45, 0x4d, 0x4e, 0x55, 0xbb, 0xaf, 0x0e, 0xc6, 0x43, 0x6c, 0x36, 0x5c, 0xa7, 0x4e, 0x3c,
|
||||
0xb3, 0x85, 0xd9, 0x86, 0xfe, 0x0c, 0xdf, 0xf5, 0xd3, 0x70, 0x0c, 0xc4, 0xf0, 0x3c, 0x47, 0x97,
|
||||
0x31, 0xdb, 0x48, 0x43, 0x4c, 0x09, 0x11, 0xa6, 0xeb, 0x6d, 0x58, 0x56, 0xca, 0xdb, 0xa8, 0xdc,
|
||||
0x5c, 0xdb, 0x54, 0xcf, 0xfa, 0x84, 0x99, 0x8e, 0xdb, 0x36, 0x5b, 0x9e, 0xed, 0x7a, 0x36, 0xdb,
|
||||
0xd1, 0x9f, 0xe5, 0x5b, 0x01, 0xfa, 0xeb, 0xf7, 0x09, 0x5b, 0x74, 0xdb, 0xcb, 0x31, 0x92, 0x76,
|
||||
0x96, 0x17, 0xf7, 0x4c, 0x2c, 0x0a, 0xcd, 0xb5, 0xf7, 0x15, 0x75, 0xb8, 0x89, 0xb7, 0x13, 0xe7,
|
||||
0x2c, 0x97, 0x5a, 0x81, 0xe7, 0x11, 0x6a, 0xed, 0xe8, 0xe3, 0x7c, 0xf4, 0x7c, 0x7e, 0xc5, 0x82,
|
||||
0xdb, 0x4b, 0x78, 0x3b, 0xe2, 0x38, 0x9b, 0xa9, 0xc0, 0x41, 0xdf, 0x94, 0xc8, 0xd3, 0x83, 0x5e,
|
||||
0x06, 0x26, 0x03, 0xcd, 0xef, 0x44, 0xe4, 0x76, 0x91, 0xd4, 0xaa, 0xf6, 0xb1, 0xa2, 0x0e, 0x5a,
|
||||
0x1e, 0xf6, 0x37, 0x0a, 0x99, 0xff, 0x73, 0x7c, 0x32, 0x3e, 0xe0, 0x99, 0xff, 0x6c, 0x92, 0xf9,
|
||||
0x5b, 0x71, 0xe6, 0x3f, 0x1f, 0x9d, 0xc8, 0xd0, 0x2c, 0xcb, 0xc1, 0xa5, 0xc1, 0x97, 0xeb, 0x94,
|
||||
0xb3, 0x79, 0x2e, 0x86, 0x15, 0x3c, 0x50, 0x32, 0x02, 0x35, 0x81, 0x15, 0xd7, 0x04, 0xd5, 0x47,
|
||||
0x31, 0x03, 0x55, 0xc1, 0x6c, 0x54, 0x15, 0x14, 0x8c, 0x79, 0x8e, 0xf6, 0x23, 0x45, 0x1d, 0x29,
|
||||
0xba, 0x97, 0x5c, 0xc6, 0x3c, 0xcf, 0xe7, 0xdf, 0x3e, 0x08, 0x8d, 0x93, 0xb3, 0x48, 0x78, 0x47,
|
||||
0xc8, 0x5b, 0x29, 0xbe, 0x23, 0x48, 0xd1, 0x5e, 0x4b, 0x63, 0x77, 0xbf, 0x9a, 0xd9, 0x46, 0x72,
|
||||
0xcb, 0xda, 0x57, 0x14, 0x75, 0xd8, 0x67, 0x01, 0x35, 0x21, 0x5f, 0xc2, 0x8e, 0xbd, 0x45, 0xcc,
|
||||
0x28, 0x0b, 0xf6, 0xf5, 0x17, 0xd2, 0x2c, 0x74, 0x10, 0x34, 0xee, 0x24, 0x0a, 0x2b, 0x80, 0xaf,
|
||||
0xa4, 0xb9, 0x91, 0x04, 0xcb, 0xa7, 0xf0, 0x42, 0x18, 0x3b, 0x36, 0x79, 0x73, 0x02, 0xc9, 0xac,
|
||||
0x41, 0x65, 0x5c, 0xa0, 0x01, 0xd1, 0xd4, 0xd7, 0x5f, 0xe4, 0x24, 0x5e, 0x87, 0x7d, 0x99, 0x6b,
|
||||
0xb6, 0x64, 0xd3, 0xac, 0x82, 0x28, 0x21, 0x62, 0x66, 0x98, 0x0b, 0xa3, 0x53, 0x13, 0xa8, 0x6c,
|
||||
0x07, 0x72, 0xf1, 0x3e, 0xde, 0x7b, 0xf2, 0xbc, 0xf5, 0x12, 0x8f, 0x9c, 0xf5, 0x83, 0xd0, 0xe8,
|
||||
0x47, 0xb8, 0xbd, 0xc2, 0x02, 0xe1, 0x61, 0xeb, 0x94, 0x9f, 0x7d, 0xa6, 0x57, 0x50, 0x99, 0xec,
|
||||
0xa1, 0x8f, 0x6f, 0x05, 0x8b, 0x48, 0xb4, 0xa7, 0x6d, 0xa9, 0x67, 0xa0, 0xd8, 0xac, 0x61, 0x9f,
|
||||
0x98, 0xd1, 0x4b, 0xa3, 0x7e, 0x65, 0x4c, 0x19, 0xef, 0x9f, 0xea, 0x4f, 0x92, 0xa1, 0x55, 0x2e,
|
||||
0xe5, 0x77, 0x86, 0xfd, 0x89, 0x6a, 0x24, 0xcb, 0xc2, 0x54, 0x4e, 0x5c, 0x19, 0x8b, 0x4b, 0x8f,
|
||||
0x78, 0x79, 0xbc, 0xb3, 0x5f, 0x55, 0x50, 0xa1, 0xa9, 0xf6, 0xed, 0xa3, 0xea, 0x25, 0x88, 0x1a,
|
||||
0x69, 0xb8, 0x80, 0xd2, 0xd5, 0x72, 0x9b, 0xb0, 0x64, 0x3d, 0x72, 0x3f, 0x20, 0x3e, 0x33, 0x37,
|
||||
0xed, 0x9a, 0x7e, 0x95, 0x4f, 0xc7, 0x9f, 0x94, 0xf8, 0x85, 0x72, 0x09, 0x6f, 0xcf, 0x2e, 0xa0,
|
||||
0x08, 0xbf, 0x63, 0xcf, 0x74, 0x42, 0xc3, 0x68, 0xe2, 0xed, 0x74, 0x8b, 0xb3, 0x85, 0xd8, 0x46,
|
||||
0xa6, 0x92, 0x9e, 0x7d, 0x0f, 0xd1, 0x13, 0xca, 0xbe, 0x87, 0x9a, 0x7c, 0xb8, 0x4a, 0xfc, 0xe6,
|
||||
0x59, 0xa0, 0x8b, 0x1e, 0xd2, 0xac, 0xa6, 0x7d, 0xaa, 0xa8, 0xc3, 0xe9, 0xc3, 0x8b, 0x83, 0xc5,
|
||||
0xa7, 0xda, 0x09, 0xbe, 0x81, 0x3f, 0x84, 0x91, 0x18, 0x4a, 0x1e, 0x2e, 0x16, 0xa7, 0xef, 0x8a,
|
||||
0xaf, 0xb5, 0x43, 0x58, 0x22, 0x4f, 0xd3, 0x67, 0x19, 0x28, 0x7b, 0x2f, 0x93, 0x1a, 0xe9, 0x21,
|
||||
0x17, 0xb6, 0xbe, 0x94, 0x14, 0xca, 0x5a, 0x61, 0xe1, 0xa9, 0x77, 0x4b, 0x3d, 0xcf, 0xdf, 0x56,
|
||||
0x1a, 0x81, 0xe3, 0xc4, 0xb9, 0x8c, 0x4b, 0x93, 0xc2, 0x54, 0x9f, 0xe4, 0x9e, 0xde, 0x82, 0x5c,
|
||||
0x01, 0xb4, 0xe6, 0x03, 0xc7, 0xe1, 0x59, 0xc8, 0x3d, 0x1a, 0x97, 0x92, 0xdd, 0xd0, 0xb8, 0x18,
|
||||
0x1f, 0x59, 0x32, 0xb8, 0x82, 0x7a, 0xb4, 0xd3, 0x5e, 0x57, 0x4f, 0x37, 0x08, 0x66, 0x81, 0x47,
|
||||
0xcc, 0x86, 0x83, 0xd7, 0x7d, 0x7d, 0x8a, 0xef, 0xbb, 0xcb, 0x70, 0xbe, 0xc7, 0xc0, 0x3c, 0xc8,
|
||||
0xd3, 0x77, 0x18, 0x41, 0x58, 0x41, 0x39, 0x15, 0xad, 0xad, 0x8e, 0x08, 0xcf, 0x2f, 0x51, 0x65,
|
||||
0x43, 0xa8, 0x1b, 0xac, 0x6f, 0xe8, 0xd7, 0xf8, 0xa2, 0x7d, 0x95, 0x87, 0xd7, 0x54, 0x65, 0x11,
|
||||
0x34, 0x5e, 0xe3, 0x0a, 0x69, 0xae, 0x23, 0x45, 0xd3, 0x3c, 0x42, 0xde, 0x58, 0xdb, 0x54, 0x87,
|
||||
0x4a, 0x1d, 0x37, 0xf1, 0xb6, 0x7e, 0x9d, 0xf7, 0xfa, 0x0a, 0xa4, 0x80, 0x85, 0x86, 0x4b, 0x78,
|
||||
0xbb, 0x1b, 0x1a, 0xba, 0xac, 0xcb, 0x25, 0xbc, 0x9d, 0xf6, 0x27, 0x69, 0xa6, 0x7d, 0x51, 0xed,
|
||||
0x0b, 0x5a, 0xb4, 0x95, 0x1e, 0x23, 0x3f, 0x9b, 0xe7, 0x93, 0xf3, 0x7f, 0x07, 0xa1, 0x71, 0x6e,
|
||||
0x8e, 0xb4, 0x3c, 0x62, 0x61, 0x46, 0xea, 0x6b, 0xcb, 0x74, 0x39, 0x3b, 0x53, 0x94, 0x97, 0xb2,
|
||||
0x64, 0xae, 0x45, 0x5b, 0x31, 0xf0, 0xa2, 0xdb, 0xb4, 0x21, 0xa1, 0x64, 0x3b, 0x95, 0xdd, 0xfd,
|
||||
0xaa, 0xbc, 0xb1, 0xae, 0xa0, 0x53, 0x42, 0x13, 0xed, 0x27, 0x4a, 0xdc, 0x7d, 0x72, 0x55, 0xff,
|
||||
0xfe, 0x3c, 0x77, 0xf2, 0x1d, 0xbe, 0x0b, 0xf2, 0x26, 0xd2, 0x6b, 0x7b, 0xde, 0xfd, 0x58, 0xda,
|
||||
0xbd, 0x78, 0xdd, 0x2e, 0x70, 0xc8, 0xb6, 0xfb, 0xf9, 0xde, 0x5a, 0xb0, 0xac, 0x65, 0xbd, 0xe8,
|
||||
0x0a, 0x52, 0xb3, 0x56, 0xda, 0xaf, 0x14, 0xb5, 0x9f, 0xd3, 0xcc, 0x2e, 0xe5, 0x7f, 0x1e, 0x11,
|
||||
0xfd, 0x3a, 0xcf, 0x85, 0xf3, 0x26, 0x84, 0x0b, 0x7a, 0x4e, 0xb5, 0x92, 0x52, 0xcd, 0x5f, 0xa9,
|
||||
0x4b, 0xc9, 0x5e, 0xfc, 0x2c, 0x3d, 0xc8, 0x78, 0xe5, 0x7d, 0xe9, 0x0a, 0xea, 0x13, 0x5b, 0x66,
|
||||
0x94, 0xb3, 0xab, 0xf7, 0x0f, 0x7a, 0x53, 0x16, 0xae, 0xe1, 0x0b, 0x94, 0xf3, 0x17, 0xe7, 0xbd,
|
||||
0x29, 0xf7, 0xd2, 0x2b, 0x53, 0x4e, 0x34, 0x13, 0xca, 0xe9, 0x4d, 0x7b, 0x43, 0x8d, 0x9e, 0xf8,
|
||||
0xd2, 0x43, 0xf3, 0x17, 0xf3, 0x7c, 0xf7, 0xfe, 0x4f, 0x9e, 0x2f, 0x7f, 0x25, 0xcb, 0x4e, 0x4f,
|
||||
0x61, 0x31, 0x7a, 0x19, 0x22, 0x10, 0x85, 0x7e, 0x04, 0xc4, 0xe7, 0x17, 0x15, 0xe5, 0x3b, 0x02,
|
||||
0xb3, 0x65, 0x31, 0xfd, 0x43, 0x18, 0x22, 0x65, 0x66, 0xe9, 0x20, 0x34, 0x2e, 0x66, 0x3d, 0x2e,
|
||||
0xe5, 0x2b, 0xfc, 0x65, 0x8b, 0xe5, 0xc7, 0xa9, 0x59, 0xc2, 0xf3, 0xdd, 0x6b, 0x65, 0x05, 0xc8,
|
||||
0x10, 0x86, 0x0a, 0xe7, 0xa3, 0x6f, 0x61, 0xea, 0xeb, 0xbf, 0x8c, 0x66, 0x69, 0xb5, 0x40, 0x41,
|
||||
0x3c, 0x57, 0x56, 0x40, 0xb1, 0x40, 0xa1, 0x84, 0x97, 0xa7, 0x8a, 0x33, 0x29, 0xe9, 0xcd, 0xdc,
|
||||
0xf9, 0xe8, 0x93, 0xd1, 0x23, 0xfb, 0x9f, 0x8c, 0x1e, 0xf9, 0xe8, 0x60, 0x54, 0xd9, 0x3f, 0x18,
|
||||
0x55, 0xbe, 0xf9, 0x60, 0xf4, 0xc8, 0x7b, 0x0f, 0x46, 0x95, 0xfd, 0x07, 0xa3, 0x47, 0xfe, 0xf6,
|
||||
0x60, 0xf4, 0xc8, 0x9b, 0xcf, 0xad, 0xdb, 0x6c, 0x23, 0xa8, 0x5d, 0xb1, 0xdc, 0xe6, 0xd5, 0x34,
|
||||
0x6b, 0x15, 0x7e, 0x65, 0xff, 0x59, 0xaa, 0x9d, 0xe0, 0x7f, 0x52, 0xba, 0xf6, 0x9f, 0x00, 0x00,
|
||||
0x00, 0xff, 0xff, 0x43, 0x37, 0xb7, 0x54, 0x10, 0x25, 0x00, 0x00,
|
||||
// 3218 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x5a, 0x5d, 0x6c, 0x1d, 0x47,
|
||||
0xf5, 0xcf, 0x26, 0x4d, 0xda, 0x6c, 0x1c, 0x27, 0x1e, 0x3b, 0xf6, 0x36, 0x49, 0xbd, 0xee, 0xcd,
|
||||
0x4d, 0xeb, 0x7e, 0x25, 0xb6, 0x93, 0xe6, 0x9f, 0x46, 0xfa, 0xab, 0xf8, 0xa3, 0x26, 0x6e, 0xec,
|
||||
0xc4, 0x1a, 0xdb, 0x2a, 0x2a, 0x42, 0xab, 0xb9, 0x7b, 0xe7, 0xda, 0x8b, 0xf7, 0xce, 0xde, 0xee,
|
||||
0xcc, 0xfa, 0xda, 0x2d, 0x82, 0xaa, 0x88, 0x8f, 0x37, 0xc0, 0xe2, 0x43, 0x02, 0x09, 0x15, 0x01,
|
||||
0x12, 0xa5, 0x14, 0x21, 0x21, 0x21, 0xc1, 0x0b, 0x08, 0x09, 0xa9, 0x82, 0x07, 0xfb, 0xb1, 0x12,
|
||||
0x65, 0x51, 0x9d, 0x3e, 0xdd, 0x07, 0x1e, 0xee, 0xa3, 0x79, 0x41, 0x33, 0xfb, 0x35, 0xbb, 0x3b,
|
||||
0xb7, 0xc9, 0xdb, 0xdd, 0xf3, 0x3b, 0x73, 0xe6, 0x77, 0xe6, 0xe3, 0xcc, 0x39, 0x33, 0x57, 0xbf,
|
||||
0xec, 0x3a, 0xb5, 0xab, 0xb6, 0x47, 0x1a, 0xce, 0xfa, 0x55, 0xaf, 0xc5, 0x1c, 0x8f, 0xd0, 0xe8,
|
||||
0x2b, 0xf0, 0x11, 0xff, 0xba, 0xd2, 0xf2, 0x3d, 0xe6, 0x81, 0x13, 0x91, 0xf0, 0xfc, 0x88, 0xa4,
|
||||
0xce, 0x02, 0xe2, 0x90, 0xf5, 0x48, 0xe1, 0xfc, 0x39, 0x09, 0xa0, 0xce, 0x9b, 0x38, 0x16, 0x9f,
|
||||
0xc4, 0xdb, 0x2c, 0xfa, 0x59, 0xf9, 0xe8, 0xb6, 0x3e, 0x74, 0x2f, 0xea, 0x61, 0x56, 0xee, 0x01,
|
||||
0xfc, 0x54, 0xd3, 0xcf, 0xba, 0x0e, 0x65, 0x98, 0x58, 0xa8, 0x5e, 0xf7, 0x31, 0xa5, 0x98, 0x1a,
|
||||
0xda, 0xd8, 0xb1, 0xf1, 0x93, 0x33, 0xf4, 0x20, 0x34, 0x01, 0x44, 0xed, 0x45, 0x01, 0x4f, 0x27,
|
||||
0x68, 0x27, 0x34, 0xcf, 0xb8, 0x79, 0x51, 0x37, 0x34, 0x2f, 0x6f, 0x37, 0xdd, 0x5b, 0x95, 0x9c,
|
||||
0xbc, 0x32, 0x56, 0xc7, 0x0d, 0x14, 0xb8, 0xec, 0x56, 0x25, 0xfe, 0x51, 0x39, 0xdc, 0xab, 0x3e,
|
||||
0x1a, 0xff, 0xde, 0xdd, 0xaf, 0x2a, 0x8c, 0xc3, 0xa2, 0x69, 0xf0, 0x1f, 0x4d, 0x37, 0xd6, 0x5d,
|
||||
0xaf, 0x86, 0x5c, 0xab, 0xee, 0x50, 0xdb, 0xdb, 0xc2, 0xfe, 0x8e, 0x45, 0xb1, 0xbf, 0x85, 0x7d,
|
||||
0x6a, 0x1c, 0x15, 0x44, 0x7f, 0xaf, 0x1d, 0x84, 0xe6, 0x20, 0x44, 0xed, 0xcf, 0x0b, 0xbd, 0x69,
|
||||
0x42, 0x56, 0x22, 0xbc, 0x13, 0x9a, 0xe7, 0xd6, 0x13, 0x99, 0x17, 0x10, 0x1b, 0xc7, 0x40, 0x37,
|
||||
0x34, 0x9f, 0x17, 0x84, 0x55, 0xa8, 0x82, 0x77, 0x67, 0xaf, 0x3a, 0xa4, 0x52, 0xed, 0xee, 0x55,
|
||||
0xd5, 0x1d, 0xe4, 0x1d, 0x55, 0x71, 0x83, 0xc3, 0x51, 0xc3, 0xb9, 0xc4, 0xa9, 0x58, 0x0e, 0x3e,
|
||||
0x55, 0x39, 0x8c, 0x09, 0xaa, 0xb9, 0xb8, 0x6e, 0x1c, 0x1b, 0xd3, 0xc6, 0x1f, 0x9b, 0x79, 0x8f,
|
||||
0x3b, 0x7c, 0x36, 0xb5, 0xf8, 0x4a, 0x04, 0x96, 0xbd, 0x8d, 0x81, 0x6e, 0x68, 0x3e, 0xab, 0xf0,
|
||||
0x36, 0x46, 0x25, 0x77, 0x99, 0x1f, 0x60, 0xee, 0x6b, 0x0f, 0x33, 0xbd, 0x80, 0xc3, 0xbd, 0xea,
|
||||
0x23, 0xbc, 0xe9, 0xee, 0x7e, 0xb5, 0x44, 0xaa, 0xe4, 0x66, 0x2c, 0x07, 0x1f, 0x6b, 0xfa, 0x88,
|
||||
0xeb, 0xd9, 0x4a, 0x2f, 0x1f, 0x11, 0x5e, 0xfe, 0x9c, 0x7b, 0x79, 0x66, 0x91, 0xeb, 0xe4, 0x9c,
|
||||
0x1c, 0x72, 0x63, 0x51, 0xc1, 0xc7, 0x67, 0xa2, 0x25, 0xa8, 0x00, 0x15, 0x2e, 0xaa, 0x8d, 0xf4,
|
||||
0x90, 0x4b, 0x0e, 0x16, 0xf9, 0xc0, 0x73, 0xa2, 0x41, 0xc9, 0xbd, 0x7f, 0x68, 0xfa, 0x60, 0xe4,
|
||||
0x1e, 0x8a, 0x6d, 0x59, 0x2d, 0xcf, 0x67, 0xc6, 0xf1, 0x31, 0x6d, 0xfc, 0xf8, 0xcc, 0x8f, 0xb9,
|
||||
0x6b, 0x7d, 0x89, 0xa9, 0x65, 0xcf, 0x67, 0x9d, 0xd0, 0x1c, 0xc8, 0x75, 0xcd, 0x85, 0xdd, 0xd0,
|
||||
0x7c, 0xba, 0xec, 0x14, 0x47, 0x24, 0x8f, 0xa6, 0x26, 0x27, 0xa6, 0xfe, 0xaf, 0x72, 0x18, 0x9a,
|
||||
0xc7, 0x1c, 0xc2, 0x3a, 0x7b, 0x55, 0x85, 0x19, 0x95, 0xf0, 0x70, 0xaf, 0x7a, 0x5c, 0x34, 0xdd,
|
||||
0xdd, 0xaf, 0xe6, 0x98, 0xc0, 0xb2, 0x2e, 0xf8, 0xfa, 0x51, 0x7d, 0xac, 0xe0, 0x4d, 0x33, 0x70,
|
||||
0x99, 0x63, 0x23, 0xca, 0x92, 0xb8, 0x61, 0x9c, 0x18, 0xd3, 0xc6, 0x4f, 0xce, 0xfc, 0x91, 0xbb,
|
||||
0xd6, 0x9f, 0x18, 0x5c, 0x9a, 0xe5, 0x3b, 0xb9, 0x13, 0x9a, 0x83, 0x39, 0xa3, 0x91, 0xb8, 0x1b,
|
||||
0x9a, 0x37, 0xca, 0xee, 0x45, 0x98, 0xe4, 0xe0, 0x17, 0x1b, 0x8d, 0xc9, 0xa9, 0x5b, 0xb7, 0x6e,
|
||||
0x5e, 0xbb, 0x79, 0xfd, 0x4b, 0xb7, 0x22, 0x6f, 0x3b, 0x7b, 0x55, 0xa5, 0x41, 0xb5, 0xf8, 0x70,
|
||||
0xaf, 0x0a, 0xca, 0x46, 0x76, 0xf7, 0xab, 0x05, 0x9a, 0xf0, 0x89, 0x7c, 0xe3, 0xc4, 0xc3, 0x38,
|
||||
0x18, 0x81, 0x7b, 0xfa, 0xe9, 0x26, 0xda, 0xb6, 0x28, 0x26, 0x75, 0x6b, 0xb3, 0xd6, 0xa2, 0xc6,
|
||||
0xa3, 0x62, 0x32, 0x9f, 0xeb, 0x84, 0xe6, 0xa9, 0x26, 0xda, 0x5e, 0xc1, 0xa4, 0x7e, 0xa7, 0xd6,
|
||||
0xe2, 0xc1, 0x65, 0x40, 0xb8, 0x25, 0xc9, 0x92, 0xf9, 0x81, 0xb2, 0x62, 0x62, 0xd0, 0xc7, 0xf6,
|
||||
0x56, 0x64, 0xf0, 0xb1, 0x9c, 0x41, 0x88, 0xed, 0xad, 0xa2, 0xc1, 0x44, 0x96, 0x33, 0x98, 0x08,
|
||||
0xc1, 0x1f, 0x34, 0x7d, 0xc4, 0xc7, 0xb6, 0x47, 0x08, 0xb6, 0x79, 0x78, 0xb7, 0x1c, 0xc2, 0xb0,
|
||||
0xbf, 0x85, 0x5c, 0x8b, 0x1a, 0x27, 0x85, 0xed, 0xaf, 0x8a, 0xa0, 0x9e, 0xa8, 0x2c, 0xc4, 0xf0,
|
||||
0x0a, 0x8f, 0x1d, 0x72, 0xc3, 0x14, 0xe8, 0x86, 0xe6, 0xb8, 0xe8, 0x5b, 0x89, 0x4a, 0xb3, 0x74,
|
||||
0x63, 0x22, 0xa1, 0x74, 0xb8, 0x57, 0x3d, 0x7a, 0x63, 0x42, 0xc4, 0xf7, 0x52, 0x3f, 0x50, 0xdd,
|
||||
0x0b, 0x68, 0xe8, 0xfd, 0x3e, 0x76, 0xd1, 0x0e, 0x4d, 0x63, 0x80, 0x2e, 0x62, 0xc0, 0xcb, 0x9d,
|
||||
0xd0, 0x3c, 0x1d, 0x21, 0xd9, 0x46, 0xaf, 0xc4, 0x84, 0x24, 0x69, 0x71, 0x87, 0x27, 0x3b, 0x16,
|
||||
0xe6, 0x1b, 0x83, 0x77, 0x8e, 0xea, 0x17, 0xe2, 0x8e, 0x52, 0x22, 0xd9, 0x20, 0x35, 0x8d, 0x53,
|
||||
0x62, 0x90, 0xfe, 0xca, 0xd7, 0xf0, 0x08, 0xe4, 0x7a, 0x25, 0x17, 0x96, 0x3a, 0xa1, 0x39, 0xe2,
|
||||
0xab, 0xa1, 0x34, 0xd0, 0xf6, 0xc0, 0x25, 0x96, 0x93, 0x13, 0xd2, 0x96, 0xed, 0x69, 0xaf, 0x37,
|
||||
0xc4, 0x07, 0x79, 0x92, 0x0f, 0x72, 0x2f, 0x9a, 0xd0, 0x88, 0xfc, 0x2c, 0x23, 0xa0, 0xa6, 0x9f,
|
||||
0xa6, 0x0c, 0xf9, 0xcc, 0xaa, 0xf9, 0x5e, 0x9b, 0x62, 0xdf, 0xe8, 0x13, 0x63, 0xfd, 0xff, 0x9d,
|
||||
0xd0, 0xec, 0x13, 0xc0, 0x4c, 0x24, 0xef, 0x86, 0xe6, 0x93, 0xc2, 0x1d, 0x59, 0xd8, 0x73, 0xa4,
|
||||
0x73, 0x4d, 0xc1, 0x2f, 0x35, 0xfd, 0x1c, 0x41, 0xcc, 0x62, 0x3e, 0xe2, 0xa7, 0x1a, 0x72, 0xd3,
|
||||
0x89, 0xed, 0x17, 0x9d, 0xbd, 0x71, 0x10, 0x9a, 0xfa, 0xdd, 0xe9, 0xd5, 0x2c, 0xac, 0xeb, 0x04,
|
||||
0xb1, 0x6c, 0x8e, 0x4d, 0xd1, 0x71, 0x26, 0x52, 0x84, 0x70, 0xb9, 0x41, 0xee, 0x4b, 0x0a, 0xd7,
|
||||
0x52, 0x17, 0x70, 0x90, 0x20, 0xb6, 0x9a, 0xd0, 0x49, 0x16, 0xc4, 0x9f, 0x4a, 0x3c, 0x5d, 0x8c,
|
||||
0x28, 0xb6, 0x9a, 0xc6, 0x19, 0xb1, 0x14, 0xbe, 0xc9, 0x97, 0xc2, 0xc9, 0xbb, 0xd3, 0xab, 0x8b,
|
||||
0x5c, 0xcc, 0x27, 0xff, 0x0c, 0x41, 0x2c, 0xfa, 0x70, 0x48, 0xc0, 0x44, 0xf2, 0x53, 0x49, 0xc8,
|
||||
0xca, 0x72, 0xe5, 0xde, 0xe8, 0xec, 0x55, 0x4b, 0xed, 0xcb, 0xa2, 0x74, 0x07, 0x65, 0x1d, 0x43,
|
||||
0x20, 0xb3, 0x8f, 0x64, 0xe0, 0xef, 0x9a, 0x3e, 0x92, 0x27, 0xef, 0x63, 0x82, 0xdb, 0x62, 0x25,
|
||||
0x9f, 0x15, 0xf4, 0x77, 0x39, 0xfd, 0x53, 0x77, 0xa7, 0x57, 0x61, 0x04, 0x70, 0x07, 0x06, 0x08,
|
||||
0x62, 0xc9, 0x67, 0xea, 0x42, 0x35, 0x71, 0x21, 0x8f, 0x48, 0x4e, 0x5c, 0x93, 0x9d, 0x50, 0xd8,
|
||||
0x50, 0x09, 0xb9, 0x23, 0xd7, 0xb8, 0x23, 0x32, 0x05, 0x38, 0x24, 0xbb, 0x92, 0x48, 0x15, 0xce,
|
||||
0x30, 0xa7, 0x89, 0xbd, 0x80, 0x59, 0xd4, 0x18, 0xc8, 0x3b, 0xb3, 0x1a, 0x01, 0x2b, 0xb1, 0x33,
|
||||
0xc9, 0x27, 0x5f, 0xe9, 0xf5, 0x9c, 0x33, 0x79, 0xa4, 0xd7, 0xf6, 0x53, 0xd8, 0x50, 0x09, 0xd3,
|
||||
0x2d, 0x27, 0x53, 0xc8, 0x3b, 0x93, 0x48, 0xc1, 0x4f, 0x34, 0xdd, 0x08, 0x28, 0x5a, 0xc7, 0x96,
|
||||
0x8f, 0xf9, 0xb9, 0xef, 0x90, 0x75, 0x0b, 0xd9, 0x36, 0x6e, 0x31, 0x5c, 0x37, 0x80, 0xf0, 0x06,
|
||||
0xf1, 0x1d, 0xb0, 0x06, 0xa7, 0x63, 0x29, 0xdf, 0x01, 0x81, 0x9f, 0x7c, 0x75, 0x43, 0xf3, 0xac,
|
||||
0x70, 0x22, 0x13, 0x49, 0x84, 0x65, 0xc5, 0xdc, 0x17, 0x5f, 0xf1, 0x99, 0x49, 0x38, 0x2c, 0x28,
|
||||
0xc0, 0x84, 0x41, 0x22, 0x07, 0x6f, 0xe9, 0x43, 0x45, 0x72, 0x14, 0x63, 0x62, 0x0c, 0x0a, 0x62,
|
||||
0x0b, 0x07, 0xa1, 0x79, 0x62, 0x0d, 0xae, 0x60, 0x4c, 0x3a, 0xa1, 0x79, 0x22, 0xf0, 0xf9, 0xaf,
|
||||
0x6e, 0x68, 0xf6, 0xc5, 0x84, 0xf8, 0xa7, 0x44, 0x26, 0x51, 0x48, 0x7f, 0xed, 0xee, 0x57, 0xe3,
|
||||
0xe6, 0x10, 0xe4, 0x09, 0x70, 0x19, 0xf8, 0x81, 0xa6, 0x3f, 0x5e, 0xec, 0x3d, 0x20, 0xce, 0x1b,
|
||||
0x01, 0xb6, 0x9c, 0xba, 0x31, 0x24, 0x92, 0x88, 0xd7, 0xa3, 0xb1, 0x59, 0x13, 0xe2, 0x85, 0xb9,
|
||||
0x68, 0x6c, 0xe2, 0x2f, 0x79, 0x6c, 0x12, 0x85, 0x4a, 0x34, 0x28, 0xc9, 0x67, 0x57, 0xfe, 0x8a,
|
||||
0x07, 0x25, 0xc1, 0x8a, 0x83, 0x92, 0x68, 0x81, 0xbf, 0x68, 0xfa, 0x60, 0x89, 0x97, 0xef, 0x1a,
|
||||
0xe7, 0x04, 0xa3, 0xef, 0xf0, 0xb5, 0x77, 0x7c, 0x0d, 0xae, 0xc1, 0xc5, 0x4e, 0x68, 0x1e, 0x0f,
|
||||
0xfc, 0x35, 0xb8, 0xd8, 0x0d, 0xcd, 0x9b, 0x09, 0x11, 0xb8, 0x28, 0xad, 0xae, 0x0d, 0xc6, 0x5a,
|
||||
0xf4, 0xd6, 0xd5, 0xab, 0x75, 0xc4, 0xd0, 0x15, 0xba, 0x43, 0x6c, 0xb6, 0xc1, 0x8b, 0x35, 0x82,
|
||||
0xd9, 0x55, 0x82, 0xdb, 0x5c, 0xca, 0x09, 0xc7, 0x46, 0x92, 0x1f, 0x87, 0x7b, 0xd5, 0x87, 0x68,
|
||||
0xb8, 0xbb, 0x5f, 0x8d, 0x58, 0xc0, 0x81, 0x82, 0x1f, 0xbe, 0x0b, 0xfe, 0xad, 0xe9, 0x66, 0xd1,
|
||||
0x85, 0x96, 0x47, 0xf9, 0x09, 0x47, 0xb1, 0x1d, 0xf8, 0xd8, 0xdd, 0x31, 0x86, 0x45, 0xf8, 0xfd,
|
||||
0x91, 0xa8, 0x20, 0xd6, 0xe0, 0xb2, 0x47, 0xd9, 0x42, 0x0a, 0x76, 0x42, 0xf3, 0x6c, 0xe0, 0xe7,
|
||||
0x65, 0xdd, 0xd0, 0x7c, 0x2a, 0x76, 0x32, 0x0f, 0x48, 0xfe, 0x36, 0x90, 0x4b, 0x45, 0x48, 0x2e,
|
||||
0xb7, 0x56, 0xc8, 0x78, 0xe6, 0x29, 0x5a, 0xf0, 0x7a, 0xa1, 0x48, 0x01, 0x5e, 0xcc, 0xbb, 0x95,
|
||||
0x47, 0xc1, 0xbf, 0x14, 0x1e, 0x3a, 0xc4, 0x61, 0x0e, 0xaf, 0x23, 0xf8, 0x79, 0x67, 0x51, 0x63,
|
||||
0x44, 0xac, 0xe2, 0x1f, 0x8a, 0xea, 0x61, 0x0d, 0x2e, 0x44, 0xe8, 0x1c, 0x07, 0x79, 0xc0, 0x38,
|
||||
0x13, 0xf8, 0x39, 0x51, 0x1a, 0x2e, 0x0a, 0x72, 0x39, 0x58, 0xdc, 0x9c, 0xc8, 0x05, 0xf0, 0xa2,
|
||||
0x85, 0xb2, 0x88, 0x9f, 0x40, 0xbc, 0x15, 0x2f, 0x18, 0x0a, 0x14, 0xe0, 0x85, 0xbc, 0x83, 0x39,
|
||||
0x10, 0x78, 0xfa, 0x80, 0x8f, 0xa3, 0xc3, 0xd9, 0x23, 0x56, 0x1b, 0x6d, 0xe2, 0xa0, 0x65, 0x18,
|
||||
0x62, 0xca, 0x66, 0x39, 0xf9, 0x18, 0xbc, 0x47, 0x5e, 0x13, 0x50, 0x4a, 0xbe, 0x20, 0xef, 0x79,
|
||||
0x48, 0x17, 0x0d, 0x80, 0x6f, 0x69, 0xfa, 0x08, 0x0a, 0x98, 0x67, 0x05, 0xad, 0x75, 0x1f, 0xd5,
|
||||
0x71, 0x96, 0x0c, 0x6d, 0x18, 0x8f, 0x8b, 0x81, 0x5c, 0xe6, 0x25, 0x17, 0x57, 0x59, 0x8b, 0x34,
|
||||
0x92, 0x3c, 0xe2, 0x76, 0x5a, 0x9d, 0xa8, 0x40, 0x79, 0xf8, 0xa6, 0xe4, 0xcc, 0x70, 0x72, 0x0a,
|
||||
0x2a, 0xad, 0x81, 0xa6, 0x3e, 0x92, 0x70, 0x60, 0x9e, 0xd5, 0xf2, 0xf9, 0x14, 0x8b, 0xb3, 0x98,
|
||||
0x1a, 0xe7, 0xc5, 0x00, 0xdc, 0xe0, 0x44, 0x62, 0x95, 0x55, 0x6f, 0xd9, 0xc7, 0x30, 0xc6, 0xbb,
|
||||
0xa1, 0x79, 0x3e, 0x9a, 0x42, 0x05, 0x58, 0x81, 0xca, 0x36, 0x60, 0x4b, 0x07, 0x9b, 0x18, 0xb7,
|
||||
0x2c, 0x86, 0x9b, 0x2d, 0xcf, 0x47, 0xbe, 0x83, 0xa9, 0xb5, 0x61, 0x5c, 0x10, 0x2e, 0xdf, 0xe6,
|
||||
0x1b, 0x81, 0xa3, 0xab, 0x19, 0xc8, 0xdd, 0xbd, 0x24, 0x7a, 0x29, 0x02, 0x72, 0x2d, 0x76, 0x5d,
|
||||
0x76, 0x75, 0xea, 0x3a, 0x2c, 0x59, 0x01, 0x3b, 0xfa, 0xa0, 0x8d, 0xec, 0x0d, 0x6c, 0x39, 0xeb,
|
||||
0xc4, 0xf3, 0x71, 0xdd, 0x6a, 0x38, 0x2e, 0xa6, 0xc6, 0x45, 0xe1, 0xe2, 0x02, 0x3f, 0xd1, 0x04,
|
||||
0xbc, 0x10, 0xa1, 0xf3, 0x1c, 0x4c, 0x07, 0xba, 0x84, 0x94, 0xf6, 0x60, 0xba, 0xb7, 0x60, 0xd9,
|
||||
0x0c, 0xf8, 0x9e, 0xa6, 0x9f, 0x6f, 0xf9, 0xde, 0x3a, 0x2f, 0x66, 0xac, 0xa0, 0x55, 0x47, 0x0c,
|
||||
0xcb, 0x05, 0xc2, 0x13, 0xc2, 0xf7, 0x55, 0x9e, 0xdf, 0x26, 0x5a, 0x6b, 0x42, 0x49, 0x2e, 0x06,
|
||||
0xa2, 0x22, 0xbb, 0x07, 0x2e, 0xd1, 0x79, 0x51, 0x1a, 0x08, 0xed, 0x45, 0xd8, 0xcb, 0x22, 0x78,
|
||||
0x47, 0xd3, 0x87, 0x5d, 0xa7, 0xe9, 0x30, 0xab, 0x86, 0x48, 0xbd, 0xed, 0xd4, 0xd9, 0x86, 0xe5,
|
||||
0x10, 0xcb, 0x45, 0xc4, 0x18, 0x15, 0x43, 0xb2, 0x24, 0x8a, 0x47, 0xae, 0x31, 0x93, 0x28, 0x2c,
|
||||
0x90, 0x45, 0x44, 0xb2, 0x82, 0xbf, 0x8c, 0x7d, 0xc6, 0xb0, 0xa8, 0x4c, 0x81, 0xb7, 0x35, 0x1d,
|
||||
0x34, 0x1d, 0x62, 0x6d, 0x78, 0x4d, 0x6c, 0xd5, 0x1d, 0xba, 0x69, 0x35, 0x7c, 0x8c, 0x0d, 0x73,
|
||||
0x4c, 0x1b, 0x3f, 0x35, 0xd5, 0x77, 0x25, 0xba, 0x59, 0xbb, 0xb2, 0xe2, 0xbc, 0x89, 0x67, 0x5e,
|
||||
0xf9, 0x30, 0x34, 0x8f, 0xf0, 0x9d, 0xd8, 0x74, 0xc8, 0x6d, 0xaf, 0x89, 0xe7, 0x1c, 0xba, 0x39,
|
||||
0xef, 0x63, 0x9c, 0xae, 0x8e, 0x82, 0x5c, 0xde, 0x07, 0x63, 0x97, 0x39, 0x91, 0x63, 0x93, 0x63,
|
||||
0x97, 0x61, 0xb1, 0x39, 0xb8, 0xaf, 0xe9, 0x7d, 0xc9, 0x7a, 0x17, 0xc7, 0xce, 0x98, 0x38, 0x76,
|
||||
0xfe, 0x2c, 0x52, 0x9e, 0x64, 0xd1, 0x46, 0x87, 0xcf, 0x29, 0x3f, 0xfb, 0xec, 0x86, 0xe6, 0x5c,
|
||||
0x52, 0x71, 0x24, 0x32, 0xc5, 0x41, 0x14, 0xef, 0x00, 0x5a, 0x38, 0x53, 0x9a, 0x98, 0xa1, 0x2b,
|
||||
0x5f, 0xa6, 0x1e, 0xe1, 0xb1, 0x3b, 0x67, 0x36, 0xff, 0x79, 0xb8, 0x57, 0x1d, 0x7f, 0x58, 0x53,
|
||||
0x3c, 0x3f, 0x92, 0xf8, 0xc2, 0xcc, 0x8e, 0xef, 0x82, 0xd7, 0xf4, 0x01, 0xe4, 0xb6, 0x79, 0xf5,
|
||||
0x15, 0xdd, 0x26, 0x10, 0xcc, 0xa8, 0xf1, 0xa4, 0xb8, 0xc4, 0xe3, 0x45, 0xef, 0x99, 0x08, 0x14,
|
||||
0x55, 0xf9, 0x5d, 0xcc, 0xf8, 0xc2, 0x1f, 0x8a, 0x22, 0x4c, 0x4e, 0x5e, 0x81, 0x45, 0x45, 0xf0,
|
||||
0x5f, 0x4d, 0x1f, 0xf7, 0xb6, 0xb0, 0xdf, 0xf6, 0x1d, 0xc6, 0x03, 0x47, 0xd3, 0x63, 0xd8, 0xaa,
|
||||
0xe3, 0x2d, 0xc7, 0xc6, 0x16, 0x41, 0x4d, 0x4c, 0x79, 0x38, 0x8d, 0x0b, 0x21, 0xa3, 0x92, 0x5d,
|
||||
0x2f, 0x8d, 0xdc, 0x4b, 0x1a, 0x41, 0xd1, 0x66, 0x0e, 0x6f, 0xdd, 0xe5, 0xea, 0x9d, 0xd0, 0xbc,
|
||||
0xe4, 0x95, 0x20, 0xc7, 0xc6, 0x02, 0xbd, 0x47, 0x66, 0x23, 0x53, 0xdd, 0xd0, 0x7c, 0x49, 0x10,
|
||||
0x7c, 0x08, 0xdd, 0xde, 0x8b, 0x92, 0x57, 0x71, 0x3d, 0x78, 0xc0, 0x87, 0x61, 0x01, 0xbe, 0xa6,
|
||||
0x9f, 0xe3, 0x61, 0xcc, 0x72, 0x48, 0x1d, 0x6f, 0x5b, 0x7c, 0x25, 0xd7, 0x5c, 0xcf, 0xde, 0xa4,
|
||||
0xc6, 0x25, 0xb1, 0xa5, 0xf9, 0xa2, 0x01, 0x5c, 0x61, 0x81, 0xe3, 0x4b, 0x0e, 0x99, 0x11, 0x68,
|
||||
0x7a, 0x6b, 0x5b, 0x86, 0x94, 0x99, 0x72, 0x94, 0xff, 0x42, 0x85, 0x25, 0xf0, 0x4f, 0x9e, 0xee,
|
||||
0x12, 0x64, 0x6f, 0xe2, 0xba, 0x45, 0x3c, 0xe6, 0x34, 0x1c, 0x1b, 0x45, 0xf7, 0x0f, 0x75, 0x6a,
|
||||
0x54, 0xc5, 0xfc, 0xbe, 0xcb, 0x87, 0x7b, 0x78, 0x2d, 0x52, 0xba, 0x2b, 0xe9, 0x2c, 0xcc, 0xf1,
|
||||
0xd1, 0x1e, 0x0e, 0x94, 0x48, 0x37, 0x34, 0x2f, 0x44, 0xa1, 0x5d, 0x05, 0x8b, 0xbb, 0x4a, 0x25,
|
||||
0xd2, 0xdd, 0xab, 0xf6, 0xb0, 0xb8, 0xbb, 0x5f, 0xed, 0xc1, 0x02, 0x2a, 0x5b, 0xd4, 0x29, 0x80,
|
||||
0xfa, 0x69, 0xe6, 0xa3, 0x46, 0xc3, 0xb1, 0x2d, 0xdb, 0x45, 0x94, 0x1a, 0x97, 0xc5, 0xb0, 0xbe,
|
||||
0xc0, 0xeb, 0xe5, 0x18, 0x98, 0xe5, 0xf2, 0x6e, 0x68, 0x82, 0x68, 0x40, 0x25, 0x61, 0x7a, 0x51,
|
||||
0x93, 0x53, 0x05, 0x6f, 0xe9, 0x83, 0xf1, 0x10, 0x5b, 0x0d, 0xcf, 0xad, 0x63, 0xdf, 0x6a, 0x21,
|
||||
0xb6, 0x61, 0x3c, 0x25, 0x76, 0xfd, 0x9d, 0x83, 0xd0, 0xbc, 0x30, 0x87, 0x5b, 0x3e, 0xb6, 0x11,
|
||||
0xc3, 0xf5, 0xb9, 0x48, 0x71, 0x5e, 0xe8, 0x2d, 0x23, 0xb6, 0xd1, 0x09, 0x4d, 0xed, 0x85, 0xb4,
|
||||
0x3a, 0xaf, 0x17, 0xe1, 0xe7, 0xbd, 0xa6, 0xc3, 0x27, 0x89, 0xed, 0x54, 0x0c, 0x0d, 0x0e, 0x94,
|
||||
0x70, 0xb0, 0xa9, 0x9f, 0xa5, 0x98, 0x59, 0xae, 0xd7, 0xb6, 0x5a, 0xbe, 0xe3, 0xf9, 0x0e, 0xdb,
|
||||
0x31, 0x9e, 0x16, 0x9b, 0x62, 0xba, 0x13, 0x9a, 0xfd, 0x14, 0xb3, 0x45, 0xaf, 0xbd, 0x1c, 0x23,
|
||||
0x69, 0x64, 0xcb, 0x8b, 0x7b, 0xa6, 0x18, 0x85, 0xe6, 0xe0, 0x3d, 0x4d, 0x1f, 0x6e, 0xa2, 0xed,
|
||||
0xc4, 0x4d, 0xdb, 0x23, 0x76, 0xe0, 0xfb, 0x98, 0xd8, 0x3b, 0xc6, 0xb8, 0x18, 0x47, 0x2a, 0x2e,
|
||||
0x5b, 0x50, 0x7b, 0x09, 0x6d, 0x47, 0x1c, 0x67, 0x33, 0x15, 0x7e, 0xe4, 0x37, 0x15, 0xf2, 0xf4,
|
||||
0xc8, 0x57, 0x81, 0xc9, 0x90, 0x8b, 0xdb, 0x11, 0xb5, 0x5d, 0xa8, 0xb4, 0x0a, 0x3e, 0xd6, 0xf4,
|
||||
0x41, 0xdb, 0x47, 0x74, 0xa3, 0x50, 0x03, 0x3c, 0x23, 0xa6, 0xe5, 0x7d, 0x51, 0x03, 0xcc, 0x26,
|
||||
0x35, 0x80, 0x1d, 0xd7, 0x00, 0xf3, 0xd1, 0xd9, 0xcc, 0x9b, 0x65, 0xd9, 0xb8, 0x32, 0x0c, 0x0b,
|
||||
0x9d, 0x72, 0x5e, 0x2f, 0xc4, 0x7c, 0x2d, 0x0f, 0x94, 0x8c, 0xf0, 0xea, 0xc0, 0x8e, 0xab, 0x83,
|
||||
0xea, 0xc3, 0x98, 0xe1, 0xf5, 0xc1, 0x6c, 0x54, 0x1f, 0x14, 0x8c, 0xf9, 0x2e, 0xf8, 0x99, 0xa6,
|
||||
0x8f, 0x14, 0xdd, 0x4b, 0xae, 0x65, 0x9e, 0x15, 0xf3, 0xef, 0x1c, 0x84, 0xe6, 0xc9, 0x59, 0x28,
|
||||
0xbd, 0x28, 0xe4, 0xad, 0x14, 0x5f, 0x14, 0x94, 0x68, 0xaf, 0xa5, 0xb1, 0xbb, 0x5f, 0xcd, 0x6c,
|
||||
0x43, 0xb5, 0x65, 0xf0, 0x0d, 0x4d, 0x1f, 0xa6, 0x2c, 0x20, 0x16, 0xcf, 0x9c, 0x90, 0xeb, 0x6c,
|
||||
0x61, 0x2b, 0xca, 0x87, 0xa9, 0xf1, 0x5c, 0x9a, 0x8f, 0x0e, 0x72, 0x8d, 0x3b, 0x89, 0xc2, 0x0a,
|
||||
0xc7, 0x57, 0xd2, 0x2c, 0x49, 0x81, 0xe5, 0x93, 0x79, 0x29, 0xa0, 0x1d, 0x9b, 0xbc, 0x39, 0x01,
|
||||
0x55, 0xd6, 0x78, 0x8d, 0x5c, 0xa0, 0xc1, 0xe3, 0x2a, 0x35, 0x9e, 0x17, 0x24, 0x5e, 0xe5, 0x89,
|
||||
0x5a, 0xae, 0xd9, 0x92, 0x43, 0xb2, 0x5a, 0xa2, 0x84, 0xc8, 0x39, 0x62, 0x2e, 0xa0, 0x4e, 0x4d,
|
||||
0xc0, 0xb2, 0x1d, 0x9e, 0x95, 0xf7, 0x89, 0xde, 0x93, 0x87, 0xae, 0x17, 0x44, 0x0c, 0xad, 0x1f,
|
||||
0x84, 0x66, 0x3f, 0x44, 0xed, 0x15, 0x16, 0x48, 0x4f, 0x5c, 0xa7, 0x68, 0xf6, 0x99, 0x5e, 0x46,
|
||||
0x65, 0xb2, 0x07, 0x3e, 0xc3, 0x15, 0x2c, 0x42, 0xd9, 0x1e, 0xd8, 0xd2, 0xcf, 0xf0, 0xb2, 0xb3,
|
||||
0x86, 0x28, 0xb6, 0xa2, 0x37, 0x47, 0xe3, 0xca, 0x98, 0x36, 0xde, 0x3f, 0xd5, 0x9f, 0xa4, 0x45,
|
||||
0xab, 0x42, 0x2a, 0x6e, 0x0f, 0xfb, 0x13, 0xd5, 0x48, 0x96, 0x46, 0x8e, 0xbc, 0xb8, 0x32, 0x16,
|
||||
0x17, 0x21, 0xf1, 0xf2, 0x78, 0x7b, 0xbf, 0xaa, 0xc1, 0x42, 0x53, 0xf0, 0xfd, 0xa3, 0xfa, 0x25,
|
||||
0x1e, 0x35, 0xd2, 0x70, 0xc1, 0x8b, 0x58, 0xdb, 0x6b, 0xf2, 0x25, 0xeb, 0xe3, 0x37, 0x02, 0x4c,
|
||||
0x99, 0xb5, 0xe9, 0xd4, 0x8c, 0xab, 0x62, 0x3a, 0xfe, 0xa6, 0xc5, 0x6f, 0x95, 0x4b, 0x68, 0x7b,
|
||||
0x76, 0x01, 0x46, 0xf8, 0x1d, 0x67, 0xa6, 0x13, 0x9a, 0x66, 0x13, 0x6d, 0xa7, 0x5b, 0x9c, 0x2d,
|
||||
0xc4, 0x36, 0x32, 0x95, 0xf4, 0x14, 0x7c, 0x80, 0x9e, 0x54, 0x00, 0x3e, 0xd0, 0xe4, 0x83, 0x55,
|
||||
0xe2, 0xd7, 0xcf, 0x02, 0x5d, 0xf8, 0x80, 0x66, 0x35, 0xf0, 0xa9, 0xa6, 0x0f, 0xa7, 0x4f, 0x30,
|
||||
0x2e, 0x92, 0x1f, 0x6d, 0x27, 0xc4, 0x06, 0xfe, 0x80, 0x8f, 0xc4, 0x50, 0xf2, 0x84, 0xb1, 0x38,
|
||||
0x7d, 0x57, 0x7e, 0xb7, 0x1d, 0x42, 0x0a, 0x79, 0x9a, 0x48, 0xab, 0x40, 0xd5, 0xcb, 0x99, 0xd2,
|
||||
0x48, 0x0f, 0xb9, 0xb4, 0xf5, 0x95, 0xa4, 0x60, 0xd6, 0x0a, 0x49, 0x8f, 0xbe, 0x5b, 0xfa, 0x79,
|
||||
0xf1, 0xca, 0xd2, 0x08, 0x5c, 0x37, 0xce, 0x6a, 0x3c, 0x92, 0x94, 0xa8, 0xc6, 0xa4, 0xf0, 0xf4,
|
||||
0x16, 0xcf, 0x1a, 0xb8, 0xd6, 0x7c, 0xe0, 0xba, 0x22, 0x1f, 0xb9, 0x47, 0xe2, 0xa2, 0xb2, 0x1b,
|
||||
0x9a, 0x17, 0xe3, 0x23, 0x4b, 0x05, 0x57, 0x60, 0x8f, 0x76, 0xe0, 0x55, 0xfd, 0x74, 0x03, 0x23,
|
||||
0x16, 0xf8, 0xd8, 0x6a, 0xb8, 0x68, 0x9d, 0x1a, 0x53, 0x62, 0xdf, 0x5d, 0xe6, 0x27, 0x7d, 0x0c,
|
||||
0xcc, 0x73, 0x79, 0xfa, 0x22, 0x23, 0x09, 0x2b, 0x30, 0xa7, 0x02, 0xda, 0xfa, 0x88, 0xf4, 0x10,
|
||||
0x13, 0xd5, 0x38, 0x98, 0x78, 0xc1, 0xfa, 0x86, 0x71, 0x4d, 0x2c, 0xda, 0x97, 0x45, 0x78, 0x4d,
|
||||
0x55, 0x16, 0xb9, 0xc6, 0x2b, 0x42, 0x21, 0xcd, 0x7a, 0x94, 0x68, 0x9a, 0x51, 0xa8, 0x1b, 0x83,
|
||||
0x4d, 0x7d, 0xa8, 0xd4, 0x71, 0x13, 0x6d, 0x1b, 0xd7, 0x45, 0xaf, 0x2f, 0xf1, 0x64, 0xb0, 0xd0,
|
||||
0x70, 0x09, 0x6d, 0x77, 0x43, 0xd3, 0x50, 0x75, 0xb9, 0x84, 0xb6, 0xd3, 0xfe, 0x14, 0xcd, 0xc0,
|
||||
0x57, 0xf4, 0xbe, 0xa0, 0x45, 0x5a, 0xe9, 0x31, 0xf2, 0xab, 0x79, 0x31, 0x39, 0x5f, 0x38, 0x08,
|
||||
0xcd, 0x73, 0x59, 0x06, 0xb3, 0xb6, 0x4c, 0x96, 0xb3, 0x33, 0x45, 0xe4, 0x2e, 0x71, 0x5a, 0xd7,
|
||||
0x22, 0xad, 0x18, 0x90, 0xb2, 0x96, 0xdd, 0xfd, 0xaa, 0xba, 0xb1, 0xa1, 0xc1, 0x53, 0x52, 0x13,
|
||||
0xf0, 0x0b, 0x2d, 0xee, 0x3e, 0xb9, 0xb4, 0x7f, 0x6f, 0x5e, 0x38, 0xf9, 0xb6, 0xd8, 0x05, 0x79,
|
||||
0x13, 0xe9, 0x05, 0xbe, 0xe8, 0x7e, 0x2c, 0xed, 0x5e, 0xbe, 0x78, 0x97, 0x38, 0x64, 0xdb, 0xfd,
|
||||
0x7c, 0x6f, 0x2d, 0xbe, 0xac, 0x55, 0xbd, 0x18, 0x1a, 0xd4, 0xb3, 0x56, 0xe0, 0x77, 0x9a, 0xde,
|
||||
0x2f, 0x68, 0x66, 0xd7, 0xf3, 0xbf, 0x8e, 0x88, 0x7e, 0x5b, 0x64, 0xc5, 0x79, 0x13, 0xd2, 0x55,
|
||||
0xbd, 0xa0, 0x5a, 0x49, 0xa9, 0xe6, 0x2f, 0xd7, 0x95, 0x64, 0x2f, 0x7e, 0x96, 0x1e, 0xcf, 0x7d,
|
||||
0xd5, 0x7d, 0x19, 0x1a, 0xec, 0x93, 0x5b, 0x66, 0x94, 0xb3, 0x4b, 0xf8, 0xf7, 0x7b, 0x53, 0x96,
|
||||
0x2e, 0xe4, 0x0b, 0x94, 0xf3, 0x57, 0xe8, 0xbd, 0x29, 0xf7, 0xd2, 0x2b, 0x53, 0x4e, 0x34, 0x13,
|
||||
0xca, 0xe9, 0x9d, 0x7b, 0x43, 0x8f, 0x1e, 0xfb, 0xd2, 0x43, 0xf3, 0x37, 0xf3, 0x62, 0xf7, 0x7e,
|
||||
0x2e, 0xcf, 0x57, 0xbc, 0x97, 0x65, 0xa7, 0xa7, 0xb4, 0x18, 0xfd, 0x0c, 0xc9, 0xa7, 0xd0, 0x7d,
|
||||
0x12, 0x42, 0xc5, 0x95, 0x45, 0xf9, 0xb6, 0xc0, 0x6a, 0xd9, 0xcc, 0xf8, 0x80, 0x0f, 0x91, 0x36,
|
||||
0xb3, 0x74, 0x10, 0x9a, 0x17, 0xb3, 0x1e, 0x97, 0xf2, 0xb5, 0xfe, 0xb2, 0xcd, 0xf2, 0xe3, 0xd4,
|
||||
0x2c, 0xe1, 0xf9, 0xee, 0x41, 0x59, 0x81, 0x67, 0x08, 0x43, 0x85, 0xf3, 0x91, 0xda, 0x88, 0x50,
|
||||
0xe3, 0xb7, 0xd1, 0x2c, 0xad, 0x16, 0x28, 0xc8, 0xe7, 0xca, 0x0a, 0x57, 0x2c, 0x50, 0x28, 0xe1,
|
||||
0xe5, 0xa9, 0x12, 0x4c, 0x4a, 0x7a, 0x33, 0x77, 0x3e, 0xfc, 0x64, 0xf4, 0xc8, 0xfe, 0x27, 0xa3,
|
||||
0x47, 0x3e, 0x3c, 0x18, 0xd5, 0xf6, 0x0f, 0x46, 0xb5, 0xef, 0xde, 0x1f, 0x3d, 0xf2, 0xee, 0xfd,
|
||||
0x51, 0x6d, 0xff, 0xfe, 0xe8, 0x91, 0x8f, 0xee, 0x8f, 0x1e, 0x79, 0xfd, 0x99, 0x75, 0x87, 0x6d,
|
||||
0x04, 0xb5, 0x2b, 0xb6, 0xd7, 0xbc, 0x9a, 0x66, 0xad, 0xd2, 0xaf, 0xec, 0xdf, 0x4b, 0xb5, 0x13,
|
||||
0xe2, 0xef, 0x4a, 0xd7, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x30, 0xd1, 0x0d, 0x1a, 0x25,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *OptionsConfiguration) Marshal() (dAtA []byte, err error) {
|
||||
@@ -558,10 +559,10 @@ func (m *OptionsConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i--
|
||||
dAtA[i] = 0xb8
|
||||
}
|
||||
if len(m.DefaultFolderPath) > 0 {
|
||||
i -= len(m.DefaultFolderPath)
|
||||
copy(dAtA[i:], m.DefaultFolderPath)
|
||||
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(len(m.DefaultFolderPath)))
|
||||
if len(m.DeprecatedDefaultFolderPath) > 0 {
|
||||
i -= len(m.DeprecatedDefaultFolderPath)
|
||||
copy(dAtA[i:], m.DeprecatedDefaultFolderPath)
|
||||
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(len(m.DeprecatedDefaultFolderPath)))
|
||||
i--
|
||||
dAtA[i] = 0x2
|
||||
i--
|
||||
@@ -1018,7 +1019,7 @@ func (m *OptionsConfiguration) ProtoSize() (n int) {
|
||||
if m.TrafficClass != 0 {
|
||||
n += 2 + sovOptionsconfiguration(uint64(m.TrafficClass))
|
||||
}
|
||||
l = len(m.DefaultFolderPath)
|
||||
l = len(m.DeprecatedDefaultFolderPath)
|
||||
if l > 0 {
|
||||
n += 2 + l + sovOptionsconfiguration(uint64(l))
|
||||
}
|
||||
@@ -1948,7 +1949,7 @@ func (m *OptionsConfiguration) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
case 38:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field DefaultFolderPath", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedDefaultFolderPath", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -1976,7 +1977,7 @@ func (m *OptionsConfiguration) Unmarshal(dAtA []byte) error {
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.DefaultFolderPath = string(dAtA[iNdEx:postIndex])
|
||||
m.DeprecatedDefaultFolderPath = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 39:
|
||||
if wireType != 0 {
|
||||
|
||||
44
lib/config/testdata/overridenvalues.xml
vendored
44
lib/config/testdata/overridenvalues.xml
vendored
@@ -1,4 +1,4 @@
|
||||
<configuration version="31">
|
||||
<configuration version="34">
|
||||
<options>
|
||||
<listenAddress>tcp://:23000</listenAddress>
|
||||
<allowDelete>false</allowDelete>
|
||||
@@ -36,7 +36,6 @@
|
||||
<releasesURL>https://localhost/releases</releasesURL>
|
||||
<overwriteRemoteDeviceNamesOnConnect>true</overwriteRemoteDeviceNamesOnConnect>
|
||||
<tempIndexMinBlocks>100</tempIndexMinBlocks>
|
||||
<defaultFolderPath>/media/syncthing</defaultFolderPath>
|
||||
<setLowPriority>false</setLowPriority>
|
||||
<crashReportingURL>https://localhost/newcrash</crashReportingURL>
|
||||
<crashReportingEnabled>false</crashReportingEnabled>
|
||||
@@ -47,4 +46,45 @@
|
||||
<announceLANAddresses>false</announceLANAddresses>
|
||||
<featureFlag>feature</featureFlag>
|
||||
</options>
|
||||
<defaults>
|
||||
<folder id="" label="" path="/media/syncthing" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning type="trashcan">
|
||||
<param key="cleanoutDays" val="0"></param>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
</folder>
|
||||
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
</device>
|
||||
</defaults>
|
||||
</configuration>
|
||||
|
||||
@@ -11,14 +11,17 @@ import (
|
||||
"encoding/xml"
|
||||
"sort"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/util"
|
||||
)
|
||||
|
||||
// internalVersioningConfiguration is used in XML serialization
|
||||
type internalVersioningConfiguration struct {
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
Params []internalParam `xml:"param"`
|
||||
CleanupIntervalS int `xml:"cleanupIntervalS" default:"3600"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
Params []internalParam `xml:"param"`
|
||||
CleanupIntervalS int `xml:"cleanupIntervalS" default:"3600"`
|
||||
FSPath string `xml:"fsPath"`
|
||||
FSType fs.FilesystemType `xml:"fsType"`
|
||||
}
|
||||
|
||||
type internalParam struct {
|
||||
@@ -52,7 +55,7 @@ func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartEl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *VersioningConfiguration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
func (c VersioningConfiguration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
// Using EncodeElement instead of plain Encode ensures that we use the
|
||||
// outer tag name from the VersioningConfiguration (i.e.,
|
||||
// `<versioning>`) rather than whatever the internal representation
|
||||
@@ -64,6 +67,8 @@ func (c *VersioningConfiguration) toInternal() internalVersioningConfiguration {
|
||||
var tmp internalVersioningConfiguration
|
||||
tmp.Type = c.Type
|
||||
tmp.CleanupIntervalS = c.CleanupIntervalS
|
||||
tmp.FSPath = c.FSPath
|
||||
tmp.FSType = c.FSType
|
||||
for k, v := range c.Params {
|
||||
tmp.Params = append(tmp.Params, internalParam{k, v})
|
||||
}
|
||||
@@ -76,6 +81,8 @@ func (c *VersioningConfiguration) toInternal() internalVersioningConfiguration {
|
||||
func (c *VersioningConfiguration) fromInternal(intCfg internalVersioningConfiguration) {
|
||||
c.Type = intCfg.Type
|
||||
c.CleanupIntervalS = intCfg.CleanupIntervalS
|
||||
c.FSPath = intCfg.FSPath
|
||||
c.FSType = intCfg.FSType
|
||||
c.Params = make(map[string]string, len(intCfg.Params))
|
||||
for _, p := range intCfg.Params {
|
||||
c.Params[p.Key] = p.Val
|
||||
|
||||
@@ -6,6 +6,7 @@ package config
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
fs "github.com/syncthing/syncthing/lib/fs"
|
||||
_ "github.com/syncthing/syncthing/proto/ext"
|
||||
io "io"
|
||||
math "math"
|
||||
@@ -25,9 +26,11 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// VersioningConfiguration is used in the code and for JSON serialization
|
||||
type VersioningConfiguration struct {
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type" `
|
||||
Params map[string]string `protobuf:"bytes,2,rep,name=parameters,proto3" json:"params" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
CleanupIntervalS int `protobuf:"varint,3,opt,name=cleanup_interval_s,json=cleanupIntervalS,proto3,casttype=int" json:"cleanupIntervalS" default:"3600"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type" xml:"type,attr"`
|
||||
Params map[string]string `protobuf:"bytes,2,rep,name=parameters,proto3" json:"params" xml:"parameter" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
CleanupIntervalS int `protobuf:"varint,3,opt,name=cleanup_interval_s,json=cleanupIntervalS,proto3,casttype=int" json:"cleanupIntervalS" xml:"cleanupIntervalS" default:"3600"`
|
||||
FSPath string `protobuf:"bytes,4,opt,name=fs_path,json=fsPath,proto3" json:"fsPath" xml:"fsPath"`
|
||||
FSType fs.FilesystemType `protobuf:"varint,5,opt,name=fs_type,json=fsType,proto3,enum=fs.FilesystemType" json:"fsType" xml:"fsType"`
|
||||
}
|
||||
|
||||
func (m *VersioningConfiguration) Reset() { *m = VersioningConfiguration{} }
|
||||
@@ -73,32 +76,40 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_95ba6bdb22ffea81 = []byte{
|
||||
// 385 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xbf, 0xcb, 0xd3, 0x40,
|
||||
0x18, 0xbe, 0x6b, 0xfa, 0x05, 0xbe, 0xfb, 0xfc, 0x45, 0x16, 0xc3, 0x07, 0xde, 0x85, 0x9a, 0x21,
|
||||
0x2e, 0x49, 0xf1, 0x43, 0x91, 0x8e, 0x11, 0x07, 0x71, 0x91, 0x0a, 0x82, 0x2e, 0x25, 0x8d, 0xd7,
|
||||
0xf4, 0x30, 0xbd, 0x84, 0xe4, 0x52, 0xcc, 0xe8, 0x20, 0x38, 0xaa, 0x7f, 0x81, 0x7f, 0x4e, 0xb7,
|
||||
0x66, 0x74, 0x3a, 0x68, 0xb3, 0x65, 0xec, 0xd8, 0x49, 0x72, 0x09, 0xb5, 0x28, 0x6e, 0xcf, 0xf3,
|
||||
0xbc, 0xcf, 0xf3, 0x3e, 0xe1, 0xcd, 0x21, 0x27, 0x66, 0x73, 0x2f, 0x4c, 0xf8, 0x82, 0x45, 0xde,
|
||||
0x9a, 0x66, 0x39, 0x4b, 0x38, 0xe3, 0x51, 0x27, 0x14, 0x59, 0x20, 0x58, 0xc2, 0xdd, 0x34, 0x4b,
|
||||
0x44, 0x62, 0xe8, 0x9d, 0x78, 0x7d, 0x49, 0x3f, 0x89, 0x4e, 0x1a, 0x7d, 0xd1, 0xd0, 0xfd, 0xb7,
|
||||
0xa7, 0xd0, 0xf3, 0xf3, 0x90, 0x61, 0xa1, 0xa1, 0x28, 0x53, 0x6a, 0x42, 0x0b, 0x3a, 0x97, 0xfe,
|
||||
0xad, 0x46, 0x12, 0xc5, 0x0f, 0x92, 0x80, 0xa9, 0x42, 0xc6, 0x67, 0x88, 0x50, 0x1a, 0x64, 0xc1,
|
||||
0x8a, 0x0a, 0x9a, 0xe5, 0xe6, 0xc0, 0xd2, 0x9c, 0xab, 0xc7, 0x9e, 0xdb, 0xd5, 0xb8, 0xff, 0xd9,
|
||||
0xeb, 0xbe, 0x3e, 0x25, 0x5e, 0x70, 0x91, 0x95, 0xfe, 0x78, 0x23, 0x09, 0xd8, 0x4b, 0xa2, 0xab,
|
||||
0x41, 0xde, 0x48, 0xa2, 0xab, 0xa5, 0x79, 0xdb, 0x74, 0xd8, 0xda, 0x3d, 0xfb, 0x51, 0xd9, 0xbd,
|
||||
0x63, 0x7a, 0x56, 0x6a, 0x84, 0xc8, 0x08, 0x63, 0x1a, 0xf0, 0x22, 0x9d, 0x31, 0x2e, 0x68, 0xb6,
|
||||
0x0e, 0xe2, 0x59, 0x6e, 0x6a, 0x16, 0x74, 0x2e, 0xfc, 0x27, 0x8d, 0x24, 0xf7, 0xfa, 0xe9, 0xcb,
|
||||
0x7e, 0xf8, 0xe6, 0x20, 0xc9, 0x9d, 0x0f, 0x74, 0x11, 0x14, 0xb1, 0x98, 0x8c, 0x6e, 0x9e, 0x8e,
|
||||
0xc7, 0xa3, 0xa3, 0x24, 0x1a, 0xe3, 0xe2, 0xb8, 0xb5, 0x87, 0x2d, 0x9f, 0xfe, 0x13, 0xb9, 0x7e,
|
||||
0x87, 0xee, 0xfe, 0xf5, 0xd5, 0xc6, 0x03, 0xa4, 0x7d, 0xa4, 0x65, 0x7f, 0x9c, 0xab, 0x46, 0x92,
|
||||
0x96, 0xaa, 0xdb, 0xb4, 0xc0, 0x78, 0x88, 0x2e, 0xd6, 0x41, 0x5c, 0x50, 0x73, 0xa0, 0x0c, 0xb7,
|
||||
0x1b, 0x49, 0x3a, 0x41, 0x59, 0x3a, 0x38, 0x19, 0x3c, 0x83, 0x93, 0xe1, 0xd7, 0xef, 0x36, 0xf0,
|
||||
0x5f, 0x6d, 0x76, 0x18, 0x54, 0x3b, 0x0c, 0x36, 0x7b, 0x0c, 0xab, 0x3d, 0x86, 0xdf, 0x6a, 0x0c,
|
||||
0x7e, 0xd6, 0x18, 0x56, 0x35, 0x06, 0xbf, 0x6a, 0x0c, 0xde, 0x3f, 0x8a, 0x98, 0x58, 0x16, 0x73,
|
||||
0x37, 0x4c, 0x56, 0x5e, 0x5e, 0xf2, 0x50, 0x2c, 0x19, 0x8f, 0xce, 0xd0, 0x9f, 0x57, 0x30, 0xd7,
|
||||
0xd5, 0xbf, 0xbd, 0xf9, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x0d, 0x6f, 0xcc, 0x1a, 0x02, 0x00,
|
||||
0x00,
|
||||
// 514 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x53, 0x4f, 0x6b, 0xdb, 0x4e,
|
||||
0x10, 0x95, 0xfc, 0xef, 0x87, 0x95, 0x1f, 0x4d, 0x59, 0x0a, 0x15, 0x3e, 0x68, 0x8d, 0x70, 0x8b,
|
||||
0x0a, 0x45, 0x0e, 0x09, 0x94, 0x62, 0x0a, 0x05, 0x97, 0xa6, 0x94, 0xf6, 0x10, 0x94, 0xd0, 0x43,
|
||||
0x7b, 0x30, 0x6b, 0x77, 0x65, 0x2f, 0x91, 0x57, 0x42, 0xbb, 0x36, 0x51, 0x3f, 0x45, 0xe8, 0x27,
|
||||
0xe8, 0xc7, 0xf1, 0xcd, 0x3e, 0xf6, 0xb4, 0x10, 0xfb, 0xa6, 0xa3, 0x8e, 0xe9, 0xa5, 0xec, 0xae,
|
||||
0xa2, 0x9a, 0x94, 0xde, 0xe6, 0xcd, 0x7b, 0xf3, 0x66, 0x46, 0xb3, 0xb2, 0xbc, 0x88, 0x8c, 0xfb,
|
||||
0x93, 0x98, 0x86, 0x64, 0xda, 0x5f, 0xe2, 0x94, 0x91, 0x98, 0x12, 0x3a, 0xd5, 0x89, 0x45, 0x8a,
|
||||
0x38, 0x89, 0xa9, 0x9f, 0xa4, 0x31, 0x8f, 0x41, 0x4b, 0x27, 0x3b, 0x40, 0x56, 0x84, 0xac, 0xcf,
|
||||
0xb3, 0x04, 0x33, 0xcd, 0x75, 0xda, 0xf8, 0x8a, 0xeb, 0xd0, 0xfd, 0xd5, 0xb0, 0x1e, 0x7f, 0xaa,
|
||||
0x8c, 0xde, 0xec, 0x1b, 0x81, 0x57, 0x56, 0x43, 0x56, 0xd9, 0x66, 0xd7, 0xf4, 0xda, 0x43, 0x2f,
|
||||
0x17, 0x50, 0xe1, 0x42, 0xc0, 0xc3, 0xab, 0x79, 0x34, 0x70, 0x25, 0x78, 0x8e, 0x38, 0x4f, 0xdd,
|
||||
0x7c, 0xdd, 0x6b, 0x57, 0x28, 0x50, 0x2a, 0x70, 0x6d, 0x5a, 0x56, 0x82, 0x52, 0x34, 0xc7, 0x1c,
|
||||
0xa7, 0xcc, 0xae, 0x75, 0xeb, 0xde, 0xc1, 0x71, 0xdf, 0xd7, 0x63, 0xf9, 0xff, 0xe8, 0xe9, 0x9f,
|
||||
0x55, 0x15, 0x6f, 0x29, 0x4f, 0xb3, 0xe1, 0xeb, 0x95, 0x80, 0xc6, 0x56, 0xc0, 0x96, 0x22, 0x58,
|
||||
0x2e, 0x60, 0x4b, 0x99, 0xb2, 0x6a, 0x8a, 0xaa, 0x87, 0x5b, 0xac, 0x7b, 0x25, 0xf9, 0x7d, 0xd3,
|
||||
0x2b, 0x0b, 0x82, 0xbd, 0x19, 0xc0, 0x37, 0x0b, 0x4c, 0x22, 0x8c, 0xe8, 0x22, 0x19, 0x11, 0xca,
|
||||
0x71, 0xba, 0x44, 0xd1, 0x88, 0xd9, 0xf5, 0xae, 0xe9, 0x35, 0x87, 0x1f, 0x73, 0x01, 0x1f, 0x96,
|
||||
0xec, 0xfb, 0x92, 0x3c, 0x2f, 0x04, 0x7c, 0xa2, 0x9a, 0xdc, 0x27, 0xdc, 0xee, 0x57, 0x1c, 0xa2,
|
||||
0x45, 0xc4, 0x07, 0xee, 0xc9, 0x8b, 0xa3, 0x23, 0xf7, 0x56, 0xc0, 0x3a, 0xa1, 0xfc, 0x76, 0xdd,
|
||||
0x6b, 0x48, 0x1c, 0xfc, 0xe5, 0x04, 0xde, 0x59, 0xff, 0x85, 0x6c, 0x94, 0x20, 0x3e, 0xb3, 0x1b,
|
||||
0xea, 0x7b, 0xfa, 0x72, 0xab, 0xd3, 0xf3, 0x33, 0xc4, 0x67, 0x72, 0xab, 0x90, 0xc9, 0xa8, 0x10,
|
||||
0xf0, 0x7f, 0xd5, 0x50, 0x43, 0x57, 0x2e, 0xa2, 0x35, 0x41, 0xa9, 0x00, 0x5f, 0x94, 0x91, 0x3a,
|
||||
0x4c, 0xb3, 0x6b, 0x7a, 0x0f, 0x8e, 0x81, 0x1f, 0x32, 0xff, 0x94, 0x44, 0x98, 0x65, 0x8c, 0xe3,
|
||||
0xf9, 0x45, 0x96, 0xe0, 0x3b, 0x73, 0x19, 0x6b, 0xf3, 0x0b, 0x7d, 0xb8, 0x3b, 0x73, 0x09, 0x4b,
|
||||
0x73, 0x19, 0x06, 0xa5, 0xa2, 0x33, 0xb7, 0x0e, 0xef, 0x5d, 0x00, 0x3c, 0xb5, 0xea, 0x97, 0x38,
|
||||
0x2b, 0x1f, 0xc1, 0xa3, 0x5c, 0x40, 0x09, 0x0b, 0x01, 0xdb, 0xca, 0xea, 0x12, 0x67, 0x6e, 0x20,
|
||||
0x33, 0xc0, 0xb7, 0x9a, 0x4b, 0x14, 0x2d, 0xb0, 0x5d, 0x53, 0x4a, 0x3b, 0x17, 0x50, 0x27, 0x0a,
|
||||
0x01, 0x0f, 0x94, 0x56, 0x21, 0x37, 0xd0, 0xd9, 0x41, 0xed, 0xa5, 0x39, 0xfc, 0xb0, 0xba, 0x71,
|
||||
0x8c, 0xcd, 0x8d, 0x63, 0xac, 0xb6, 0x8e, 0xb9, 0xd9, 0x3a, 0xe6, 0xf5, 0xce, 0x31, 0x7e, 0xec,
|
||||
0x1c, 0x73, 0xb3, 0x73, 0x8c, 0x9f, 0x3b, 0xc7, 0xf8, 0xfc, 0x6c, 0x4a, 0xf8, 0x6c, 0x31, 0xf6,
|
||||
0x27, 0xf1, 0xbc, 0xcf, 0x32, 0x3a, 0xe1, 0x33, 0x42, 0xa7, 0x7b, 0xd1, 0x9f, 0xff, 0x61, 0xdc,
|
||||
0x52, 0x2f, 0xfa, 0xe4, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x04, 0xd0, 0xd5, 0x24, 0x03,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *VersioningConfiguration) Marshal() (dAtA []byte, err error) {
|
||||
@@ -121,6 +132,18 @@ func (m *VersioningConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.FSType != 0 {
|
||||
i = encodeVarintVersioningconfiguration(dAtA, i, uint64(m.FSType))
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if len(m.FSPath) > 0 {
|
||||
i -= len(m.FSPath)
|
||||
copy(dAtA[i:], m.FSPath)
|
||||
i = encodeVarintVersioningconfiguration(dAtA, i, uint64(len(m.FSPath)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if m.CleanupIntervalS != 0 {
|
||||
i = encodeVarintVersioningconfiguration(dAtA, i, uint64(m.CleanupIntervalS))
|
||||
i--
|
||||
@@ -187,6 +210,13 @@ func (m *VersioningConfiguration) ProtoSize() (n int) {
|
||||
if m.CleanupIntervalS != 0 {
|
||||
n += 1 + sovVersioningconfiguration(uint64(m.CleanupIntervalS))
|
||||
}
|
||||
l = len(m.FSPath)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersioningconfiguration(uint64(l))
|
||||
}
|
||||
if m.FSType != 0 {
|
||||
n += 1 + sovVersioningconfiguration(uint64(m.FSType))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -403,6 +433,57 @@ func (m *VersioningConfiguration) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field FSPath", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersioningconfiguration
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthVersioningconfiguration
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthVersioningconfiguration
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.FSPath = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field FSType", wireType)
|
||||
}
|
||||
m.FSType = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersioningconfiguration
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.FSType |= fs.FilesystemType(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipVersioningconfiguration(dAtA[iNdEx:])
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
@@ -16,6 +20,13 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
const (
|
||||
maxModifications = 1000
|
||||
minSaveInterval = 5 * time.Second
|
||||
)
|
||||
|
||||
var errTooManyModifications = errors.New("too many concurrent config modifications")
|
||||
|
||||
// The Committer interface is implemented by objects that need to know about
|
||||
// or have a say in configuration changes.
|
||||
//
|
||||
@@ -35,6 +46,10 @@ import (
|
||||
// false will result in a "restart needed" response to the API/user. Note that
|
||||
// the new configuration will still have been applied by those who were
|
||||
// capable of doing so.
|
||||
//
|
||||
// A Committer must take care not to hold any locks while changing the
|
||||
// configuration (e.g. calling Wrapper.SetFolder), that are also acquired in any
|
||||
// methods of the Committer interface.
|
||||
type Committer interface {
|
||||
VerifyConfiguration(from, to Configuration) error
|
||||
CommitConfiguration(from, to Configuration) (handled bool)
|
||||
@@ -50,45 +65,49 @@ type noopWaiter struct{}
|
||||
|
||||
func (noopWaiter) Wait() {}
|
||||
|
||||
// A Wrapper around a Configuration that manages loads, saves and published
|
||||
// notifications of changes to registered Handlers
|
||||
// ModifyFunction gets a pointer to a copy of the currently active configuration
|
||||
// for modification.
|
||||
type ModifyFunction func(*Configuration)
|
||||
|
||||
// Wrapper handles a Configuration, i.e. it provides methods to access, change
|
||||
// and save the config, and notifies registered subscribers (Committer) of
|
||||
// changes.
|
||||
//
|
||||
// Modify allows changing the currently active configuration through the given
|
||||
// ModifyFunction. It can be called concurrently: All calls will be queued and
|
||||
// called in order.
|
||||
type Wrapper interface {
|
||||
ConfigPath() string
|
||||
MyID() protocol.DeviceID
|
||||
|
||||
RawCopy() Configuration
|
||||
Replace(cfg Configuration) (Waiter, error)
|
||||
RequiresRestart() bool
|
||||
Save() error
|
||||
|
||||
GUI() GUIConfiguration
|
||||
SetGUI(gui GUIConfiguration) (Waiter, error)
|
||||
LDAP() LDAPConfiguration
|
||||
SetLDAP(ldap LDAPConfiguration) (Waiter, error)
|
||||
Modify(ModifyFunction) (Waiter, error)
|
||||
RemoveFolder(id string) (Waiter, error)
|
||||
RemoveDevice(id protocol.DeviceID) (Waiter, error)
|
||||
|
||||
GUI() GUIConfiguration
|
||||
LDAP() LDAPConfiguration
|
||||
Options() OptionsConfiguration
|
||||
SetOptions(opts OptionsConfiguration) (Waiter, error)
|
||||
|
||||
Folder(id string) (FolderConfiguration, bool)
|
||||
Folders() map[string]FolderConfiguration
|
||||
FolderList() []FolderConfiguration
|
||||
RemoveFolder(id string) (Waiter, error)
|
||||
SetFolder(fld FolderConfiguration) (Waiter, error)
|
||||
SetFolders(folders []FolderConfiguration) (Waiter, error)
|
||||
FolderPasswords(device protocol.DeviceID) map[string]string
|
||||
DefaultFolder() FolderConfiguration
|
||||
|
||||
Device(id protocol.DeviceID) (DeviceConfiguration, bool)
|
||||
Devices() map[protocol.DeviceID]DeviceConfiguration
|
||||
DeviceList() []DeviceConfiguration
|
||||
RemoveDevice(id protocol.DeviceID) (Waiter, error)
|
||||
SetDevice(DeviceConfiguration) (Waiter, error)
|
||||
SetDevices([]DeviceConfiguration) (Waiter, error)
|
||||
DefaultDevice() DeviceConfiguration
|
||||
|
||||
IgnoredDevices() []ObservedDevice
|
||||
IgnoredDevice(id protocol.DeviceID) bool
|
||||
IgnoredFolder(device protocol.DeviceID, folder string) bool
|
||||
|
||||
Subscribe(c Committer)
|
||||
Subscribe(c Committer) Configuration
|
||||
Unsubscribe(c Committer)
|
||||
}
|
||||
|
||||
@@ -97,6 +116,7 @@ type wrapper struct {
|
||||
path string
|
||||
evLogger events.Logger
|
||||
myID protocol.DeviceID
|
||||
queue chan modifyEntry
|
||||
|
||||
waiter Waiter // Latest ongoing config change
|
||||
subs []Committer
|
||||
@@ -107,12 +127,15 @@ type wrapper struct {
|
||||
|
||||
// Wrap wraps an existing Configuration structure and ties it to a file on
|
||||
// disk.
|
||||
// The returned Wrapper is a suture.Service, thus needs to be started (added to
|
||||
// a supervisor).
|
||||
func Wrap(path string, cfg Configuration, myID protocol.DeviceID, evLogger events.Logger) Wrapper {
|
||||
w := &wrapper{
|
||||
cfg: cfg,
|
||||
path: path,
|
||||
evLogger: evLogger,
|
||||
myID: myID,
|
||||
queue: make(chan modifyEntry, maxModifications),
|
||||
waiter: noopWaiter{}, // Noop until first config change
|
||||
mut: sync.NewMutex(),
|
||||
}
|
||||
@@ -121,6 +144,8 @@ func Wrap(path string, cfg Configuration, myID protocol.DeviceID, evLogger event
|
||||
|
||||
// Load loads an existing file on disk and returns a new configuration
|
||||
// wrapper.
|
||||
// The returned Wrapper is a suture.Service, thus needs to be started (added to
|
||||
// a supervisor).
|
||||
func Load(path string, myID protocol.DeviceID, evLogger events.Logger) (Wrapper, int, error) {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
@@ -145,11 +170,13 @@ func (w *wrapper) MyID() protocol.DeviceID {
|
||||
}
|
||||
|
||||
// Subscribe registers the given handler to be called on any future
|
||||
// configuration changes.
|
||||
func (w *wrapper) Subscribe(c Committer) {
|
||||
// configuration changes. It returns the config that is in effect while
|
||||
// subscribing, that can be used for initial setup.
|
||||
func (w *wrapper) Subscribe(c Committer) Configuration {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
w.subs = append(w.subs, c)
|
||||
w.mut.Unlock()
|
||||
return w.cfg.Copy()
|
||||
}
|
||||
|
||||
// Unsubscribe de-registers the given handler from any future calls to
|
||||
@@ -179,11 +206,84 @@ func (w *wrapper) RawCopy() Configuration {
|
||||
return w.cfg.Copy()
|
||||
}
|
||||
|
||||
// Replace swaps the current configuration object for the given one.
|
||||
func (w *wrapper) Replace(cfg Configuration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
return w.replaceLocked(cfg.Copy())
|
||||
func (w *wrapper) Modify(fn ModifyFunction) (Waiter, error) {
|
||||
return w.modifyQueued(fn)
|
||||
}
|
||||
|
||||
func (w *wrapper) modifyQueued(modifyFunc ModifyFunction) (Waiter, error) {
|
||||
e := modifyEntry{
|
||||
modifyFunc: modifyFunc,
|
||||
res: make(chan modifyResult),
|
||||
}
|
||||
select {
|
||||
case w.queue <- e:
|
||||
default:
|
||||
return noopWaiter{}, errTooManyModifications
|
||||
}
|
||||
res := <-e.res
|
||||
return res.w, res.err
|
||||
}
|
||||
|
||||
func (w *wrapper) Serve(ctx context.Context) error {
|
||||
defer w.serveSave()
|
||||
|
||||
var e modifyEntry
|
||||
saveTimer := time.NewTimer(0)
|
||||
<-saveTimer.C
|
||||
saveTimerRunning := false
|
||||
for {
|
||||
select {
|
||||
case e = <-w.queue:
|
||||
case <-saveTimer.C:
|
||||
w.serveSave()
|
||||
saveTimerRunning = false
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
var waiter Waiter = noopWaiter{}
|
||||
var err error
|
||||
|
||||
// Let the caller modify the config.
|
||||
to := w.RawCopy()
|
||||
e.modifyFunc(&to)
|
||||
|
||||
// Check if the config was actually changed at all.
|
||||
w.mut.Lock()
|
||||
if !reflect.DeepEqual(w.cfg, to) {
|
||||
waiter, err = w.replaceLocked(to)
|
||||
if !saveTimerRunning {
|
||||
saveTimer.Reset(minSaveInterval)
|
||||
saveTimerRunning = true
|
||||
}
|
||||
}
|
||||
w.mut.Unlock()
|
||||
|
||||
e.res <- modifyResult{
|
||||
w: waiter,
|
||||
err: err,
|
||||
}
|
||||
|
||||
// Wait for all subscriber to handle the config change before continuing
|
||||
// to process the next change.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
waiter.Wait()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapper) serveSave() {
|
||||
if err := w.Save(); err != nil {
|
||||
l.Warnln("Failed to save config:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) {
|
||||
@@ -246,55 +346,22 @@ func (w *wrapper) DeviceList() []DeviceConfiguration {
|
||||
return w.cfg.Copy().Devices
|
||||
}
|
||||
|
||||
// SetDevices adds new devices to the configuration, or overwrites existing
|
||||
// devices with the same ID.
|
||||
func (w *wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
newCfg := w.cfg.Copy()
|
||||
var replaced bool
|
||||
for oldIndex := range devs {
|
||||
replaced = false
|
||||
for newIndex := range newCfg.Devices {
|
||||
if newCfg.Devices[newIndex].DeviceID == devs[oldIndex].DeviceID {
|
||||
newCfg.Devices[newIndex] = devs[oldIndex].Copy()
|
||||
replaced = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
newCfg.Devices = append(newCfg.Devices, devs[oldIndex].Copy())
|
||||
}
|
||||
}
|
||||
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// SetDevice adds a new device to the configuration, or overwrites an existing
|
||||
// device with the same ID.
|
||||
func (w *wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) {
|
||||
return w.SetDevices([]DeviceConfiguration{dev})
|
||||
}
|
||||
|
||||
// RemoveDevice removes the device from the configuration
|
||||
func (w *wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
newCfg := w.cfg.Copy()
|
||||
for i := range newCfg.Devices {
|
||||
if newCfg.Devices[i].DeviceID == id {
|
||||
newCfg.Devices = append(newCfg.Devices[:i], newCfg.Devices[i+1:]...)
|
||||
return w.replaceLocked(newCfg)
|
||||
return w.modifyQueued(func(cfg *Configuration) {
|
||||
if _, i, ok := cfg.Device(id); ok {
|
||||
cfg.Devices = append(cfg.Devices[:i], cfg.Devices[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return noopWaiter{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Folders returns a map of folders. Folder structures should not be changed,
|
||||
// other than for the purpose of updating via SetFolder().
|
||||
func (w *wrapper) DefaultDevice() DeviceConfiguration {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
return w.cfg.Defaults.Device.Copy()
|
||||
}
|
||||
|
||||
// Folders returns a map of folders.
|
||||
func (w *wrapper) Folders() map[string]FolderConfiguration {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
@@ -312,51 +379,13 @@ func (w *wrapper) FolderList() []FolderConfiguration {
|
||||
return w.cfg.Copy().Folders
|
||||
}
|
||||
|
||||
// SetFolder adds a new folder to the configuration, or overwrites an existing
|
||||
// folder with the same ID.
|
||||
func (w *wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
|
||||
return w.SetFolders([]FolderConfiguration{fld})
|
||||
}
|
||||
|
||||
// SetFolders adds new folders to the configuration, or overwrites existing
|
||||
// folders with the same ID.
|
||||
func (w *wrapper) SetFolders(folders []FolderConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
newCfg := w.cfg.Copy()
|
||||
|
||||
inds := make(map[string]int, len(w.cfg.Folders))
|
||||
for i, folder := range newCfg.Folders {
|
||||
inds[folder.ID] = i
|
||||
}
|
||||
filtered := folders[:0]
|
||||
for _, folder := range folders {
|
||||
if i, ok := inds[folder.ID]; ok {
|
||||
newCfg.Folders[i] = folder
|
||||
} else {
|
||||
filtered = append(filtered, folder)
|
||||
}
|
||||
}
|
||||
newCfg.Folders = append(newCfg.Folders, filtered...)
|
||||
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// RemoveFolder removes the folder from the configuration
|
||||
func (w *wrapper) RemoveFolder(id string) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
newCfg := w.cfg.Copy()
|
||||
for i := range newCfg.Folders {
|
||||
if newCfg.Folders[i].ID == id {
|
||||
newCfg.Folders = append(newCfg.Folders[:i], newCfg.Folders[i+1:]...)
|
||||
return w.replaceLocked(newCfg)
|
||||
return w.modifyQueued(func(cfg *Configuration) {
|
||||
if _, i, ok := cfg.Folder(id); ok {
|
||||
cfg.Folders = append(cfg.Folders[:i], cfg.Folders[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return noopWaiter{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// FolderPasswords returns the folder passwords set for this device, for
|
||||
@@ -367,6 +396,12 @@ func (w *wrapper) FolderPasswords(device protocol.DeviceID) map[string]string {
|
||||
return w.cfg.FolderPasswords(device)
|
||||
}
|
||||
|
||||
func (w *wrapper) DefaultFolder() FolderConfiguration {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
return w.cfg.Defaults.Folder.Copy()
|
||||
}
|
||||
|
||||
// Options returns the current options configuration object.
|
||||
func (w *wrapper) Options() OptionsConfiguration {
|
||||
w.mut.Lock()
|
||||
@@ -374,29 +409,12 @@ func (w *wrapper) Options() OptionsConfiguration {
|
||||
return w.cfg.Options.Copy()
|
||||
}
|
||||
|
||||
// SetOptions replaces the current options configuration object.
|
||||
func (w *wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
newCfg := w.cfg.Copy()
|
||||
newCfg.Options = opts.Copy()
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
func (w *wrapper) LDAP() LDAPConfiguration {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
return w.cfg.LDAP.Copy()
|
||||
}
|
||||
|
||||
func (w *wrapper) SetLDAP(ldap LDAPConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
newCfg := w.cfg.Copy()
|
||||
newCfg.LDAP = ldap.Copy()
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// GUI returns the current GUI configuration object.
|
||||
func (w *wrapper) GUI() GUIConfiguration {
|
||||
w.mut.Lock()
|
||||
@@ -404,15 +422,6 @@ func (w *wrapper) GUI() GUIConfiguration {
|
||||
return w.cfg.GUI.Copy()
|
||||
}
|
||||
|
||||
// SetGUI replaces the current GUI configuration object.
|
||||
func (w *wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
newCfg := w.cfg.Copy()
|
||||
newCfg.GUI = gui.Copy()
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// IgnoredDevice returns whether or not connection attempts from the given
|
||||
// device should be silently ignored.
|
||||
func (w *wrapper) IgnoredDevice(id protocol.DeviceID) bool {
|
||||
@@ -449,24 +458,22 @@ func (w *wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool {
|
||||
func (w *wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
for _, device := range w.cfg.Devices {
|
||||
if device.DeviceID == id {
|
||||
return device.Copy(), true
|
||||
}
|
||||
device, _, ok := w.cfg.Device(id)
|
||||
if !ok {
|
||||
return DeviceConfiguration{}, false
|
||||
}
|
||||
return DeviceConfiguration{}, false
|
||||
return device.Copy(), ok
|
||||
}
|
||||
|
||||
// Folder returns the configuration for the given folder and an "ok" bool.
|
||||
func (w *wrapper) Folder(id string) (FolderConfiguration, bool) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
for _, folder := range w.cfg.Folders {
|
||||
if folder.ID == id {
|
||||
return folder.Copy(), true
|
||||
}
|
||||
fcfg, _, ok := w.cfg.Folder(id)
|
||||
if !ok {
|
||||
return FolderConfiguration{}, false
|
||||
}
|
||||
return FolderConfiguration{}, false
|
||||
return fcfg.Copy(), ok
|
||||
}
|
||||
|
||||
// Save writes the configuration to disk, and generates a ConfigSaved event.
|
||||
@@ -502,3 +509,13 @@ func (w *wrapper) RequiresRestart() bool {
|
||||
func (w *wrapper) setRequiresRestart() {
|
||||
atomic.StoreUint32(&w.requiresRestart, 1)
|
||||
}
|
||||
|
||||
type modifyEntry struct {
|
||||
modifyFunc ModifyFunction
|
||||
res chan modifyResult
|
||||
}
|
||||
|
||||
type modifyResult struct {
|
||||
w Waiter
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package connections
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"io"
|
||||
"math/rand"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/thejerf/suture/v4"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
@@ -29,24 +31,41 @@ func init() {
|
||||
device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
|
||||
}
|
||||
|
||||
func initConfig() config.Wrapper {
|
||||
cfg := config.Wrap("/dev/null", config.New(device1), device1, events.NoopLogger)
|
||||
dev1Conf = config.NewDeviceConfiguration(device1, "device1")
|
||||
dev2Conf = config.NewDeviceConfiguration(device2, "device2")
|
||||
dev3Conf = config.NewDeviceConfiguration(device3, "device3")
|
||||
dev4Conf = config.NewDeviceConfiguration(device4, "device4")
|
||||
func newDeviceConfiguration(w config.Wrapper, id protocol.DeviceID, name string) config.DeviceConfiguration {
|
||||
cfg := w.DefaultDevice()
|
||||
cfg.DeviceID = id
|
||||
cfg.Name = name
|
||||
return cfg
|
||||
}
|
||||
|
||||
func initConfig() (config.Wrapper, context.CancelFunc) {
|
||||
wrapper := config.Wrap("/dev/null", config.New(device1), device1, events.NoopLogger)
|
||||
dev1Conf = newDeviceConfiguration(wrapper, device1, "device1")
|
||||
dev2Conf = newDeviceConfiguration(wrapper, device2, "device2")
|
||||
dev3Conf = newDeviceConfiguration(wrapper, device3, "device3")
|
||||
dev4Conf = newDeviceConfiguration(wrapper, device4, "device4")
|
||||
|
||||
var cancel context.CancelFunc = func() {}
|
||||
if wrapperService, ok := wrapper.(suture.Service); ok {
|
||||
var ctx context.Context
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
go wrapperService.Serve(ctx)
|
||||
}
|
||||
|
||||
dev2Conf.MaxRecvKbps = rand.Int() % 100000
|
||||
dev2Conf.MaxSendKbps = rand.Int() % 100000
|
||||
|
||||
waiter, _ := cfg.SetDevices([]config.DeviceConfiguration{dev1Conf, dev2Conf, dev3Conf, dev4Conf})
|
||||
waiter, _ := wrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevices([]config.DeviceConfiguration{dev1Conf, dev2Conf, dev3Conf, dev4Conf})
|
||||
})
|
||||
waiter.Wait()
|
||||
return cfg
|
||||
return wrapper, cancel
|
||||
}
|
||||
|
||||
func TestLimiterInit(t *testing.T) {
|
||||
cfg := initConfig()
|
||||
lim := newLimiter(device1, cfg)
|
||||
wrapper, wrapperCancel := initConfig()
|
||||
defer wrapperCancel()
|
||||
lim := newLimiter(device1, wrapper)
|
||||
|
||||
device2ReadLimit := dev2Conf.MaxRecvKbps
|
||||
device2WriteLimit := dev2Conf.MaxSendKbps
|
||||
@@ -70,8 +89,9 @@ func TestLimiterInit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetDeviceLimits(t *testing.T) {
|
||||
cfg := initConfig()
|
||||
lim := newLimiter(device1, cfg)
|
||||
wrapper, wrapperCancel := initConfig()
|
||||
defer wrapperCancel()
|
||||
lim := newLimiter(device1, wrapper)
|
||||
|
||||
// should still be inf/inf because this is local device
|
||||
dev1ReadLimit := rand.Int() % 100000
|
||||
@@ -87,7 +107,9 @@ func TestSetDeviceLimits(t *testing.T) {
|
||||
dev3ReadLimit := rand.Int() % 10000
|
||||
dev3Conf.MaxRecvKbps = dev3ReadLimit
|
||||
|
||||
waiter, _ := cfg.SetDevices([]config.DeviceConfiguration{dev1Conf, dev2Conf, dev3Conf, dev4Conf})
|
||||
waiter, _ := wrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevices([]config.DeviceConfiguration{dev1Conf, dev2Conf, dev3Conf, dev4Conf})
|
||||
})
|
||||
waiter.Wait()
|
||||
|
||||
expectedR := map[protocol.DeviceID]*rate.Limiter{
|
||||
@@ -108,10 +130,11 @@ func TestSetDeviceLimits(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveDevice(t *testing.T) {
|
||||
cfg := initConfig()
|
||||
lim := newLimiter(device1, cfg)
|
||||
wrapper, wrapperCancel := initConfig()
|
||||
defer wrapperCancel()
|
||||
lim := newLimiter(device1, wrapper)
|
||||
|
||||
waiter, _ := cfg.RemoveDevice(device3)
|
||||
waiter, _ := wrapper.RemoveDevice(device3)
|
||||
waiter.Wait()
|
||||
expectedR := map[protocol.DeviceID]*rate.Limiter{
|
||||
device2: rate.NewLimiter(rate.Limit(dev2Conf.MaxRecvKbps*1024), limiterBurstSize),
|
||||
@@ -128,15 +151,18 @@ func TestRemoveDevice(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddDevice(t *testing.T) {
|
||||
cfg := initConfig()
|
||||
lim := newLimiter(device1, cfg)
|
||||
wrapper, wrapperCancel := initConfig()
|
||||
defer wrapperCancel()
|
||||
lim := newLimiter(device1, wrapper)
|
||||
|
||||
addedDevice, _ := protocol.DeviceIDFromString("XZJ4UNS-ENI7QGJ-J45DT6G-QSGML2K-6I4XVOG-NAZ7BF5-2VAOWNT-TFDOMQU")
|
||||
addDevConf := config.NewDeviceConfiguration(addedDevice, "addedDevice")
|
||||
addDevConf := newDeviceConfiguration(wrapper, addedDevice, "addedDevice")
|
||||
addDevConf.MaxRecvKbps = 120
|
||||
addDevConf.MaxSendKbps = 240
|
||||
|
||||
waiter, _ := cfg.SetDevice(addDevConf)
|
||||
waiter, _ := wrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevice(addDevConf)
|
||||
})
|
||||
waiter.Wait()
|
||||
|
||||
expectedR := map[protocol.DeviceID]*rate.Limiter{
|
||||
@@ -159,17 +185,20 @@ func TestAddDevice(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddAndRemove(t *testing.T) {
|
||||
cfg := initConfig()
|
||||
lim := newLimiter(device1, cfg)
|
||||
wrapper, wrapperCancel := initConfig()
|
||||
defer wrapperCancel()
|
||||
lim := newLimiter(device1, wrapper)
|
||||
|
||||
addedDevice, _ := protocol.DeviceIDFromString("XZJ4UNS-ENI7QGJ-J45DT6G-QSGML2K-6I4XVOG-NAZ7BF5-2VAOWNT-TFDOMQU")
|
||||
addDevConf := config.NewDeviceConfiguration(addedDevice, "addedDevice")
|
||||
addDevConf := newDeviceConfiguration(wrapper, addedDevice, "addedDevice")
|
||||
addDevConf.MaxRecvKbps = 120
|
||||
addDevConf.MaxSendKbps = 240
|
||||
|
||||
waiter, _ := cfg.SetDevice(addDevConf)
|
||||
waiter, _ := wrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevice(addDevConf)
|
||||
})
|
||||
waiter.Wait()
|
||||
waiter, _ = cfg.RemoveDevice(device3)
|
||||
waiter, _ = wrapper.RemoveDevice(device3)
|
||||
waiter.Wait()
|
||||
|
||||
expectedR := map[protocol.DeviceID]*rate.Limiter{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.14,!noquic,!go1.16
|
||||
// +build go1.14,!noquic,!go1.17
|
||||
|
||||
package connections
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.14,!noquic,!go1.16
|
||||
// +build go1.14,!noquic,!go1.17
|
||||
|
||||
package connections
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.14,!noquic,!go1.16
|
||||
// +build go1.14,!noquic,!go1.17
|
||||
|
||||
package connections
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build noquic !go1.14 go1.16
|
||||
// +build noquic !go1.14 go1.17
|
||||
|
||||
package connections
|
||||
|
||||
|
||||
@@ -513,8 +513,10 @@ func TestCheckGlobals(t *testing.T) {
|
||||
}
|
||||
|
||||
// Clean up global entry of the now missing file
|
||||
if err := db.checkGlobals([]byte(fs.folder)); err != nil {
|
||||
if repaired, err := db.checkGlobals([]byte(fs.folder)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if repaired != 1 {
|
||||
t.Error("Expected 1 repaired global item, got", repaired)
|
||||
}
|
||||
|
||||
// Check that the global entry is gone
|
||||
@@ -949,6 +951,49 @@ func TestDuplicateNeedCount(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeedAfterDropGlobal(t *testing.T) {
|
||||
db := newLowlevelMemory(t)
|
||||
defer db.Close()
|
||||
|
||||
folder := "test"
|
||||
testFs := fs.NewFilesystem(fs.FilesystemTypeFake, "")
|
||||
|
||||
fs := newFileSet(t, folder, testFs, db)
|
||||
|
||||
// Initial:
|
||||
// Three devices and a file "test": local has Version 1, remoteDevice0
|
||||
// Version 2 and remoteDevice2 doesn't have it.
|
||||
// All of them have "bar", just so the db knows about remoteDevice2.
|
||||
files := []protocol.FileInfo{
|
||||
{Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1},
|
||||
{Name: "bar", Version: protocol.Vector{}.Update(myID), Sequence: 2},
|
||||
}
|
||||
fs.Update(protocol.LocalDeviceID, files)
|
||||
files[0].Version = files[0].Version.Update(myID)
|
||||
fs.Update(remoteDevice0, files)
|
||||
fs.Update(remoteDevice1, files[1:])
|
||||
|
||||
// remoteDevice1 needs one file: test
|
||||
snap := fs.Snapshot()
|
||||
c := snap.NeedSize(remoteDevice1)
|
||||
if c.Files != 1 {
|
||||
t.Errorf("Expected 1 needed files initially, got %v", c.Files)
|
||||
}
|
||||
snap.Release()
|
||||
|
||||
// Drop remoteDevice0, i.e. remove all their files from db.
|
||||
// That changes the global file, which is now what local has.
|
||||
fs.Drop(remoteDevice0)
|
||||
|
||||
// remoteDevice1 still needs test.
|
||||
snap = fs.Snapshot()
|
||||
c = snap.NeedSize(remoteDevice1)
|
||||
if c.Files != 1 {
|
||||
t.Errorf("Expected still 1 needed files, got %v", c.Files)
|
||||
}
|
||||
snap.Release()
|
||||
}
|
||||
|
||||
func numBlockLists(db *Lowlevel) (int, error) {
|
||||
it, err := db.Backend.NewPrefixIterator([]byte{KeyTypeBlockList})
|
||||
if err != nil {
|
||||
|
||||
@@ -441,30 +441,31 @@ func (db *Lowlevel) dropDeviceFolder(device, folder []byte, meta *metadataTracke
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
func (db *Lowlevel) checkGlobals(folder []byte) error {
|
||||
func (db *Lowlevel) checkGlobals(folder []byte) (int, error) {
|
||||
t, err := db.newReadWriteTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
defer t.close()
|
||||
|
||||
key, err := db.keyer.GenerateGlobalVersionKey(nil, folder, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
dbi, err := t.NewPrefixIterator(key.WithoutName())
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
defer dbi.Release()
|
||||
|
||||
fixed := 0
|
||||
var dk []byte
|
||||
ro := t.readOnlyTransaction
|
||||
for dbi.Next() {
|
||||
var vl VersionList
|
||||
if err := vl.Unmarshal(dbi.Value()); err != nil || vl.Empty() {
|
||||
if err := t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -480,34 +481,36 @@ func (db *Lowlevel) checkGlobals(folder []byte) error {
|
||||
for _, fv := range vl.RawVersions {
|
||||
changedHere, err = checkGlobalsFilterDevices(dk, folder, name, fv.Devices, newVL, ro)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
changed = changed || changedHere
|
||||
|
||||
changedHere, err = checkGlobalsFilterDevices(dk, folder, name, fv.InvalidDevices, newVL, ro)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
changed = changed || changedHere
|
||||
}
|
||||
|
||||
if newVL.Empty() {
|
||||
if err := t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
fixed++
|
||||
} else if changed {
|
||||
if err := t.Put(dbi.Key(), mustMarshal(newVL)); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
fixed++
|
||||
}
|
||||
}
|
||||
dbi.Release()
|
||||
if err := dbi.Error(); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
l.Debugf("db check completed for %q", folder)
|
||||
return t.Commit()
|
||||
l.Debugf("global db check completed for %q", folder)
|
||||
return fixed, t.Commit()
|
||||
}
|
||||
|
||||
func checkGlobalsFilterDevices(dk, folder, name []byte, devices [][]byte, vl *VersionList, t readOnlyTransaction) (bool, error) {
|
||||
@@ -899,8 +902,10 @@ func (db *Lowlevel) recalcMeta(folderStr string) (*metadataTracker, error) {
|
||||
folder := []byte(folderStr)
|
||||
|
||||
meta := newMetadataTracker(db.keyer, db.evLogger)
|
||||
if err := db.checkGlobals(folder); err != nil {
|
||||
if fixed, err := db.checkGlobals(folder); err != nil {
|
||||
return nil, fmt.Errorf("checking globals: %w", err)
|
||||
} else if fixed > 0 {
|
||||
l.Infof("Repaired %d global entries for folder %v in database", fixed, folderStr)
|
||||
}
|
||||
|
||||
t, err := db.newReadWriteTransaction(meta.CommitHook(folder))
|
||||
@@ -1197,7 +1202,7 @@ func unchanged(nf, ef protocol.FileIntf) bool {
|
||||
func (db *Lowlevel) handleFailure(err error) {
|
||||
db.checkErrorForRepair(err)
|
||||
if shouldReportFailure(err) {
|
||||
db.evLogger.Log(events.Failure, err)
|
||||
db.evLogger.Log(events.Failure, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,42 @@ func (db *Lowlevel) RemovePendingFolder(id string) {
|
||||
}
|
||||
}
|
||||
|
||||
// RemovePendingFoldersBeforeTime removes entries for a specific device which are older
|
||||
// than a given timestamp or invalid. It returns only the valid removed folder IDs.
|
||||
func (db *Lowlevel) RemovePendingFoldersBeforeTime(device protocol.DeviceID, oldest time.Time) ([]string, error) {
|
||||
prefixKey, err := db.keyer.GeneratePendingFolderKey(nil, device[:], nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iter, err := db.NewPrefixIterator(prefixKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer iter.Release()
|
||||
oldest = oldest.Round(time.Second)
|
||||
var res []string
|
||||
for iter.Next() {
|
||||
var of ObservedFolder
|
||||
var folderID string
|
||||
if err = of.Unmarshal(iter.Value()); err != nil {
|
||||
l.Infof("Invalid pending folder entry, deleting from database: %x", iter.Key())
|
||||
} else if of.Time.Before(oldest) {
|
||||
folderID = string(db.keyer.FolderFromPendingFolderKey(iter.Key()))
|
||||
l.Infof("Removing stale pending folder %s (%s) from device %s, last seen %v",
|
||||
folderID, of.Label, device.Short(), of.Time)
|
||||
} else {
|
||||
// Keep entries younger or equal to the given timestamp
|
||||
continue
|
||||
}
|
||||
if err := db.Delete(iter.Key()); err != nil {
|
||||
l.Warnf("Failed to remove pending folder entry: %v", err)
|
||||
} else if len(folderID) > 0 {
|
||||
res = append(res, folderID)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Consolidated information about a pending folder
|
||||
type PendingFolder struct {
|
||||
OfferedBy map[protocol.DeviceID]ObservedFolder `json:"offeredBy"`
|
||||
|
||||
@@ -748,7 +748,7 @@ func (db *schemaUpdater) updateSchemaTo14(_ int) error {
|
||||
continue
|
||||
}
|
||||
|
||||
fi.SetMustRescan(protocol.LocalDeviceID.Short())
|
||||
fi.SetMustRescan()
|
||||
if err = t.putFile(it.Key(), fi); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1354,7 +1354,7 @@ func TestNeedAfterUnignore(t *testing.T) {
|
||||
|
||||
// Initial state: Devices in sync, locally ignored
|
||||
local := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10}
|
||||
local.SetIgnored(myID)
|
||||
local.SetIgnored()
|
||||
remote := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10}
|
||||
s.Update(protocol.LocalDeviceID, fileList{local})
|
||||
s.Update(remoteDevice0, fileList{remote})
|
||||
|
||||
@@ -125,9 +125,9 @@ func (f FileInfoTruncated) FileModifiedBy() protocol.ShortID {
|
||||
return f.ModifiedBy
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) ConvertToIgnoredFileInfo(by protocol.ShortID) protocol.FileInfo {
|
||||
func (f FileInfoTruncated) ConvertToIgnoredFileInfo() protocol.FileInfo {
|
||||
file := f.copyToFileInfo()
|
||||
file.SetIgnored(by)
|
||||
file.SetIgnored()
|
||||
return file
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
github_com_gogo_protobuf_types "github.com/gogo/protobuf/types"
|
||||
_ "github.com/golang/protobuf/ptypes/timestamp"
|
||||
github_com_syncthing_syncthing_lib_protocol "github.com/syncthing/syncthing/lib/protocol"
|
||||
protocol "github.com/syncthing/syncthing/lib/protocol"
|
||||
_ "github.com/syncthing/syncthing/proto/ext"
|
||||
_ "google.golang.org/protobuf/types/known/timestamppb"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
|
||||
@@ -817,11 +817,11 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
|
||||
}
|
||||
|
||||
var global protocol.FileIntf
|
||||
var gotGlobal, ok bool
|
||||
var gotGlobal bool
|
||||
|
||||
globalFV, ok := fl.GetGlobal()
|
||||
globalFV, haveGlobal := fl.GetGlobal()
|
||||
// Add potential needs of the removed device
|
||||
if ok && !globalFV.IsInvalid() && Need(globalFV, false, protocol.Vector{}) && !Need(oldGlobalFV, haveRemoved, removedFV.Version) {
|
||||
if haveGlobal && !globalFV.IsInvalid() && Need(globalFV, false, protocol.Vector{}) && !Need(oldGlobalFV, haveRemoved, removedFV.Version) {
|
||||
keyBuf, global, _, err = t.getGlobalFromVersionList(keyBuf, folder, file, true, fl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -844,16 +844,23 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
|
||||
return keyBuf, nil
|
||||
}
|
||||
|
||||
var f protocol.FileIntf
|
||||
keyBuf, f, err = t.getGlobalFromFileVersion(keyBuf, folder, file, true, oldGlobalFV)
|
||||
var oldGlobal protocol.FileIntf
|
||||
keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, file, true, oldGlobalFV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.removeFile(protocol.GlobalDeviceID, f)
|
||||
meta.removeFile(protocol.GlobalDeviceID, oldGlobal)
|
||||
|
||||
// Remove potential device needs
|
||||
if fv, have := fl.Get(protocol.LocalDeviceID[:]); Need(removedFV, have, fv.Version) {
|
||||
meta.removeNeeded(protocol.LocalDeviceID, f)
|
||||
shouldRemoveNeed := func(dev protocol.DeviceID) bool {
|
||||
fv, have := fl.Get(dev[:])
|
||||
if !Need(oldGlobalFV, have, fv.Version) {
|
||||
return false // Didn't need it before
|
||||
}
|
||||
return !haveGlobal || !Need(globalFV, have, fv.Version)
|
||||
}
|
||||
if shouldRemoveNeed(protocol.LocalDeviceID) {
|
||||
meta.removeNeeded(protocol.LocalDeviceID, oldGlobal)
|
||||
if keyBuf, err = t.updateLocalNeed(keyBuf, folder, file, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -862,8 +869,8 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
|
||||
if bytes.Equal(dev[:], device) { // Was the previous global
|
||||
continue
|
||||
}
|
||||
if fv, have := fl.Get(dev[:]); Need(removedFV, have, fv.Version) {
|
||||
meta.removeNeeded(dev, f)
|
||||
if shouldRemoveNeed(dev) {
|
||||
meta.removeNeeded(dev, oldGlobal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ const (
|
||||
DeviceDiscovered
|
||||
DeviceConnected
|
||||
DeviceDisconnected
|
||||
DeviceRejected
|
||||
DeviceRejected // DEPRECATED, superseded by PendingDevicesChanged
|
||||
PendingDevicesChanged
|
||||
DevicePaused
|
||||
DeviceResumed
|
||||
LocalChangeDetected
|
||||
@@ -38,7 +39,8 @@ const (
|
||||
ItemStarted
|
||||
ItemFinished
|
||||
StateChanged
|
||||
FolderRejected
|
||||
FolderRejected // DEPRECATED, superseded by PendingFoldersChanged
|
||||
PendingFoldersChanged
|
||||
ConfigSaved
|
||||
DownloadProgress
|
||||
RemoteDownloadProgress
|
||||
@@ -77,6 +79,8 @@ func (t EventType) String() string {
|
||||
return "DeviceDisconnected"
|
||||
case DeviceRejected:
|
||||
return "DeviceRejected"
|
||||
case PendingDevicesChanged:
|
||||
return "PendingDevicesChanged"
|
||||
case LocalChangeDetected:
|
||||
return "LocalChangeDetected"
|
||||
case RemoteChangeDetected:
|
||||
@@ -93,6 +97,8 @@ func (t EventType) String() string {
|
||||
return "StateChanged"
|
||||
case FolderRejected:
|
||||
return "FolderRejected"
|
||||
case PendingFoldersChanged:
|
||||
return "PendingFoldersChanged"
|
||||
case ConfigSaved:
|
||||
return "ConfigSaved"
|
||||
case DownloadProgress:
|
||||
@@ -158,6 +164,8 @@ func UnmarshalEventType(s string) EventType {
|
||||
return DeviceDisconnected
|
||||
case "DeviceRejected":
|
||||
return DeviceRejected
|
||||
case "PendingDevicesChanged":
|
||||
return PendingDevicesChanged
|
||||
case "LocalChangeDetected":
|
||||
return LocalChangeDetected
|
||||
case "RemoteChangeDetected":
|
||||
@@ -174,6 +182,8 @@ func UnmarshalEventType(s string) EventType {
|
||||
return StateChanged
|
||||
case "FolderRejected":
|
||||
return FolderRejected
|
||||
case "PendingFoldersChanged":
|
||||
return PendingFoldersChanged
|
||||
case "ConfigSaved":
|
||||
return ConfigSaved
|
||||
case "DownloadProgress":
|
||||
|
||||
@@ -27,12 +27,15 @@ var (
|
||||
)
|
||||
|
||||
func WithJunctionsAsDirs() Option {
|
||||
return func(fs Filesystem) {
|
||||
if basic, ok := fs.(*BasicFilesystem); !ok {
|
||||
l.Warnln("WithJunctionsAsDirs must only be used with FilesystemTypeBasic")
|
||||
} else {
|
||||
basic.junctionsAsDirs = true
|
||||
}
|
||||
return Option{
|
||||
apply: func(fs Filesystem) {
|
||||
if basic, ok := fs.(*BasicFilesystem); !ok {
|
||||
l.Warnln("WithJunctionsAsDirs must only be used with FilesystemTypeBasic")
|
||||
} else {
|
||||
basic.junctionsAsDirs = true
|
||||
}
|
||||
},
|
||||
id: "junctionsAsDirs",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +85,7 @@ func newBasicFilesystem(root string, opts ...Option) *BasicFilesystem {
|
||||
root: root,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(fs)
|
||||
opt.apply(fs)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func wrapError(err error) error {
|
||||
}
|
||||
|
||||
// call FSCTL_DUPLICATE_EXTENTS_TO_FILE IOCTL
|
||||
// see https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_duplicate_extents_to_file
|
||||
// see https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_duplicate_extents_to_file
|
||||
//
|
||||
// memo: Overflow (cloneRegionSize is greater than file ends) is safe and just ignored by windows.
|
||||
func callDuplicateExtentsToFile(src, dst uintptr, srcOffset, dstOffset int64, cloneRegionSize int64) error {
|
||||
|
||||
@@ -29,7 +29,7 @@ func isDirectoryJunction(path string) (bool, error) {
|
||||
}
|
||||
defer syscall.CloseHandle(h)
|
||||
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_attribute_tag_info
|
||||
//https://docs.microsoft.com/windows/win32/api/winbase/ns-winbase-file_attribute_tag_info
|
||||
const fileAttributeTagInfo = 9
|
||||
type FILE_ATTRIBUTE_TAG_INFO struct {
|
||||
FileAttributes uint32
|
||||
|
||||
225
lib/fs/casefs.go
225
lib/fs/casefs.go
@@ -14,11 +14,14 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const (
|
||||
// How long to consider cached dirnames valid
|
||||
caseCacheTimeout = time.Second
|
||||
caseCacheTimeout = time.Second
|
||||
caseCacheItemLimit = 4 << 10
|
||||
)
|
||||
|
||||
type ErrCaseConflict struct {
|
||||
@@ -40,33 +43,57 @@ type realCaser interface {
|
||||
}
|
||||
|
||||
type fskey struct {
|
||||
fstype FilesystemType
|
||||
uri string
|
||||
fstype FilesystemType
|
||||
uri, opts string
|
||||
}
|
||||
|
||||
// caseFilesystemRegistry caches caseFilesystems and runs a routine to drop
|
||||
// their cache every now and then.
|
||||
type caseFilesystemRegistry struct {
|
||||
fss map[fskey]*caseFilesystem
|
||||
mut sync.Mutex
|
||||
mut sync.RWMutex
|
||||
startCleaner sync.Once
|
||||
}
|
||||
|
||||
func (r *caseFilesystemRegistry) get(fs Filesystem) Filesystem {
|
||||
r.mut.Lock()
|
||||
defer r.mut.Unlock()
|
||||
|
||||
k := fskey{fs.Type(), fs.URI()}
|
||||
caseFs, ok := r.fss[k]
|
||||
if !ok {
|
||||
caseFs = &caseFilesystem{
|
||||
Filesystem: fs,
|
||||
realCaser: newDefaultRealCaser(fs),
|
||||
func newFSKey(fs Filesystem, opts ...Option) fskey {
|
||||
k := fskey{
|
||||
fstype: fs.Type(),
|
||||
uri: fs.URI(),
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
k.opts = opts[0].id
|
||||
for _, o := range opts[1:] {
|
||||
k.opts += "&" + o.id
|
||||
}
|
||||
r.fss[k] = caseFs
|
||||
r.startCleaner.Do(func() {
|
||||
go r.cleaner()
|
||||
})
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func (r *caseFilesystemRegistry) get(fs Filesystem, opts ...Option) Filesystem {
|
||||
k := newFSKey(fs, opts...)
|
||||
|
||||
// Use double locking when getting a caseFs. In the common case it will
|
||||
// already exist and we take the read lock fast path. If it doesn't, we
|
||||
// take a write lock and try again.
|
||||
|
||||
r.mut.RLock()
|
||||
caseFs, ok := r.fss[k]
|
||||
r.mut.RUnlock()
|
||||
|
||||
if !ok {
|
||||
r.mut.Lock()
|
||||
caseFs, ok = r.fss[k]
|
||||
if !ok {
|
||||
caseFs = &caseFilesystem{
|
||||
Filesystem: fs,
|
||||
realCaser: newDefaultRealCaser(fs),
|
||||
}
|
||||
r.fss[k] = caseFs
|
||||
r.startCleaner.Do(func() {
|
||||
go r.cleaner()
|
||||
})
|
||||
}
|
||||
r.mut.Unlock()
|
||||
}
|
||||
|
||||
return caseFs
|
||||
@@ -74,11 +101,23 @@ func (r *caseFilesystemRegistry) get(fs Filesystem) Filesystem {
|
||||
|
||||
func (r *caseFilesystemRegistry) cleaner() {
|
||||
for range time.NewTicker(time.Minute).C {
|
||||
r.mut.Lock()
|
||||
// We need to not hold this lock for a long time, as it blocks
|
||||
// creating new filesystems in get(), which is needed to do things
|
||||
// like add new folders. The (*caseFs).dropCache() method can take
|
||||
// an arbitrarily long time to kick in because it in turn waits for
|
||||
// locks held by things performing I/O. So we can't call that from
|
||||
// within the loop.
|
||||
|
||||
r.mut.RLock()
|
||||
toProcess := make([]*caseFilesystem, 0, len(r.fss))
|
||||
for _, caseFs := range r.fss {
|
||||
toProcess = append(toProcess, caseFs)
|
||||
}
|
||||
r.mut.RUnlock()
|
||||
|
||||
for _, caseFs := range toProcess {
|
||||
caseFs.dropCache()
|
||||
}
|
||||
r.mut.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +136,10 @@ type caseFilesystem struct {
|
||||
// from the real path. It is safe to use with any filesystem, i.e. also a
|
||||
// case-sensitive one. However it will add some overhead and thus shouldn't be
|
||||
// used if the filesystem is known to already behave case-sensitively.
|
||||
func NewCaseFilesystem(fs Filesystem) Filesystem {
|
||||
return wrapFilesystem(fs, globalCaseFilesystemRegistry.get)
|
||||
func NewCaseFilesystem(fs Filesystem, opts ...Option) Filesystem {
|
||||
return wrapFilesystem(fs, func(fs Filesystem) Filesystem {
|
||||
return globalCaseFilesystemRegistry.get(fs, opts...)
|
||||
})
|
||||
}
|
||||
|
||||
func (f *caseFilesystem) Chmod(name string, mode FileMode) error {
|
||||
@@ -329,96 +370,118 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
|
||||
}
|
||||
|
||||
type defaultRealCaser struct {
|
||||
fs Filesystem
|
||||
root *caseNode
|
||||
mut sync.RWMutex
|
||||
fs Filesystem
|
||||
cache caseCache
|
||||
}
|
||||
|
||||
func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
|
||||
cache, err := lru.New2Q(caseCacheItemLimit)
|
||||
// New2Q only errors if given invalid parameters, which we don't.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
caser := &defaultRealCaser{
|
||||
fs: fs,
|
||||
root: &caseNode{name: "."},
|
||||
fs: fs,
|
||||
cache: caseCache{
|
||||
TwoQueueCache: cache,
|
||||
},
|
||||
}
|
||||
return caser
|
||||
}
|
||||
|
||||
func (r *defaultRealCaser) realCase(name string) (string, error) {
|
||||
out := "."
|
||||
if name == out {
|
||||
return out, nil
|
||||
realName := "."
|
||||
if name == realName {
|
||||
return realName, nil
|
||||
}
|
||||
|
||||
r.mut.Lock()
|
||||
defer r.mut.Unlock()
|
||||
|
||||
node := r.root
|
||||
for _, comp := range strings.Split(name, string(PathSeparator)) {
|
||||
if node.dirNames == nil || node.expires.Before(time.Now()) {
|
||||
// Haven't called DirNames yet, or the node has expired
|
||||
node := r.cache.getExpireAdd(realName)
|
||||
|
||||
var err error
|
||||
node.dirNames, err = r.fs.DirNames(out)
|
||||
node.once.Do(func() {
|
||||
dirNames, err := r.fs.DirNames(realName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
r.cache.Remove(realName)
|
||||
node.err = err
|
||||
return
|
||||
}
|
||||
|
||||
node.dirNamesLower = make([]string, len(node.dirNames))
|
||||
for i, n := range node.dirNames {
|
||||
node.dirNamesLower[i] = UnicodeLowercase(n)
|
||||
num := len(dirNames)
|
||||
node.children = make(map[string]struct{}, num)
|
||||
node.lowerToReal = make(map[string]string, num)
|
||||
lastLower := ""
|
||||
for _, n := range dirNames {
|
||||
node.children[n] = struct{}{}
|
||||
lower := UnicodeLowercase(n)
|
||||
if lower != lastLower {
|
||||
node.lowerToReal[lower] = n
|
||||
lastLower = n
|
||||
}
|
||||
}
|
||||
|
||||
node.expires = time.Now().Add(caseCacheTimeout)
|
||||
node.child = nil
|
||||
})
|
||||
if node.err != nil {
|
||||
return "", node.err
|
||||
}
|
||||
|
||||
// If we don't already have a correct cached child, try to find it.
|
||||
if node.child == nil || node.child.name != comp {
|
||||
// Actually loop dirNames to search for a match.
|
||||
n, err := findCaseInsensitiveMatch(comp, node.dirNames, node.dirNamesLower)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// Try to find a direct or case match
|
||||
if _, ok := node.children[comp]; !ok {
|
||||
comp, ok = node.lowerToReal[UnicodeLowercase(comp)]
|
||||
if !ok {
|
||||
return "", ErrNotExist
|
||||
}
|
||||
node.child = &caseNode{name: n}
|
||||
}
|
||||
|
||||
node = node.child
|
||||
out = filepath.Join(out, node.name)
|
||||
realName = filepath.Join(realName, comp)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
return realName, nil
|
||||
}
|
||||
|
||||
func (r *defaultRealCaser) dropCache() {
|
||||
r.mut.Lock()
|
||||
r.root = &caseNode{name: "."}
|
||||
r.mut.Unlock()
|
||||
r.cache.Purge()
|
||||
}
|
||||
|
||||
// Both name and the key to children are "Real", case resolved names of the path
|
||||
func newCaseNode() *caseNode {
|
||||
return &caseNode{
|
||||
expires: time.Now().Add(caseCacheTimeout),
|
||||
}
|
||||
}
|
||||
|
||||
// The keys to children are "real", case resolved names of the path
|
||||
// component this node represents (i.e. containing no path separator).
|
||||
// The key to results is also a path component, but as given to RealCase, not
|
||||
// case resolved.
|
||||
// lowerToReal is a map of lowercase path components (as in UnicodeLowercase)
|
||||
// to their corresponding "real", case resolved names.
|
||||
// A node is created empty and populated using once. If an error occurs the node
|
||||
// is removed from cache and the error stored in err, such that anyone that
|
||||
// already got the node doesn't try to access the nil maps.
|
||||
type caseNode struct {
|
||||
name string
|
||||
expires time.Time
|
||||
dirNames []string
|
||||
dirNamesLower []string
|
||||
child *caseNode
|
||||
expires time.Time
|
||||
lowerToReal map[string]string
|
||||
children map[string]struct{}
|
||||
once sync.Once
|
||||
err error
|
||||
}
|
||||
|
||||
func findCaseInsensitiveMatch(name string, names, namesLower []string) (string, error) {
|
||||
lower := UnicodeLowercase(name)
|
||||
candidate := ""
|
||||
for i, n := range names {
|
||||
if n == name {
|
||||
return n, nil
|
||||
}
|
||||
if candidate == "" && namesLower[i] == lower {
|
||||
candidate = n
|
||||
}
|
||||
}
|
||||
if candidate == "" {
|
||||
return "", ErrNotExist
|
||||
}
|
||||
return candidate, nil
|
||||
type caseCache struct {
|
||||
*lru.TwoQueueCache
|
||||
mut sync.Mutex
|
||||
}
|
||||
|
||||
// getExpireAdd gets an entry for the given key. If no entry exists, or it is
|
||||
// expired a new one is created and added to the cache.
|
||||
func (c *caseCache) getExpireAdd(key string) *caseNode {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
v, ok := c.Get(key)
|
||||
if !ok {
|
||||
node := newCaseNode()
|
||||
c.Add(key, node)
|
||||
return node
|
||||
}
|
||||
node := v.(*caseNode)
|
||||
if node.expires.Before(time.Now()) {
|
||||
node = newCaseNode()
|
||||
c.Add(key, node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
|
||||
b.Run("rawfs", func(b *testing.B) {
|
||||
fakefs := unwrapFilesystem(fsys).(*fakefs)
|
||||
fakefs.resetCounters()
|
||||
benchmarkWalkFakeFS(b, fsys, paths)
|
||||
benchmarkWalkFakeFS(b, fsys, paths, 0, "")
|
||||
fakefs.reportMetricsPerOp(b)
|
||||
fakefs.reportMetricsPer(b, entries, "entry")
|
||||
b.ReportAllocs()
|
||||
@@ -176,14 +176,37 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
|
||||
}
|
||||
fakefs := unwrapFilesystem(fsys).(*fakefs)
|
||||
fakefs.resetCounters()
|
||||
benchmarkWalkFakeFS(b, casefs, paths)
|
||||
benchmarkWalkFakeFS(b, casefs, paths, 0, "")
|
||||
fakefs.reportMetricsPerOp(b)
|
||||
fakefs.reportMetricsPer(b, entries, "entry")
|
||||
b.ReportAllocs()
|
||||
})
|
||||
var otherOpPath string
|
||||
sep := string(PathSeparator)
|
||||
longest := 0
|
||||
for _, p := range paths {
|
||||
if length := len(strings.Split(p, sep)); length > longest {
|
||||
otherOpPath = p
|
||||
longest = length
|
||||
}
|
||||
}
|
||||
otherOpEvery := 1000
|
||||
b.Run(fmt.Sprintf("casefs-otherOpEvery%v", otherOpEvery), func(b *testing.B) {
|
||||
// Construct the casefs manually or it will get cached and the benchmark is invalid.
|
||||
casefs := &caseFilesystem{
|
||||
Filesystem: fsys,
|
||||
realCaser: newDefaultRealCaser(fsys),
|
||||
}
|
||||
fakefs := unwrapFilesystem(fsys).(*fakefs)
|
||||
fakefs.resetCounters()
|
||||
benchmarkWalkFakeFS(b, casefs, paths, otherOpEvery, otherOpPath)
|
||||
fakefs.reportMetricsPerOp(b)
|
||||
fakefs.reportMetricsPer(b, entries, "entry")
|
||||
b.ReportAllocs()
|
||||
})
|
||||
}
|
||||
|
||||
func benchmarkWalkFakeFS(b *testing.B, fsys Filesystem, paths []string) {
|
||||
func benchmarkWalkFakeFS(b *testing.B, fsys Filesystem, paths []string, otherOpEvery int, otherOpPath string) {
|
||||
// Simulate a scanner pass over the filesystem. First walk it to
|
||||
// discover all names, then stat each name individually to check if it's
|
||||
// been deleted or not (pretending that they all existed in the
|
||||
@@ -194,7 +217,7 @@ func benchmarkWalkFakeFS(b *testing.B, fsys Filesystem, paths []string) {
|
||||
t0 := time.Now()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := doubleWalkFS(fsys, paths); err != nil {
|
||||
if err := doubleWalkFSWithOtherOps(fsys, paths, otherOpEvery, otherOpPath); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -250,15 +273,39 @@ func TestStressCaseFS(t *testing.T) {
|
||||
}
|
||||
|
||||
func doubleWalkFS(fsys Filesystem, paths []string) error {
|
||||
return doubleWalkFSWithOtherOps(fsys, paths, 0, "")
|
||||
}
|
||||
|
||||
func doubleWalkFSWithOtherOps(fsys Filesystem, paths []string, otherOpEvery int, otherOpPath string) error {
|
||||
i := 0
|
||||
if err := fsys.Walk("/", func(path string, info FileInfo, err error) error {
|
||||
i++
|
||||
if otherOpEvery != 0 && i%otherOpEvery == 0 {
|
||||
// l.Infoln("AAA", otherOpPath)
|
||||
if _, err := fsys.Lstat(otherOpPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// l.Infoln("CCC", path)
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
if _, err := fsys.Lstat(p); err != nil {
|
||||
return err
|
||||
for p != "." {
|
||||
i++
|
||||
if otherOpEvery != 0 && i%otherOpEvery == 0 {
|
||||
if _, err := fsys.Lstat(otherOpPath); err != nil {
|
||||
// l.Infoln("AAA", otherOpPath)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// l.Infoln("CCC", p)
|
||||
if _, err := fsys.Lstat(p); err != nil {
|
||||
return err
|
||||
}
|
||||
p = filepath.Dir(p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -66,7 +66,7 @@ func DebugSymlinkForTestsOnly(oldFs, newFS Filesystem, oldname, newname string)
|
||||
// or contains .. elements), or is short enough, fixLongPath returns
|
||||
// path unmodified.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||
// See https://docs.microsoft.com/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
|
||||
func fixLongPath(path string) string {
|
||||
// Do nothing (and don't allocate) if the path is "short".
|
||||
// Empirically (at least on the Windows Server 2013 builder),
|
||||
@@ -77,7 +77,7 @@ func fixLongPath(path string) string {
|
||||
// name (that is, the directory name cannot exceed MAX_PATH
|
||||
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||
//
|
||||
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||
// The MS docs appear to say that a normal path that is 248 bytes long
|
||||
// will work; empirically the path must be less than 248 bytes long.
|
||||
if len(path) < 248 {
|
||||
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||
|
||||
@@ -178,7 +178,10 @@ var IsPermission = os.IsPermission
|
||||
// IsPathSeparator is the equivalent of os.IsPathSeparator
|
||||
var IsPathSeparator = os.IsPathSeparator
|
||||
|
||||
type Option func(Filesystem)
|
||||
type Option struct {
|
||||
apply func(Filesystem)
|
||||
id string
|
||||
}
|
||||
|
||||
func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem {
|
||||
var fs Filesystem
|
||||
|
||||
@@ -63,7 +63,7 @@ func WindowsInvalidFilename(name string) error {
|
||||
// None of the path components should end in space or period, or be a
|
||||
// reserved name. COM0 and LPT0 are missing from the Microsoft docs,
|
||||
// but Windows Explorer treats them as invalid too.
|
||||
// (https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file)
|
||||
// (https://docs.microsoft.com/windows/win32/fileio/naming-a-file)
|
||||
for _, part := range strings.Split(name, `\`) {
|
||||
if len(part) == 0 {
|
||||
continue
|
||||
|
||||
@@ -21,7 +21,7 @@ type cache struct {
|
||||
|
||||
type cacheEntry struct {
|
||||
result Result
|
||||
access time.Time
|
||||
access int64 // Unix nanosecond count. Sufficient until the year 2262.
|
||||
}
|
||||
|
||||
func newCache(patterns []Pattern) *cache {
|
||||
@@ -33,7 +33,7 @@ func newCache(patterns []Pattern) *cache {
|
||||
|
||||
func (c *cache) clean(d time.Duration) {
|
||||
for k, v := range c.entries {
|
||||
if clock.Now().Sub(v.access) > d {
|
||||
if clock.Now().Sub(time.Unix(0, v.access)) > d {
|
||||
delete(c.entries, k)
|
||||
}
|
||||
}
|
||||
@@ -42,14 +42,14 @@ func (c *cache) clean(d time.Duration) {
|
||||
func (c *cache) get(key string) (Result, bool) {
|
||||
entry, ok := c.entries[key]
|
||||
if ok {
|
||||
entry.access = clock.Now()
|
||||
entry.access = clock.Now().UnixNano()
|
||||
c.entries[key] = entry
|
||||
}
|
||||
return entry.result, ok
|
||||
}
|
||||
|
||||
func (c *cache) set(key string, result Result) {
|
||||
c.entries[key] = cacheEntry{result, time.Now()}
|
||||
c.entries[key] = cacheEntry{result, time.Now().UnixNano()}
|
||||
}
|
||||
|
||||
func (c *cache) len() int {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user