mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-22 02:48:59 +00:00
de0b4270df
### Purpose This PR contains the set of changes needed to make Syncthing work on iOS for [my iOS app for Syncthing](https://github.com/pixelspark/sushitrain). Most changes originate from [the Mobius Sync fork](http://github.com/MobiusSync/syncthing/tree/ios). I have removed the changes from their fork that are not strictly needed for my app (i.e. their changes to the GUI and command line utilities, for instance) and squashed it all in a single commit. In summary, the changes are: * Resolve non-absolute paths to the 'Documents' folder (basically the only one an app can/should write user data to by default on iOS) * Tweaking of build flags/conditions for iOS (i.e. determine which basicfs_watch, ignoreresult variant to build for iOS) * Disable upgrade mechanism on iOS * Make `RequestGlobal` and `PullerProgress` public symbols * Expose syncthing.app's Model instance (app.M) * Add no-op stub for SetLowPriority on iOS I would very much appreciate these changes to be (eventually) merged to mainline syncthing, as this would allow my iOS app to track the mainline source code directly and removes the need (for me at least) for maintaining a separate fork. Perhaps the Mobius folks can also benefit from this (although as noted this branch does not contain their changes to e.g. the GUI). ### Testing This branch has been tested with the iOS app and appears to work fine. The full set of MobiusSync changes has been used before with success. ### Screenshots n/a ### Documentation There should be no visible changes for users due to this set of changes. --------- Co-authored-by: Simon Pickup <simon@pickupinfinity.com>
303 lines
9.1 KiB
Go
303 lines
9.1 KiB
Go
// Copyright (C) 2019 The Syncthing Authors.
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
package locations
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/syncthing/syncthing/lib/build"
|
|
"github.com/syncthing/syncthing/lib/fs"
|
|
)
|
|
|
|
type LocationEnum string
|
|
|
|
// Use strings as keys to make printout and serialization of the locations map
|
|
// more meaningful.
|
|
const (
|
|
ConfigFile LocationEnum = "config"
|
|
CertFile LocationEnum = "certFile"
|
|
KeyFile LocationEnum = "keyFile"
|
|
HTTPSCertFile LocationEnum = "httpsCertFile"
|
|
HTTPSKeyFile LocationEnum = "httpsKeyFile"
|
|
Database LocationEnum = "database"
|
|
LogFile LocationEnum = "logFile"
|
|
PanicLog LocationEnum = "panicLog"
|
|
AuditLog LocationEnum = "auditLog"
|
|
GUIAssets LocationEnum = "guiAssets"
|
|
DefFolder LocationEnum = "defFolder"
|
|
)
|
|
|
|
type BaseDirEnum string
|
|
|
|
const (
|
|
// Overridden by --home flag, $STHOMEDIR, --config flag, or $STCONFDIR
|
|
ConfigBaseDir BaseDirEnum = "config"
|
|
// Overridden by --home flag, $STHOMEDIR, --data flag, or $STDATADIR
|
|
DataBaseDir BaseDirEnum = "data"
|
|
|
|
// User's home directory, *not* --home flag
|
|
UserHomeBaseDir BaseDirEnum = "userHome"
|
|
|
|
LevelDBDir = "index-v0.14.0.db"
|
|
configFileName = "config.xml"
|
|
defaultStateDir = ".local/state/syncthing"
|
|
oldDefaultConfigDir = ".config/syncthing"
|
|
)
|
|
|
|
// Platform dependent directories
|
|
var baseDirs = make(map[BaseDirEnum]string, 3)
|
|
|
|
func init() {
|
|
userHome := userHomeDir()
|
|
config := defaultConfigDir(userHome)
|
|
data := defaultDataDir(userHome, config)
|
|
|
|
baseDirs[UserHomeBaseDir] = userHome
|
|
baseDirs[ConfigBaseDir] = config
|
|
baseDirs[DataBaseDir] = data
|
|
|
|
if err := expandLocations(); err != nil {
|
|
fmt.Println(err)
|
|
panic("Failed to expand locations at init time")
|
|
}
|
|
}
|
|
|
|
// Set overrides a location to the given path, making sure to it points to an
|
|
// absolute path first. Only the special "-" value will be used verbatim.
|
|
func Set(locationName LocationEnum, path string) error {
|
|
if !filepath.IsAbs(path) && path != "-" {
|
|
var err error
|
|
path, err = filepath.Abs(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_, ok := locationTemplates[locationName]
|
|
if !ok {
|
|
return fmt.Errorf("unknown location: %s", locationName)
|
|
}
|
|
locations[locationName] = filepath.Clean(path)
|
|
return nil
|
|
}
|
|
|
|
func SetBaseDir(baseDirName BaseDirEnum, path string) error {
|
|
if !filepath.IsAbs(path) {
|
|
var err error
|
|
path, err = filepath.Abs(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, ok := baseDirs[baseDirName]; !ok {
|
|
return fmt.Errorf("unknown base dir: %s", baseDirName)
|
|
}
|
|
baseDirs[baseDirName] = filepath.Clean(path)
|
|
return expandLocations()
|
|
}
|
|
|
|
func Get(location LocationEnum) string {
|
|
return locations[location]
|
|
}
|
|
|
|
func GetBaseDir(baseDir BaseDirEnum) string {
|
|
return baseDirs[baseDir]
|
|
}
|
|
|
|
// Use the variables from baseDirs here
|
|
var locationTemplates = map[LocationEnum]string{
|
|
ConfigFile: "${config}/config.xml",
|
|
CertFile: "${config}/cert.pem",
|
|
KeyFile: "${config}/key.pem",
|
|
HTTPSCertFile: "${config}/https-cert.pem",
|
|
HTTPSKeyFile: "${config}/https-key.pem",
|
|
Database: "${data}/" + LevelDBDir,
|
|
LogFile: "${data}/syncthing.log", // --logfile on Windows
|
|
PanicLog: "${data}/panic-%{timestamp}.log",
|
|
AuditLog: "${data}/audit-%{timestamp}.log",
|
|
GUIAssets: "${config}/gui",
|
|
DefFolder: "${userHome}/Sync",
|
|
}
|
|
|
|
var locations = make(map[LocationEnum]string)
|
|
|
|
// expandLocations replaces the variables in the locations map with actual
|
|
// directory locations.
|
|
func expandLocations() error {
|
|
newLocations := make(map[LocationEnum]string)
|
|
for key, dir := range locationTemplates {
|
|
dir = os.Expand(dir, func(s string) string {
|
|
return baseDirs[BaseDirEnum(s)]
|
|
})
|
|
var err error
|
|
dir, err = fs.ExpandTilde(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newLocations[key] = filepath.Clean(dir)
|
|
}
|
|
locations = newLocations
|
|
return nil
|
|
}
|
|
|
|
// ListExpandedPaths returns a machine-readable mapping of the currently configured locations.
|
|
func ListExpandedPaths() map[string]string {
|
|
res := make(map[string]string, len(locations))
|
|
for key, path := range baseDirs {
|
|
res["baseDir-"+string(key)] = path
|
|
}
|
|
for key, path := range locations {
|
|
res[string(key)] = path
|
|
}
|
|
return res
|
|
}
|
|
|
|
// PrettyPaths returns a nicely formatted, human-readable listing
|
|
func PrettyPaths() string {
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "Configuration file:\n\t%s\n\n", Get(ConfigFile))
|
|
fmt.Fprintf(&b, "Device private key & certificate files:\n\t%s\n\t%s\n\n", Get(KeyFile), Get(CertFile))
|
|
fmt.Fprintf(&b, "GUI / API HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", Get(HTTPSKeyFile), Get(HTTPSCertFile))
|
|
fmt.Fprintf(&b, "Database location:\n\t%s\n\n", Get(Database))
|
|
fmt.Fprintf(&b, "Log file:\n\t%s\n\n", Get(LogFile))
|
|
fmt.Fprintf(&b, "GUI override directory:\n\t%s\n\n", Get(GUIAssets))
|
|
fmt.Fprintf(&b, "Default sync folder directory:\n\t%s\n\n", Get(DefFolder))
|
|
return b.String()
|
|
}
|
|
|
|
// defaultConfigDir returns the default configuration directory, as figured
|
|
// out by various the environment variables present on each platform, or dies
|
|
// trying.
|
|
func defaultConfigDir(userHome string) string {
|
|
switch {
|
|
case build.IsWindows:
|
|
return windowsConfigDataDir()
|
|
|
|
case build.IsDarwin, build.IsIOS:
|
|
return darwinConfigDataDir(userHome)
|
|
|
|
default:
|
|
return unixConfigDir(userHome, os.Getenv("XDG_CONFIG_HOME"), os.Getenv("XDG_STATE_HOME"), fileExists)
|
|
}
|
|
}
|
|
|
|
// defaultDataDir returns the default data directory, where we store the
|
|
// database, log files, etc.
|
|
func defaultDataDir(userHome, configDir string) string {
|
|
if build.IsWindows || build.IsDarwin || build.IsIOS {
|
|
return configDir
|
|
}
|
|
|
|
return unixDataDir(userHome, configDir, os.Getenv("XDG_DATA_HOME"), os.Getenv("XDG_STATE_HOME"), fileExists)
|
|
}
|
|
|
|
func windowsConfigDataDir() string {
|
|
if p := os.Getenv("LocalAppData"); p != "" {
|
|
return filepath.Join(p, "Syncthing")
|
|
}
|
|
return filepath.Join(os.Getenv("AppData"), "Syncthing")
|
|
}
|
|
|
|
func darwinConfigDataDir(userHome string) string {
|
|
return filepath.Join(userHome, "Library/Application Support/Syncthing")
|
|
}
|
|
|
|
func unixConfigDir(userHome, xdgConfigHome, xdgStateHome string, fileExists func(string) bool) string {
|
|
// Legacy: if our config exists under $XDG_CONFIG_HOME/syncthing,
|
|
// use that. The variable should be set to an absolute path or be
|
|
// ignored, but that's not what we did previously, so we retain the
|
|
// old behavior.
|
|
if xdgConfigHome != "" {
|
|
candidate := filepath.Join(xdgConfigHome, "syncthing")
|
|
if fileExists(filepath.Join(candidate, configFileName)) {
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
// Legacy: if our config exists under ~/.config/syncthing, use that
|
|
candidate := filepath.Join(userHome, oldDefaultConfigDir)
|
|
if fileExists(filepath.Join(candidate, configFileName)) {
|
|
return candidate
|
|
}
|
|
|
|
// If XDG_STATE_HOME is set to an absolute path, use that
|
|
if filepath.IsAbs(xdgStateHome) {
|
|
return filepath.Join(xdgStateHome, "syncthing")
|
|
}
|
|
|
|
// Use our default
|
|
return filepath.Join(userHome, defaultStateDir)
|
|
}
|
|
|
|
// unixDataDir returns the default data directory, where we store the
|
|
// database, log files, etc, on Unix-like systems.
|
|
func unixDataDir(userHome, configDir, xdgDataHome, xdgStateHome string, fileExists func(string) bool) string {
|
|
// If a database exists at the config location, use that. This is the
|
|
// most common case for both legacy (~/.config/syncthing) and current
|
|
// (~/.local/state/syncthing) setups.
|
|
if fileExists(filepath.Join(configDir, LevelDBDir)) {
|
|
return configDir
|
|
}
|
|
|
|
// Legacy: if a database exists under $XDG_DATA_HOME/syncthing, use
|
|
// that. The variable should be set to an absolute path or be ignored,
|
|
// but that's not what we did previously, so we retain the old behavior.
|
|
if xdgDataHome != "" {
|
|
candidate := filepath.Join(xdgDataHome, "syncthing")
|
|
if fileExists(filepath.Join(candidate, LevelDBDir)) {
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
// Legacy: if a database exists under ~/.config/syncthing, use that
|
|
candidate := filepath.Join(userHome, oldDefaultConfigDir)
|
|
if fileExists(filepath.Join(candidate, LevelDBDir)) {
|
|
return candidate
|
|
}
|
|
|
|
// If XDG_STATE_HOME is set to an absolute path, use that
|
|
if filepath.IsAbs(xdgStateHome) {
|
|
return filepath.Join(xdgStateHome, "syncthing")
|
|
}
|
|
|
|
// Use our default
|
|
return filepath.Join(userHome, defaultStateDir)
|
|
}
|
|
|
|
// userHomeDir returns the user's home directory, or dies trying.
|
|
func userHomeDir() string {
|
|
userHome, err := fs.ExpandTilde("~")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
panic("Failed to get user home dir")
|
|
}
|
|
return userHome
|
|
}
|
|
|
|
func GetTimestamped(key LocationEnum) string {
|
|
return getTimestampedAt(key, time.Now())
|
|
}
|
|
|
|
func getTimestampedAt(key LocationEnum, when time.Time) string {
|
|
// We take the roundtrip via "%{timestamp}" instead of passing the path
|
|
// directly through time.Format() to avoid issues when the path we are
|
|
// expanding contains numbers; otherwise for example
|
|
// /home/user2006/.../panic-20060102-150405.log would get both instances of
|
|
// 2006 replaced by 2015...
|
|
tpl := locations[key]
|
|
timestamp := when.Format("20060102-150405")
|
|
return strings.ReplaceAll(tpl, "%{timestamp}", timestamp)
|
|
}
|
|
|
|
func fileExists(path string) bool {
|
|
_, err := os.Lstat(path)
|
|
return err == nil
|
|
}
|