Example #1
1
func withDetailsMiddleware(id protocol.DeviceID, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Syncthing-Version", Version)
		w.Header().Set("X-Syncthing-ID", id.String())
		h.ServeHTTP(w, r)
	})
}
Example #2
0
// requires write lock on model.fmut before entry
// requires file does not exist in local model
func (m *Model) addToLocalModel(deviceID protocol.DeviceID, folder string, file protocol.FileInfo) {
	if file.IsDeleted() {
		if debug {
			l.Debugln("peer", deviceID.String(), "has deleted file, doing nothing", file.Name)
		}
		return
	}
	if file.IsInvalid() {
		if debug {
			l.Debugln("peer", deviceID.String(), "has invalid file, doing nothing", file.Name)
		}
		return
	}
	if file.IsSymlink() {
		if debug {
			l.Debugln("peer", deviceID.String(), "has symlink, doing nothing", file.Name)
		}
		return
	}

	if debug && file.IsDirectory() {
		l.Debugln("peer", deviceID.String(), "has directory, adding", file.Name)
	} else if debug {
		l.Debugln("peer", deviceID.String(), "has file, adding", file.Name)
	}

	m.treeCaches[folder].AddEntry(file)

	m.addPeerForEntry(deviceID, folder, file)
}
Example #3
0
// Lookup returns the list of addresses where the given device is available;
// direct, and via relays.
func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) {
	qURL, err := url.Parse(c.server)
	if err != nil {
		return nil, nil, err
	}

	q := qURL.Query()
	q.Set("device", device.String())
	qURL.RawQuery = q.Encode()

	resp, err := c.queryClient.Get(qURL.String())
	if err != nil {
		if debug {
			l.Debugln("globalClient.Lookup", qURL.String(), err)
		}
		return nil, nil, err
	}
	if resp.StatusCode != 200 {
		resp.Body.Close()
		if debug {
			l.Debugln("globalClient.Lookup", qURL.String(), resp.Status)
		}
		return nil, nil, errors.New(resp.Status)
	}

	// TODO: Handle 429 and Retry-After?

	var ann announcement
	err = json.NewDecoder(resp.Body).Decode(&ann)
	resp.Body.Close()
	return ann.Direct, ann.Relays, err
}
Example #4
0
// Lookup returns the list of addresses where the given device is available;
// direct, and via relays.
func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) {
	qURL, err := url.Parse(c.server)
	if err != nil {
		return nil, nil, err
	}

	q := qURL.Query()
	q.Set("device", device.String())
	qURL.RawQuery = q.Encode()

	resp, err := c.queryClient.Get(qURL.String())
	if err != nil {
		l.Debugln("globalClient.Lookup", qURL, err)
		return nil, nil, err
	}
	if resp.StatusCode != 200 {
		resp.Body.Close()
		l.Debugln("globalClient.Lookup", qURL, resp.Status)
		err := errors.New(resp.Status)
		if secs, atoiErr := strconv.Atoi(resp.Header.Get("Retry-After")); atoiErr == nil && secs > 0 {
			err = lookupError{
				error:    err,
				cacheFor: time.Duration(secs) * time.Second,
			}
		}
		return nil, nil, err
	}

	var ann announcement
	err = json.NewDecoder(resp.Body).Decode(&ann)
	resp.Body.Close()
	return ann.Direct, ann.Relays, err
}
Example #5
0
func (s *querysrv) getDeviceSeen(device protocol.DeviceID) (time.Time, error) {
	row := s.prep["selectDevice"].QueryRow(device.String())
	var seen time.Time
	if err := row.Scan(&seen); err != nil {
		return time.Time{}, err
	}
	return seen.In(time.UTC), nil
}
Example #6
0
func (c *localClient) registerDevice(src net.Addr, device Device) bool {
	var id protocol.DeviceID
	copy(id[:], device.ID)

	// Remember whether we already had a valid cache entry for this device.

	ce, existsAlready := c.Get(id)
	isNewDevice := !existsAlready || time.Since(ce.when) > CacheLifeTime

	// Any empty or unspecified addresses should be set to the source address
	// of the announcement. We also skip any addresses we can't parse.

	l.Debugln("discover: Registering addresses for", id)
	var validAddresses []string
	for _, addr := range device.Addresses {
		u, err := url.Parse(addr.URL)
		if err != nil {
			continue
		}

		tcpAddr, err := net.ResolveTCPAddr("tcp", u.Host)
		if err != nil {
			continue
		}

		if len(tcpAddr.IP) == 0 || tcpAddr.IP.IsUnspecified() {
			host, _, err := net.SplitHostPort(src.String())
			if err != nil {
				continue
			}
			u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
			l.Debugf("discover: Reconstructed URL is %#v", u)
			validAddresses = append(validAddresses, u.String())
			l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr.URL, u.String())
		} else {
			validAddresses = append(validAddresses, addr.URL)
			l.Debugf("discover: Accepted address %s verbatim", addr.URL)
		}
	}

	c.Set(id, CacheEntry{
		Direct: validAddresses,
		Relays: device.Relays,
		when:   time.Now(),
		found:  true,
	})

	if isNewDevice {
		events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
			"device": id.String(),
			"addrs":  validAddresses,
			"relays": device.Relays,
		})
	}

	return isNewDevice
}
Example #7
0
func (d *FileTreeCache) AddEntry(entry protocol.FileInfo, peer protocol.DeviceID) {
	d.db.Update(func(tx *bolt.Tx) error {
		eb := tx.Bucket(d.folderBucketKey).Bucket(entriesBucket)

		/* save entry */
		var buf bytes.Buffer
		enc := gob.NewEncoder(&buf)
		enc.Encode(entry)
		eb.Put([]byte(entry.Name), buf.Bytes()) // TODO handle error?

		/* add peer */
		edb := tx.Bucket(d.folderBucketKey).Bucket(entryDevicesBucket)
		v := edb.Get([]byte(entry.Name))
		var devices map[string]bool
		if v == nil {
			devices = make(map[string]bool)
		} else {
			rbuf := bytes.NewBuffer(v)
			dec := gob.NewDecoder(rbuf)
			dec.Decode(&devices)
		}
		devices[peer.String()] = true
		var dbuf bytes.Buffer
		enc = gob.NewEncoder(&dbuf)
		enc.Encode(devices)
		edb.Put([]byte(entry.Name), dbuf.Bytes())

		/* add child lookup */
		dir := path.Dir(entry.Name)
		clb := tx.Bucket(d.folderBucketKey).Bucket(childLookupBucket)
		v = clb.Get([]byte(dir))
		if debug {
			l.Debugln("Adding child", entry.Name, "for dir", dir)
		}

		var children map[string]bool
		if v == nil {
			children = make(map[string]bool)
		} else {
			rbuf := bytes.NewBuffer(v)
			dec := gob.NewDecoder(rbuf)
			dec.Decode(&children)
		}
		children[entry.Name] = true

		var cbuf bytes.Buffer
		enc = gob.NewEncoder(&cbuf)
		enc.Encode(children)
		clb.Put([]byte(dir), cbuf.Bytes())

		return nil
	})
}
Example #8
0
func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
	var cfg Configuration

	cfg.MyID = myID.String()
	setDefaults(&cfg)
	setDefaults(&cfg.GUI)
	setDefaults(&cfg.Options)

	err := xml.NewDecoder(r).Decode(&cfg)

	cfg.prepare()

	return cfg, err
}
Example #9
0
func (s *querysrv) updateAddress(ctx context.Context, tx *sql.Tx, device protocol.DeviceID, uri string) error {
	res, err := tx.Stmt(s.prep["updateAddress"]).Exec(device.String(), uri)
	if err != nil {
		return err
	}

	if rows, _ := res.RowsAffected(); rows == 0 {
		_, err := tx.Stmt(s.prep["insertAddress"]).Exec(device.String(), uri)
		if err != nil {
			return err
		}
	}

	return nil
}
Example #10
0
// An index update was received from the peer device
func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {
	if debug {
		l.Debugln("model: receiving index update from device", deviceID.String()[:5], "for folder", folder)
	}

	m.fmut.Lock()
	defer m.fmut.Unlock()
	m.lmut.L.Lock()
	defer m.lmut.L.Unlock()

	if false == m.isFolderSharedWithDevice(folder, deviceID) {
		if debug {
			l.Debugln("model:", deviceID.String()[:5], "not shared with folder", folder, "so ignoring")
		}
		return
	}

	m.updateIndex(deviceID, folder, files)
}
Example #11
0
func setup(deviceID protocol.DeviceID, dir string, peers ...protocol.DeviceID) (*config.Wrapper, *bolt.DB, string) {
	configFile, _ := ioutil.TempFile(dir, "config")
	realCfg := config.New(deviceID, deviceID.String()[:5])
	cfg := config.Wrap(configFile.Name(), realCfg)

	databasePath := path.Join(path.Dir(cfg.ConfigPath()), "boltdb")
	database, _ := bolt.Open(databasePath, 0600, nil)

	folder := "syncthingfusetest"
	folderCfg := config.FolderConfiguration{
		ID:        folder,
		CacheSize: "1MiB",
		Devices:   make([]stconfig.FolderDeviceConfiguration, len(peers)),
	}
	for i, peer := range peers {
		folderCfg.Devices[i] = stconfig.FolderDeviceConfiguration{DeviceID: peer}
	}
	cfg.SetFolder(folderCfg)

	return cfg, database, folder
}
Example #12
0
func (s *querysrv) getAddresses(ctx context.Context, device protocol.DeviceID) ([]string, error) {
	rows, err := s.prep["selectAddress"].Query(device.String())
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var res []string
	for rows.Next() {
		var addr string

		err := rows.Scan(&addr)
		if err != nil {
			log.Println("Scan:", err)
			continue
		}
		res = append(res, addr)
	}

	return res, nil
}
Example #13
0
func New(myID protocol.DeviceID, myName string) Configuration {
	var cfg Configuration
	cfg.Version = CurrentVersion

	cfg.MyID = myID.String()
	setDefaults(&cfg)
	setDefaults(&cfg.GUI)
	setDefaults(&cfg.Options)

	thisDevice, _ := protocol.DeviceIDFromString(cfg.MyID)
	thisDeviceCfg := config.NewDeviceConfiguration(thisDevice, myName)
	thisDeviceCfg.Addresses = []string{"dynamic"}
	cfg.Folders = []FolderConfiguration{}
	cfg.Devices = []config.DeviceConfiguration{thisDeviceCfg}

	cfg.prepare()

	usr, _ := user.Current()
	cfg.MountPoint = path.Join(usr.HomeDir, "SyncthingFUSE")

	return cfg
}
Example #14
0
func (s *querysrv) updateDevice(ctx context.Context, tx *sql.Tx, device protocol.DeviceID) error {
	reqID := ctx.Value("id").(requestID)
	t0 := time.Now()
	res, err := tx.Stmt(s.prep["updateDevice"]).Exec(device.String())
	if err != nil {
		return err
	}
	if debug {
		log.Println(reqID, "updateDevice in", time.Since(t0))
	}

	if rows, _ := res.RowsAffected(); rows == 0 {
		t0 = time.Now()
		_, err := tx.Stmt(s.prep["insertDevice"]).Exec(device.String())
		if err != nil {
			return err
		}
		if debug {
			log.Println(reqID, "insertDevice in", time.Since(t0))
		}
	}

	return nil
}
Example #15
0
func (m *Model) updateIndex(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {
	m.fmut.Lock()

	treeCache, ok := m.treeCaches[folder]
	if !ok {
		if debug {
			l.Debugln("folder", folder, "from", deviceID.String(), "not configured, skipping")
		}
		m.fmut.Unlock()
		return
	}

	for _, file := range files {
		entry, existsInLocalModel := treeCache.GetEntry(file.Name)

		if !existsInLocalModel {
			if debug {
				l.Debugln("file", file.Name, "from", deviceID.String(), "does not exist in local model, trying to add")
			}
			m.addToLocalModel(deviceID, folder, file)
			continue
		}

		localToGlobal := entry.Version.Compare(file.Version)
		if localToGlobal == protocol.Equal {
			if debug {
				l.Debugln("peer", deviceID.String(), "has same version for file", file.Name, ", adding as peer.")
			}
			m.addPeerForEntry(deviceID, folder, file)
			continue
		}

		if localToGlobal == protocol.Lesser || localToGlobal == protocol.ConcurrentLesser {
			if debug {
				l.Debugln("peer", deviceID.String(), "has new version for file", file.Name, ", replacing current data.")
			}
			m.removeEntryFromLocalModel(folder, file.Name)
			m.addToLocalModel(deviceID, folder, file)
			// TODO probably want to re-fill disk cache if file small enough (10MB?) or within certain size (5%?)
			continue
		}
	}

	m.fmut.Unlock()
}
Example #16
0
func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
	var id protocol.DeviceID
	copy(id[:], device.ID)

	// Remember whether we already had a valid cache entry for this device.
	// If the instance ID has changed the remote device has restarted since
	// we last heard from it, so we should treat it as a new device.

	ce, existsAlready := c.Get(id)
	isNewDevice := !existsAlready || time.Since(ce.when) > CacheLifeTime || ce.instanceID != device.InstanceID

	// Any empty or unspecified addresses should be set to the source address
	// of the announcement. We also skip any addresses we can't parse.

	l.Debugln("discover: Registering addresses for", id)
	var validAddresses []string
	for _, addr := range device.Addresses {
		u, err := url.Parse(addr)
		if err != nil {
			continue
		}

		tcpAddr, err := net.ResolveTCPAddr("tcp", u.Host)
		if err != nil {
			continue
		}

		if len(tcpAddr.IP) == 0 || tcpAddr.IP.IsUnspecified() {
			srcAddr, err := net.ResolveTCPAddr("tcp", src.String())
			if err != nil {
				continue
			}

			// Do not use IPv6 source address if requested scheme is tcp4
			if u.Scheme == "tcp4" && srcAddr.IP.To4() == nil {
				continue
			}

			// Do not use IPv4 source address if requested scheme is tcp6
			if u.Scheme == "tcp6" && srcAddr.IP.To4() != nil {
				continue
			}

			host, _, err := net.SplitHostPort(src.String())
			if err != nil {
				continue
			}
			u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
			l.Debugf("discover: Reconstructed URL is %#v", u)
			validAddresses = append(validAddresses, u.String())
			l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr, u.String())
		} else {
			validAddresses = append(validAddresses, addr)
			l.Debugf("discover: Accepted address %s verbatim", addr)
		}
	}

	c.Set(id, CacheEntry{
		Addresses:  validAddresses,
		when:       time.Now(),
		found:      true,
		instanceID: device.InstanceID,
	})

	if isNewDevice {
		events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
			"device": id.String(),
			"addrs":  validAddresses,
		})
	}

	return isNewDevice
}
Example #17
0
// requires write locks on fmut and lmut before entry
func (m *Model) updateIndex(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {
	treeCache, ok := m.treeCaches[folder]
	if !ok {
		if debug {
			l.Debugln("folder", folder, "from", deviceID.String()[:5], "tree not configured, skipping")
		}
		return
	}
	fbc, ok := m.blockCaches[folder]
	if !ok {
		if debug {
			l.Debugln("folder", folder, "from", deviceID.String()[:5], "block not configured, skipping")
		}
		return
	}

	for _, file := range files {
		entry, existsInLocalModel := treeCache.GetEntry(file.Name)

		var globalToLocal protocol.Ordering
		if existsInLocalModel {
			globalToLocal = file.Version.Compare(entry.Version)
		}

		if debug {
			l.Debugln("updating entry for", file.Name, "from", deviceID.String()[:5], existsInLocalModel, globalToLocal)
		}

		// remove if necessary
		if existsInLocalModel && (globalToLocal == protocol.Greater || (file.Version.Concurrent(entry.Version) && file.WinsConflict(entry))) {
			if debug {
				l.Debugln("remove entry for", file.Name, "from", deviceID.String()[:5])
			}

			treeCache.RemoveEntry(file.Name)

			if m.isFilePinned(folder, file.Name) {
				for _, block := range entry.Blocks {
					fbc.UnpinBlock(block.Hash)
				}
			}
		}

		// add if necessary
		if !existsInLocalModel || (globalToLocal == protocol.Greater || (file.Version.Concurrent(entry.Version) && file.WinsConflict(entry))) || (globalToLocal == protocol.Equal) {
			if file.IsDeleted() {
				if debug {
					l.Debugln("peer", deviceID.String()[:5], "has deleted file, doing nothing", file.Name)
				}
				continue
			}
			if file.IsInvalid() {
				if debug {
					l.Debugln("peer", deviceID.String()[:5], "has invalid file, doing nothing", file.Name)
				}
				continue
			}
			if file.IsSymlink() {
				if debug {
					l.Debugln("peer", deviceID.String()[:5], "has symlink, doing nothing", file.Name)
				}
				continue
			}

			if debug && file.IsDirectory() {
				l.Debugln("add directory", file.Name, "from", deviceID.String()[:5])
			} else if debug {
				l.Debugln("add file", file.Name, "from", deviceID.String()[:5])
			}

			treeCache.AddEntry(file, deviceID)

			// trigger pull on unsatisfied blocks for pinned files
			if m.isFilePinned(folder, file.Name) {
				for i, block := range file.Blocks {
					if false == fbc.HasPinnedBlock(block.Hash) {
						blockStart := int64(i * protocol.BlockSize)
						status := m.getOrCreatePullStatus("Pin fetch", folder, file.Name, block, blockStart, queued)
						m.pinnedList.PushBack(status)
					}
				}
			}
		}
	}

	m.lmut.Broadcast()
}
Example #18
0
// A cluster configuration message was received
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfig) {
	if debug {
		l.Debugln("model: receiving cluster config from device", deviceID.String()[:5])
	}
}
Example #19
0
func (p *Process) ResumeDevice(dev protocol.DeviceID) error {
	_, err := p.Post("/rest/system/resume?device="+dev.String(), nil)
	return err
}