mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-14 08:49:17 -05:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d87051ca99 | ||
|
|
3798cebad0 | ||
|
|
a477989950 | ||
|
|
5065d1d0b4 | ||
|
|
829990c9ef | ||
|
|
ac037e0fa3 | ||
|
|
da42d51008 | ||
|
|
99027813ef | ||
|
|
9112ba8f0b | ||
|
|
843fd9bdbd | ||
|
|
26c33c4a69 | ||
|
|
2db76ae786 | ||
|
|
a0b15d006d | ||
|
|
23b27fa24a | ||
|
|
b6f580cbc2 | ||
|
|
f2459ef331 | ||
|
|
0a37fac794 | ||
|
|
2d9a822ed7 | ||
|
|
98622ca4d0 | ||
|
|
f7a25adcbd | ||
|
|
9bf13b253c | ||
|
|
2e8b639a34 | ||
|
|
672f7a010f | ||
|
|
37e15c4368 | ||
|
|
4d7837ba96 | ||
|
|
a6c8423905 | ||
|
|
832ed556d9 | ||
|
|
7c6fb018ca | ||
|
|
9c5c06bf31 | ||
|
|
61e3daaead | ||
|
|
9c0fde795e | ||
|
|
ce4f565e2f | ||
|
|
5369a62fd5 | ||
|
|
b44016ff70 | ||
|
|
9f76c87880 | ||
|
|
42ae2898e1 | ||
|
|
dd649a6be4 | ||
|
|
593f098276 | ||
|
|
4a87221f16 | ||
|
|
7745ed34d3 | ||
|
|
8fe546c4a2 | ||
|
|
381f6aeaf6 | ||
|
|
9154bacced | ||
|
|
dc0dc8efb4 | ||
|
|
b062d5dd7f | ||
|
|
c519e582b5 | ||
|
|
6b9dce36bf | ||
|
|
8e0520887a | ||
|
|
cfd1fdb38e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ stcli.exe
|
||||
*.sublime*
|
||||
discosrv
|
||||
stpidx
|
||||
.jshintrc
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package beacon
|
||||
|
||||
import "net"
|
||||
@@ -102,9 +106,7 @@ func (b *Beacon) writer() {
|
||||
if debug {
|
||||
l.Debugln(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
} else if debug {
|
||||
l.Debugf("sent %d bytes to %s", len(bs), dst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/beacon"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := beacon.NewBeacon(21025)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
bs, addr := b.Recv()
|
||||
log.Printf("Received %d bytes from %s: %x %x", len(bs), addr, bs[:8], bs[8:])
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
bs := [16]byte{}
|
||||
binary.BigEndian.PutUint64(bs[:], uint64(time.Now().UnixNano()))
|
||||
log.Printf("My ID: %x", bs[:8])
|
||||
for {
|
||||
binary.BigEndian.PutUint64(bs[8:], uint64(time.Now().UnixNano()))
|
||||
b.Send(bs[:])
|
||||
log.Printf("Sent %d bytes", len(bs[:]))
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}()
|
||||
select {}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package beacon
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package beacon implements an UDP broadcast beacon
|
||||
package beacon
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package buffers manages a set of reusable byte buffers.
|
||||
package buffers
|
||||
|
||||
|
||||
7
build.sh
7
build.sh
@@ -3,7 +3,7 @@
|
||||
export COPYFILE_DISABLE=true
|
||||
export GO386=387 # Don't use SSE on 32 bit builds
|
||||
|
||||
distFiles=(README.md LICENSE) # apart from the binary itself
|
||||
distFiles=(README.md LICENSE CONTRIBUTORS) # apart from the binary itself
|
||||
version=$(git describe --always --dirty)
|
||||
date=$(git show -s --format=%ct)
|
||||
user=$(whoami)
|
||||
@@ -60,7 +60,10 @@ zipDist() {
|
||||
name="$1"
|
||||
rm -rf "$name"
|
||||
mkdir -p "$name"
|
||||
cp syncthing.exe "${distFiles[@]}" "$name"
|
||||
for f in "${distFiles[@]}" ; do
|
||||
sed 's/$/
|
||||
/' < "$f" > "$name/$f.txt"
|
||||
done
|
||||
cp syncthing.exe "$name"
|
||||
sign "$name/syncthing.exe"
|
||||
zip -r "$name.zip" "$name"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package cid provides a manager for mappings between node ID:s and connection ID:s.
|
||||
package cid
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package cid
|
||||
|
||||
import "testing"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//+build solaris
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//+build !windows,!solaris
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//+build locktrace
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -49,7 +53,7 @@ func init() {
|
||||
LongVersion = fmt.Sprintf("syncthing %s (%s %s-%s) %s@%s %s", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH, BuildUser, BuildHost, date)
|
||||
|
||||
if os.Getenv("STTRACE") != "" {
|
||||
l.SetFlags(log.Ltime | log.Ldate | log.Lmicroseconds | log.Lshortfile)
|
||||
logFlags = log.Ltime | log.Ldate | log.Lmicroseconds | log.Lshortfile
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +61,7 @@ var (
|
||||
cfg config.Configuration
|
||||
myID string
|
||||
confDir string
|
||||
logFlags int = log.Ltime
|
||||
rateBucket *ratelimit.Bucket
|
||||
stop = make(chan bool)
|
||||
discoverer *discover.Discoverer
|
||||
@@ -64,7 +69,19 @@ var (
|
||||
|
||||
const (
|
||||
usage = "syncthing [options]"
|
||||
extraUsage = `The following enviroment variables are interpreted by syncthing:
|
||||
extraUsage = `The value for the -logflags option is a sum of the following:
|
||||
|
||||
1 Date
|
||||
2 Time
|
||||
4 Microsecond time
|
||||
8 Long filename
|
||||
16 Short filename
|
||||
|
||||
I.e. to prefix each log line with date and time, set -logflags=3 (1 + 2 from
|
||||
above). The value 0 is used to disable all of the above. The default is to
|
||||
show time only (2).
|
||||
|
||||
The following enviroment variables are interpreted by syncthing:
|
||||
|
||||
STNORESTART Do not attempt to restart when requested to, instead just exit.
|
||||
Set this variable when running under a service manager such as
|
||||
@@ -98,6 +115,7 @@ func main() {
|
||||
flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster")
|
||||
flag.BoolVar(&showVersion, "version", false, "Show version")
|
||||
flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade")
|
||||
flag.IntVar(&logFlags, "logflags", logFlags, "Set log flags")
|
||||
flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
|
||||
flag.Parse()
|
||||
|
||||
@@ -106,6 +124,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
l.SetFlags(logFlags)
|
||||
|
||||
if doUpgrade {
|
||||
err := upgrade()
|
||||
if err != nil {
|
||||
@@ -203,9 +223,9 @@ func main() {
|
||||
l.FatalErr(err)
|
||||
cfg.GUI.Address = fmt.Sprintf("127.0.0.1:%d", port)
|
||||
|
||||
port, err = getFreePort("", 22000)
|
||||
port, err = getFreePort("0.0.0.0", 22000)
|
||||
l.FatalErr(err)
|
||||
cfg.Options.ListenAddress = []string{fmt.Sprintf(":%d", port)}
|
||||
cfg.Options.ListenAddress = []string{fmt.Sprintf("0.0.0.0:%d", port)}
|
||||
|
||||
saveConfig()
|
||||
l.Infof("Edit %s to taste or use the GUI\n", cfgFile)
|
||||
@@ -695,6 +715,7 @@ func expandTilde(p string) string {
|
||||
return getHomeDir()
|
||||
}
|
||||
|
||||
p = filepath.FromSlash(p)
|
||||
if !strings.HasPrefix(p, fmt.Sprintf("~%c", os.PathSeparator)) {
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package config implements reading and writing of the syncthing configuration file.
|
||||
package config
|
||||
|
||||
@@ -244,7 +248,7 @@ func Load(rd io.Reader, myID string) (Configuration, error) {
|
||||
repo := &cfg.Repositories[i]
|
||||
|
||||
if len(repo.Directory) == 0 {
|
||||
repo.Invalid = "empty directory"
|
||||
repo.Invalid = "no directory configured"
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
@@ -187,9 +191,8 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
} else {
|
||||
buf = d.announcementPkt()
|
||||
}
|
||||
var errCounter = 0
|
||||
|
||||
for errCounter < maxErrors {
|
||||
for {
|
||||
var ok bool
|
||||
|
||||
if debug {
|
||||
@@ -201,11 +204,8 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
if debug {
|
||||
l.Debugln("discover: warning:", err)
|
||||
}
|
||||
errCounter++
|
||||
ok = false
|
||||
} else {
|
||||
errCounter = 0
|
||||
|
||||
// Verify that the announce server responds positively for our node ID
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
@@ -214,7 +214,6 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
l.Debugln("discover: external lookup check:", res)
|
||||
}
|
||||
ok = len(res) > 0
|
||||
|
||||
}
|
||||
|
||||
d.extAnnounceOKmut.Lock()
|
||||
@@ -227,7 +226,6 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
}
|
||||
l.Warnf("Global discovery: %v: stopping due to too many errors: %v", remote, err)
|
||||
}
|
||||
|
||||
func (d *Discoverer) recvAnnouncements() {
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package discover implements the node discovery protocol.
|
||||
package discover
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package discover
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package files
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package files provides a set type to track local/remote files with newness checks.
|
||||
package files
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//+build anal
|
||||
|
||||
package files
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//+build !anal
|
||||
|
||||
package files
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package files
|
||||
|
||||
import (
|
||||
|
||||
68
gui/app.js
68
gui/app.js
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/*jslint browser: true, continue: true, plusplus: true */
|
||||
/*global $: false, angular: false */
|
||||
|
||||
@@ -16,6 +20,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.myID = '';
|
||||
$scope.nodes = [];
|
||||
$scope.configInSync = true;
|
||||
$scope.protocolChanged = false;
|
||||
$scope.errors = [];
|
||||
$scope.seenError = '';
|
||||
$scope.model = {};
|
||||
@@ -122,7 +127,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.repoClass = function (repo) {
|
||||
if (typeof $scope.model[repo] === 'undefined') {
|
||||
@@ -141,7 +146,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
return 'primary';
|
||||
}
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.syncPercentage = function (repo) {
|
||||
if (typeof $scope.model[repo] === 'undefined') {
|
||||
@@ -255,13 +260,19 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.config.workingOptions = angular.copy($scope.config.Options);
|
||||
$scope.config.workingGUI = angular.copy($scope.config.GUI);
|
||||
$('#settings').modal({backdrop: 'static', keyboard: true});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.saveSettings = function () {
|
||||
// Make sure something changed
|
||||
var changed = ! angular.equals($scope.config.Options, $scope.config.workingOptions) ||
|
||||
! angular.equals($scope.config.GUI, $scope.config.workingGUI);
|
||||
if(changed){
|
||||
// see if protocol will need to be changed on restart
|
||||
if($scope.config.GUI.UseTLS !== $scope.config.workingGUI.UseTLS){
|
||||
$scope.protocolChanged = true;
|
||||
}
|
||||
|
||||
// Apply new settings locally
|
||||
$scope.config.Options = angular.copy($scope.config.workingOptions);
|
||||
$scope.config.GUI = angular.copy($scope.config.workingGUI);
|
||||
|
||||
@@ -278,6 +289,21 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$('#restarting').modal({backdrop: 'static', keyboard: false});
|
||||
$http.post(urlbase + '/restart');
|
||||
$scope.configInSync = true;
|
||||
|
||||
// Switch webpage protocol if needed
|
||||
if($scope.protocolChanged){
|
||||
var protocol = 'http';
|
||||
|
||||
if($scope.config.GUI.UseTLS){
|
||||
protocol = 'https';
|
||||
}
|
||||
|
||||
setTimeout(function(){
|
||||
window.location.protocol = protocol;
|
||||
}, 1000);
|
||||
|
||||
$scope.protocolChanged = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.shutdown = function () {
|
||||
@@ -402,7 +428,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
|
||||
$scope.repoList = function () {
|
||||
return repoList($scope.repos);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.editRepo = function (nodeCfg) {
|
||||
$scope.currentRepo = angular.copy(nodeCfg);
|
||||
@@ -544,7 +570,7 @@ function repoMap(l) {
|
||||
function repoList(m) {
|
||||
var l = [];
|
||||
for (var id in m) {
|
||||
l.push(m[id])
|
||||
l.push(m[id]);
|
||||
}
|
||||
l.sort(repoCompare);
|
||||
return l;
|
||||
@@ -633,7 +659,7 @@ syncthing.filter('chunkID', function () {
|
||||
if (!parts)
|
||||
return "";
|
||||
return parts.join('-');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.filter('shortPath', function () {
|
||||
@@ -645,7 +671,13 @@ syncthing.filter('shortPath', function () {
|
||||
return input;
|
||||
}
|
||||
return ".../" + parts.slice(parts.length-2).join("/");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.filter('clean', function () {
|
||||
return function (input) {
|
||||
return encodeURIComponent(input).replace(/%/g, '');
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.directive('optionEditor', function () {
|
||||
@@ -680,3 +712,25 @@ syncthing.directive('uniqueRepo', function() {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.directive('validNodeid', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('validNodeid', true);
|
||||
} else {
|
||||
var cleaned = viewValue.replace(/ /g, '').replace(/-/g, '').toUpperCase().trim();
|
||||
if (cleaned.match(/^[A-Z2-7]{52}$/)) {
|
||||
ctrl.$setValidity('validNodeid', true);
|
||||
} else {
|
||||
ctrl.$setValidity('validNodeid', false);
|
||||
}
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
Use of this source code is governed by an MIT-style license that can be
|
||||
found in the LICENSE file.
|
||||
-->
|
||||
<html lang="en" ng-app="syncthing" ng-controller="SyncthingCtrl" class="ng-cloak">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -60,12 +65,22 @@
|
||||
}
|
||||
|
||||
.table th {
|
||||
white-space:nowrap;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
@media (max-width:767px) {
|
||||
.table-responsive>.table>tbody>tr>td {
|
||||
/* revert a bootstrap setting e.g.:
|
||||
* for mobile phones to allow linebreaks in long repro folder/shared with
|
||||
* columns. */
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -124,13 +139,13 @@
|
||||
<div class="panel panel-{{repoClass(repo.ID)}}" ng-repeat="repo in repoList()">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#repositories" href="#repo-{{repo.ID}}">
|
||||
<a data-toggle="collapse" data-parent="#repositories" href="#repo-{{$index}}">
|
||||
<span class="glyphicon glyphicon-hdd"></span> {{repo.Directory | shortPath}}
|
||||
<span class="pull-right hidden-xs">{{repoStatus(repo.ID)}}</span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="repo-{{repo.ID}}" class="panel-collapse collapse">
|
||||
<div id="repo-{{$index}}" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-striped">
|
||||
@@ -143,6 +158,10 @@
|
||||
<th><span class="glyphicon glyphicon-folder-open"></span> Folder</th>
|
||||
<td class="text-right">{{repo.Directory}}</td>
|
||||
</tr>
|
||||
<tr ng-if="model[repo.ID].invalid">
|
||||
<th><span class="glyphicon glyphicon-warning-sign"></span> Error</th>
|
||||
<td class="text-right">{{model[repo.ID].invalid}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="glyphicon glyphicon-comment"></span> Synchronization</th>
|
||||
<td class="text-right">{{repoStatus(repo.ID)}}</td>
|
||||
@@ -194,10 +213,10 @@
|
||||
<div class="panel panel-default" ng-repeat="nodeCfg in [thisNode()]">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#nodes" href="#node-{{nodeCfg.NodeID}}"><span class="glyphicon glyphicon-home"></span> {{nodeName(nodeCfg)}}</a>
|
||||
<a data-toggle="collapse" data-parent="#nodes" href="#node-this"><span class="glyphicon glyphicon-home"></span> {{nodeName(nodeCfg)}}</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="node-{{nodeCfg.NodeID}}" class="panel-collapse collapse in">
|
||||
<div id="node-this" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-striped">
|
||||
@@ -240,14 +259,14 @@
|
||||
<div class="panel panel-{{nodeClass(nodeCfg)}}" ng-repeat="nodeCfg in otherNodes()">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#nodes" href="#node-{{nodeCfg.NodeID}}">
|
||||
<a data-toggle="collapse" data-parent="#nodes" href="#node-{{$index}}">
|
||||
<span class="glyphicon glyphicon-retweet"></span>
|
||||
{{nodeName(nodeCfg)}}
|
||||
<span class="pull-right hidden-xs">{{nodeStatus(nodeCfg)}}</span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="node-{{nodeCfg.NodeID}}" class="panel-collapse collapse">
|
||||
<div id="node-{{$index}}" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-striped">
|
||||
@@ -414,13 +433,14 @@
|
||||
<form role="form" name="nodeEditor">
|
||||
<div class="form-group" ng-class="{'has-error': nodeEditor.nodeID.$invalid && nodeEditor.nodeID.$dirty}">
|
||||
<label for="nodeID">Node ID</label>
|
||||
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control text-monospace" type="text" ng-model="currentNode.NodeID" required></input>
|
||||
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control text-monospace" type="text" ng-model="currentNode.NodeID" required valid-nodeid></input>
|
||||
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentNode.NodeID | chunkID}}</div>
|
||||
<p class="help-block">
|
||||
<span ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).
|
||||
<span ng-show="!editingExisting">When adding a new node, keep in mind that <em>this node</em> must be added on the other side too.</span>
|
||||
</span>
|
||||
<span ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
|
||||
<span ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -459,11 +479,12 @@
|
||||
<div class="col-md-12">
|
||||
<div class="form-group" ng-class="{'has-error': repoEditor.repoID.$invalid && repoEditor.repoID.$dirty}">
|
||||
<label for="repoID">Repository ID</label>
|
||||
<input name="repoID" placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID" required unique-repo></input>
|
||||
<input name="repoID" placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID" required unique-repo ng-pattern="/^[a-zA-Z0-9-_.]{1,64}$/"></input>
|
||||
<p class="help-block">
|
||||
<span ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span>
|
||||
<span ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span>
|
||||
<span ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span>
|
||||
<span ng-if="repoEditor.repoID.$error.pattern && repoEditor.repoID.$dirty">The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the <code>-_.</code> characters only.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': repoEditor.repoPath.$invalid && repoEditor.repoPath.$dirty}">
|
||||
@@ -508,7 +529,7 @@
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> Simple File Versioning
|
||||
<input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> File Versioning
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">Files are moved to date stamped versions in a <code>.stversions</code> folder when replaced or deleted by syncthing.</p>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
# Use of this source code is governed by an MIT-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
iterations=${1:-5}
|
||||
|
||||
id1=I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
# Use of this source code is governed by an MIT-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
iterations=${1:-5}
|
||||
|
||||
id1=I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package lamport implements a simple Lamport Clock for versioning
|
||||
package lamport
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package logger implements a standardized logger with callback functionality
|
||||
package logger
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -16,6 +20,7 @@ type bqBlock struct {
|
||||
file scanner.File
|
||||
block scanner.Block // get this block from the network
|
||||
copy []scanner.Block // copy these blocks from the old version of the file
|
||||
first bool
|
||||
last bool
|
||||
}
|
||||
|
||||
@@ -47,24 +52,30 @@ func (q *blockQueue) addBlock(a bqAdd) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
l := len(a.need)
|
||||
|
||||
if len(a.have) > 0 {
|
||||
// First queue a copy operation
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
copy: a.have,
|
||||
file: a.file,
|
||||
copy: a.have,
|
||||
first: true,
|
||||
last: l == 0,
|
||||
})
|
||||
}
|
||||
|
||||
// Queue the needed blocks individually
|
||||
l := len(a.need)
|
||||
for i, b := range a.need {
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
block: b,
|
||||
first: len(a.have) == 0 && i == 0,
|
||||
last: i == l-1,
|
||||
})
|
||||
}
|
||||
|
||||
if l == 0 {
|
||||
if len(a.need)+len(a.have) == 0 {
|
||||
// If we didn't have anything to fetch, queue an empty block with the "last" flag set to close the file.
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package model implements repository abstraction and file pulling mechanisms
|
||||
package model
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -186,8 +190,8 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
res["total"] = ConnectionInfo{
|
||||
Statistics: protocol.Statistics{
|
||||
At: time.Now(),
|
||||
InBytesTotal: int(in),
|
||||
OutBytesTotal: int(out),
|
||||
InBytesTotal: in,
|
||||
OutBytesTotal: out,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -275,7 +279,8 @@ func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
if r, ok := m.repoFiles[repo]; ok {
|
||||
r.Replace(id, files)
|
||||
} else {
|
||||
l.Warnf("Index from %s for nonexistant repo %q; dropping", nodeID, repo)
|
||||
l.Warnf("Index from %s for unexpected repo %q; verify configuration", nodeID, repo)
|
||||
|
||||
}
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
@@ -566,7 +571,10 @@ func (m *Model) broadcastIndexLoop() {
|
||||
idx := m.protocolIndex(repo)
|
||||
indexWg.Add(1)
|
||||
go func() {
|
||||
m.saveIndex(repo, m.indexDir, idx)
|
||||
err := m.saveIndex(repo, m.indexDir, idx)
|
||||
if err != nil {
|
||||
l.Infof("Saving index for %q: %v", repo, err)
|
||||
}
|
||||
indexWg.Done()
|
||||
}()
|
||||
|
||||
@@ -628,7 +636,10 @@ func (m *Model) ScanRepos() {
|
||||
for _, repo := range repos {
|
||||
repo := repo
|
||||
go func() {
|
||||
m.ScanRepo(repo)
|
||||
err := m.ScanRepo(repo)
|
||||
if err != nil {
|
||||
invalidateRepo(m.cfg, repo, err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
@@ -684,7 +695,10 @@ func (m *Model) SaveIndexes(dir string) {
|
||||
m.rmut.RLock()
|
||||
for repo := range m.repoCfgs {
|
||||
fs := m.protocolIndex(repo)
|
||||
m.saveIndex(repo, dir, fs)
|
||||
err := m.saveIndex(repo, dir, fs)
|
||||
if err != nil {
|
||||
l.Infof("Saving index for %q: %v", repo, err)
|
||||
}
|
||||
}
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
@@ -698,26 +712,43 @@ func (m *Model) LoadIndexes(dir string) {
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
|
||||
func (m *Model) saveIndex(repo string, dir string, fs []protocol.FileInfo) {
|
||||
func (m *Model) saveIndex(repo string, dir string, fs []protocol.FileInfo) error {
|
||||
id := fmt.Sprintf("%x", sha1.Sum([]byte(m.repoCfgs[repo].Directory)))
|
||||
name := id + ".idx.gz"
|
||||
name = filepath.Join(dir, name)
|
||||
|
||||
idxf, err := os.Create(name + ".tmp")
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
gzw := gzip.NewWriter(idxf)
|
||||
|
||||
protocol.IndexMessage{
|
||||
n, err := protocol.IndexMessage{
|
||||
Repository: repo,
|
||||
Files: fs,
|
||||
}.EncodeXDR(gzw)
|
||||
gzw.Close()
|
||||
idxf.Close()
|
||||
if err != nil {
|
||||
gzw.Close()
|
||||
idxf.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
osutil.Rename(name+".tmp", name)
|
||||
err = gzw.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = idxf.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln("wrote index,", n, "bytes uncompressed")
|
||||
}
|
||||
|
||||
return osutil.Rename(name+".tmp", name)
|
||||
}
|
||||
|
||||
func (m *Model) loadIndex(repo string, dir string) []protocol.FileInfo {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -5,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/buffers"
|
||||
@@ -219,6 +224,10 @@ func (p *puller) fixupDirectories() {
|
||||
var changed = 0
|
||||
|
||||
var walkFn = func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
@@ -258,7 +267,7 @@ func (p *puller) fixupDirectories() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !scanner.PermsEqual(cur.Flags, uint32(info.Mode())) {
|
||||
if !p.repoCfg.IgnorePerms && protocol.HasPermissionBits(cur.Flags) && !scanner.PermsEqual(cur.Flags, uint32(info.Mode())) {
|
||||
err := os.Chmod(path, os.FileMode(cur.Flags)&os.ModePerm)
|
||||
if err != nil {
|
||||
l.Warnf("Restoring folder flags: %q: %v", path, err)
|
||||
@@ -274,7 +283,10 @@ func (p *puller) fixupDirectories() {
|
||||
t := time.Unix(cur.Modified, 0)
|
||||
err := os.Chtimes(path, t, t)
|
||||
if err != nil {
|
||||
l.Warnf("Restoring folder modtime: %q: %v", path, err)
|
||||
if runtime.GOOS != "windows" {
|
||||
// https://code.google.com/p/go/issues/detail?id=8090
|
||||
l.Warnf("Restoring folder modtime: %q: %v", path, err)
|
||||
}
|
||||
} else {
|
||||
changed++
|
||||
if debug {
|
||||
@@ -369,6 +381,29 @@ func (p *puller) handleBlock(b bqBlock) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(b.copy) > 0 && len(b.copy) == len(b.file.Blocks) && b.last {
|
||||
// We are supposed to copy the entire file, and then fetch nothing.
|
||||
// We don't actually need to make the copy.
|
||||
if debug {
|
||||
l.Debugln("taking shortcut:", f)
|
||||
}
|
||||
fp := filepath.Join(p.repoCfg.Directory, f.Name)
|
||||
t := time.Unix(f.Modified, 0)
|
||||
err := os.Chtimes(fp, t, t)
|
||||
if debug && err != nil {
|
||||
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
|
||||
}
|
||||
if !p.repoCfg.IgnorePerms && protocol.HasPermissionBits(f.Flags) {
|
||||
err = os.Chmod(fp, os.FileMode(f.Flags&0777))
|
||||
if debug && err != nil {
|
||||
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
p.model.updateLocal(p.repoCfg.ID, f)
|
||||
return true
|
||||
}
|
||||
|
||||
of, ok := p.openFiles[f.Name]
|
||||
of.done = b.last
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package model
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package model
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -81,17 +85,8 @@ func compareClusterConfig(local, remote protocol.ClusterConfigMessage) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ClusterConfigMismatch(fmt.Errorf("remote is missing repository %q", repo))
|
||||
}
|
||||
}
|
||||
|
||||
for repo := range rm {
|
||||
if _, ok := lm[repo]; !ok {
|
||||
return ClusterConfigMismatch(fmt.Errorf("remote has extra repository %q", repo))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -20,24 +24,6 @@ var testcases = []struct {
|
||||
remote: protocol.ClusterConfigMessage{ClientName: "c", ClientVersion: "d"},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{ClientName: "c", ClientVersion: "d"},
|
||||
err: `remote is missing repository "foo"`,
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{ClientName: "c", ClientVersion: "d"},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
},
|
||||
},
|
||||
err: `remote has extra repository "foo"`,
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
@@ -53,38 +39,6 @@ var testcases = []struct {
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "quux"},
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "bar"},
|
||||
{ID: "quux"},
|
||||
},
|
||||
},
|
||||
err: `remote is missing repository "foo"`,
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "quux"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "bar"},
|
||||
{ID: "foo"},
|
||||
{ID: "quux"},
|
||||
},
|
||||
},
|
||||
err: `remote has extra repository "foo"`,
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package osutil
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package osutil
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package osutil
|
||||
|
||||
import (
|
||||
|
||||
@@ -575,7 +575,7 @@ Message Limits
|
||||
|
||||
An implementation MAY impose reasonable limits on the length of message
|
||||
fields to aid robustness in the face of corruption or broken
|
||||
implementations. These limits, if imposed, SHOULD not be more
|
||||
implementations. These limits, if imposed, SHOULD NOT be more
|
||||
restrictive than the following:
|
||||
|
||||
### Index and Index Update Messages
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
|
||||
17
protocol/debug.go
Normal file
17
protocol/debug.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/calmh/syncthing/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all"
|
||||
l = logger.DefaultLogger
|
||||
)
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package protocol implements the Block Exchange Protocol.
|
||||
package protocol
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import "github.com/calmh/syncthing/xdr"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
type IndexMessage struct {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build darwin
|
||||
|
||||
package protocol
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !windows,!darwin
|
||||
|
||||
package protocol
|
||||
|
||||
@@ -1,24 +1,48 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package protocol
|
||||
|
||||
// Windows uses backslashes as file separator
|
||||
// Windows uses backslashes as file separator and disallows a bunch of
|
||||
// characters in the filename
|
||||
|
||||
import "path/filepath"
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var disallowedCharacters = string([]rune{
|
||||
'<', '>', ':', '"', '|', '?', '*',
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
31,
|
||||
})
|
||||
|
||||
type nativeModel struct {
|
||||
next Model
|
||||
}
|
||||
|
||||
func (m nativeModel) Index(nodeID string, repo string, files []FileInfo) {
|
||||
for i := range files {
|
||||
files[i].Name = filepath.FromSlash(files[i].Name)
|
||||
for i, f := range files {
|
||||
if strings.ContainsAny(f.Name, disallowedCharacters) {
|
||||
files[i].Flags |= FlagInvalid
|
||||
l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
|
||||
}
|
||||
files[i].Name = filepath.FromSlash(f.Name)
|
||||
}
|
||||
m.next.Index(nodeID, repo, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) IndexUpdate(nodeID string, repo string, files []FileInfo) {
|
||||
for i := range files {
|
||||
for i, f := range files {
|
||||
if strings.ContainsAny(f.Name, disallowedCharacters) {
|
||||
files[i].Flags |= FlagInvalid
|
||||
l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
|
||||
}
|
||||
files[i].Name = filepath.FromSlash(files[i].Name)
|
||||
}
|
||||
m.next.IndexUpdate(nodeID, repo, files)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
@@ -92,8 +96,8 @@ type asyncResult struct {
|
||||
}
|
||||
|
||||
const (
|
||||
pingTimeout = 4 * time.Minute
|
||||
pingIdleTime = 5 * time.Minute
|
||||
pingTimeout = 300 * time.Second
|
||||
pingIdleTime = 600 * time.Second
|
||||
)
|
||||
|
||||
func NewConnection(nodeID string, reader io.Reader, writer io.Writer, receiver Model) Connection {
|
||||
@@ -476,11 +480,29 @@ func (c *rawConnection) pingerLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
if d := time.Since(c.xr.LastRead()); d < pingIdleTime {
|
||||
if debug {
|
||||
l.Debugln(c.id, "ping skipped after rd", d)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if d := time.Since(c.xw.LastWrite()); d < pingIdleTime {
|
||||
if debug {
|
||||
l.Debugln(c.id, "ping skipped after wr", d)
|
||||
}
|
||||
continue
|
||||
}
|
||||
go func() {
|
||||
if debug {
|
||||
l.Debugln(c.id, "ping ->")
|
||||
}
|
||||
rc <- c.ping()
|
||||
}()
|
||||
select {
|
||||
case ok := <-rc:
|
||||
if debug {
|
||||
l.Debugln(c.id, "<- pong")
|
||||
}
|
||||
if !ok {
|
||||
c.close(fmt.Errorf("ping failure"))
|
||||
}
|
||||
@@ -505,15 +527,15 @@ func (c *rawConnection) processRequest(msgID int, req RequestMessage) {
|
||||
|
||||
type Statistics struct {
|
||||
At time.Time
|
||||
InBytesTotal int
|
||||
OutBytesTotal int
|
||||
InBytesTotal uint64
|
||||
OutBytesTotal uint64
|
||||
}
|
||||
|
||||
func (c *rawConnection) Statistics() Statistics {
|
||||
return Statistics{
|
||||
At: time.Now(),
|
||||
InBytesTotal: int(c.cr.Tot()),
|
||||
OutBytesTotal: int(c.cw.Tot()),
|
||||
InBytesTotal: c.cr.Tot(),
|
||||
OutBytesTotal: c.cw.Tot(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package scanner implements a file system scanner and hasher.
|
||||
package scanner
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import "fmt"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
@@ -167,7 +171,7 @@ func (w *Walker) walkAndHashFiles(res *[]File, ign map[string][]string) filepath
|
||||
} else {
|
||||
var flags uint32 = protocol.FlagDirectory
|
||||
if w.IgnorePerms {
|
||||
flags |= protocol.FlagNoPermBits
|
||||
flags |= protocol.FlagNoPermBits | 0777
|
||||
} else {
|
||||
flags |= uint32(info.Mode() & os.ModePerm)
|
||||
}
|
||||
@@ -284,10 +288,12 @@ func (w *Walker) ignoreFile(patterns map[string][]string, file string) bool {
|
||||
}
|
||||
|
||||
func checkDir(dir string) error {
|
||||
if info, err := os.Stat(dir); err != nil {
|
||||
if info, err := os.Lstat(dir); err != nil {
|
||||
return err
|
||||
} else if !info.IsDir() {
|
||||
return errors.New(dir + ": not a directory")
|
||||
} else if debug {
|
||||
l.Debugln("checkDir", dir, info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package upnp
|
||||
|
||||
import (
|
||||
|
||||
15
upnp/upnp.go
15
upnp/upnp.go
@@ -1,9 +1,12 @@
|
||||
// Package upnp implements UPnP Internet Gateway upnpDevice port mappings
|
||||
package upnp
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Adapted from https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/IGD.go
|
||||
// Copyright (c) 2010 Jack Palevich (https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/LICENSE)
|
||||
// Copyright (c) 2014 Jakob Borg
|
||||
|
||||
// Package upnp implements UPnP Internet Gateway upnpDevice port mappings
|
||||
package upnp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -60,14 +63,14 @@ func Discover() (*IGD, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
search := []byte(`
|
||||
M-SEARCH * HTTP/1.1
|
||||
searchStr := `M-SEARCH * HTTP/1.1
|
||||
Host: 239.255.255.250:1900
|
||||
St: urn:schemas-upnp-org:device:InternetGatewayDevice:1
|
||||
Man: "ssdp:discover"
|
||||
Mx: 3
|
||||
|
||||
`)
|
||||
`
|
||||
search := []byte(strings.Replace(searchStr, "\n", "\r\n", -1))
|
||||
|
||||
_, err = socket.WriteTo(search, ssdp)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package versioner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package versioner
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package versioner
|
||||
|
||||
type Versioner interface {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package xdr implements an XDR (RFC 4506) encoder/decoder.
|
||||
package xdr
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrElementSizeExceeded = errors.New("element size exceeded")
|
||||
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
tot int
|
||||
err error
|
||||
b [8]byte
|
||||
r io.Reader
|
||||
tot int
|
||||
err error
|
||||
b [8]byte
|
||||
last time.Time
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
@@ -44,6 +50,9 @@ func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
|
||||
if r.err != nil {
|
||||
return nil
|
||||
}
|
||||
r.last = time.Now()
|
||||
s := r.tot
|
||||
|
||||
l := int(r.ReadUint32())
|
||||
if r.err != nil {
|
||||
return nil
|
||||
@@ -52,19 +61,28 @@ func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
|
||||
r.err = ErrElementSizeExceeded
|
||||
return nil
|
||||
}
|
||||
|
||||
if l+pad(l) > len(dst) {
|
||||
dst = make([]byte, l+pad(l))
|
||||
} else {
|
||||
dst = dst[:l+pad(l)]
|
||||
}
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, dst)
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Debugf("@0x%x: rd bytes (%d): %v", s, len(dst), r.err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.tot += n
|
||||
|
||||
if debug {
|
||||
if n > maxDebugBytes {
|
||||
dl.Debugf("rd bytes (%d): %x...", n, dst[:maxDebugBytes])
|
||||
dl.Debugf("@0x%x: rd bytes (%d): %x...", s, len(dst), dst[:maxDebugBytes])
|
||||
} else {
|
||||
dl.Debugf("rd bytes (%d): %x", n, dst)
|
||||
dl.Debugf("@0x%x: rd bytes (%d): %x", s, len(dst), dst)
|
||||
}
|
||||
}
|
||||
return dst[:l]
|
||||
@@ -74,43 +92,74 @@ func (r *Reader) ReadUint16() uint16 {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
_, r.err = io.ReadFull(r.r, r.b[:4])
|
||||
r.tot += 4
|
||||
r.last = time.Now()
|
||||
s := r.tot
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, r.b[:4])
|
||||
r.tot += n
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Debugf("@0x%x: rd uint16: %v", r.tot, r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint16(r.b[1]) | uint16(r.b[0])<<8
|
||||
|
||||
if debug {
|
||||
dl.Debugf("rd uint16=%d", v)
|
||||
dl.Debugf("@0x%x: rd uint16=%d (0x%04x)", s, v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint32() uint32 {
|
||||
var n int
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
r.last = time.Now()
|
||||
s := r.tot
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, r.b[:4])
|
||||
if n < 4 {
|
||||
r.tot += n
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Debugf("@0x%x: rd uint32: %v", r.tot, r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
r.tot += n
|
||||
|
||||
v := uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
|
||||
|
||||
if debug {
|
||||
dl.Debugf("rd uint32=%d", v)
|
||||
dl.Debugf("@0x%x: rd uint32=%d (0x%08x)", s, v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint64() uint64 {
|
||||
var n int
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
r.last = time.Now()
|
||||
s := r.tot
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, r.b[:8])
|
||||
r.tot += n
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Debugf("@0x%x: rd uint64: %v", r.tot, r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
|
||||
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
|
||||
|
||||
if debug {
|
||||
dl.Debugf("rd uint64=%d", v)
|
||||
dl.Debugf("@0x%x: rd uint64=%d (0x%016x)", s, v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -122,3 +171,7 @@ func (r *Reader) Tot() int {
|
||||
func (r *Reader) Error() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *Reader) LastRead() time.Time {
|
||||
return r.last
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
func pad(l int) int {
|
||||
d := l % 4
|
||||
@@ -13,10 +20,11 @@ func pad(l int) int {
|
||||
var padBytes = []byte{0, 0, 0}
|
||||
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
tot int
|
||||
err error
|
||||
b [8]byte
|
||||
w io.Writer
|
||||
tot int
|
||||
err error
|
||||
b [8]byte
|
||||
last time.Time
|
||||
}
|
||||
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
@@ -34,6 +42,7 @@ func (w *Writer) WriteBytes(bs []byte) (int, error) {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
w.last = time.Now()
|
||||
w.WriteUint32(uint32(len(bs)))
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
@@ -65,6 +74,7 @@ func (w *Writer) WriteUint16(v uint16) (int, error) {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
w.last = time.Now()
|
||||
if debug {
|
||||
dl.Debugf("wr uint16=%d", v)
|
||||
}
|
||||
@@ -85,6 +95,7 @@ func (w *Writer) WriteUint32(v uint32) (int, error) {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
w.last = time.Now()
|
||||
if debug {
|
||||
dl.Debugf("wr uint32=%d", v)
|
||||
}
|
||||
@@ -105,6 +116,7 @@ func (w *Writer) WriteUint64(v uint64) (int, error) {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
w.last = time.Now()
|
||||
if debug {
|
||||
dl.Debugf("wr uint64=%d", v)
|
||||
}
|
||||
@@ -131,3 +143,7 @@ func (w *Writer) Tot() int {
|
||||
func (w *Writer) Error() error {
|
||||
return w.err
|
||||
}
|
||||
|
||||
func (w *Writer) LastWrite() time.Time {
|
||||
return w.last
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
|
||||
Reference in New Issue
Block a user