github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/config/config.go (about)

     1  // Package config reads, writes and edits the config file and deals with command line flags
     2  package config
     3  
     4  import (
     5  	"bufio"
     6  	"bytes"
     7  	"crypto/rand"
     8  	"crypto/sha256"
     9  	"encoding/base64"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"log"
    15  	mathrand "math/rand"
    16  	"os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"regexp"
    20  	"runtime"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  	"unicode/utf8"
    26  
    27  	"github.com/Unknwon/goconfig"
    28  	homedir "github.com/mitchellh/go-homedir"
    29  	"github.com/pkg/errors"
    30  	"github.com/rclone/rclone/fs"
    31  	"github.com/rclone/rclone/fs/accounting"
    32  	"github.com/rclone/rclone/fs/config/configmap"
    33  	"github.com/rclone/rclone/fs/config/configstruct"
    34  	"github.com/rclone/rclone/fs/config/obscure"
    35  	"github.com/rclone/rclone/fs/driveletter"
    36  	"github.com/rclone/rclone/fs/fshttp"
    37  	"github.com/rclone/rclone/fs/fspath"
    38  	"github.com/rclone/rclone/fs/rc"
    39  	"github.com/rclone/rclone/lib/random"
    40  	"github.com/rclone/rclone/lib/terminal"
    41  	"golang.org/x/crypto/nacl/secretbox"
    42  	"golang.org/x/text/unicode/norm"
    43  )
    44  
    45  const (
    46  	configFileName       = "rclone.conf"
    47  	hiddenConfigFileName = "." + configFileName
    48  
    49  	// ConfigToken is the key used to store the token under
    50  	ConfigToken = "token"
    51  
    52  	// ConfigClientID is the config key used to store the client id
    53  	ConfigClientID = "client_id"
    54  
    55  	// ConfigClientSecret is the config key used to store the client secret
    56  	ConfigClientSecret = "client_secret"
    57  
    58  	// ConfigAuthURL is the config key used to store the auth server endpoint
    59  	ConfigAuthURL = "auth_url"
    60  
    61  	// ConfigTokenURL is the config key used to store the token server endpoint
    62  	ConfigTokenURL = "token_url"
    63  
    64  	// ConfigEncoding is the config key to change the encoding for a backend
    65  	ConfigEncoding = "encoding"
    66  
    67  	// ConfigEncodingHelp is the help for ConfigEncoding
    68  	ConfigEncodingHelp = "This sets the encoding for the backend.\n\nSee: the [encoding section in the overview](/overview/#encoding) for more info."
    69  
    70  	// ConfigAuthorize indicates that we just want "rclone authorize"
    71  	ConfigAuthorize = "config_authorize"
    72  
    73  	// ConfigAuthNoBrowser indicates that we do not want to open browser
    74  	ConfigAuthNoBrowser = "config_auth_no_browser"
    75  )
    76  
    77  // Global
    78  var (
    79  	// configFile is the global config data structure. Don't read it directly, use getConfigData()
    80  	configFile *goconfig.ConfigFile
    81  
    82  	// ConfigPath points to the config file
    83  	ConfigPath = makeConfigPath()
    84  
    85  	// CacheDir points to the cache directory.  Users of this
    86  	// should make a subdirectory and use MkdirAll() to create it
    87  	// and any parents.
    88  	CacheDir = makeCacheDir()
    89  
    90  	// Key to use for password en/decryption.
    91  	// When nil, no encryption will be used for saving.
    92  	configKey []byte
    93  
    94  	// output of prompt for password
    95  	PasswordPromptOutput = os.Stderr
    96  
    97  	// If set to true, the configKey is obscured with obscure.Obscure and saved to a temp file when it is
    98  	// calculated from the password. The path of that temp file is then written to the environment variable
    99  	// `_RCLONE_CONFIG_KEY_FILE`. If `_RCLONE_CONFIG_KEY_FILE` is present, password prompt is skipped and `RCLONE_CONFIG_PASS` ignored.
   100  	// For security reasons, the temp file is deleted once the configKey is successfully loaded.
   101  	// This can be used to pass the configKey to a child process.
   102  	PassConfigKeyForDaemonization = false
   103  
   104  	// Password can be used to configure the random password generator
   105  	Password = random.Password
   106  )
   107  
   108  func init() {
   109  	// Set the function pointers up in fs
   110  	fs.ConfigFileGet = FileGetFlag
   111  	fs.ConfigFileSet = SetValueAndSave
   112  }
   113  
   114  func getConfigData() *goconfig.ConfigFile {
   115  	if configFile == nil {
   116  		LoadConfig()
   117  	}
   118  	return configFile
   119  }
   120  
   121  // Return the path to the configuration file
   122  func makeConfigPath() string {
   123  
   124  	// Use rclone.conf from rclone executable directory if already existing
   125  	exe, err := os.Executable()
   126  	if err == nil {
   127  		exedir := filepath.Dir(exe)
   128  		cfgpath := filepath.Join(exedir, configFileName)
   129  		_, err := os.Stat(cfgpath)
   130  		if err == nil {
   131  			return cfgpath
   132  		}
   133  	}
   134  
   135  	// Find user's home directory
   136  	homeDir, err := homedir.Dir()
   137  
   138  	// Find user's configuration directory.
   139  	// Prefer XDG config path, with fallback to $HOME/.config.
   140  	// See XDG Base Directory specification
   141  	// https://specifications.freedesktop.org/basedir-spec/latest/),
   142  	xdgdir := os.Getenv("XDG_CONFIG_HOME")
   143  	var cfgdir string
   144  	if xdgdir != "" {
   145  		// User's configuration directory for rclone is $XDG_CONFIG_HOME/rclone
   146  		cfgdir = filepath.Join(xdgdir, "rclone")
   147  	} else if homeDir != "" {
   148  		// User's configuration directory for rclone is $HOME/.config/rclone
   149  		cfgdir = filepath.Join(homeDir, ".config", "rclone")
   150  	}
   151  
   152  	// Use rclone.conf from user's configuration directory if already existing
   153  	var cfgpath string
   154  	if cfgdir != "" {
   155  		cfgpath = filepath.Join(cfgdir, configFileName)
   156  		_, err := os.Stat(cfgpath)
   157  		if err == nil {
   158  			return cfgpath
   159  		}
   160  	}
   161  
   162  	// Use .rclone.conf from user's home directory if already existing
   163  	var homeconf string
   164  	if homeDir != "" {
   165  		homeconf = filepath.Join(homeDir, hiddenConfigFileName)
   166  		_, err := os.Stat(homeconf)
   167  		if err == nil {
   168  			return homeconf
   169  		}
   170  	}
   171  
   172  	// Check to see if user supplied a --config variable or environment
   173  	// variable.  We can't use pflag for this because it isn't initialised
   174  	// yet so we search the command line manually.
   175  	_, configSupplied := os.LookupEnv("RCLONE_CONFIG")
   176  	if !configSupplied {
   177  		for _, item := range os.Args {
   178  			if item == "--config" || strings.HasPrefix(item, "--config=") {
   179  				configSupplied = true
   180  				break
   181  			}
   182  		}
   183  	}
   184  
   185  	// If user's configuration directory was found, then try to create it
   186  	// and assume rclone.conf can be written there. If user supplied config
   187  	// then skip creating the directory since it will not be used.
   188  	if cfgpath != "" {
   189  		// cfgpath != "" implies cfgdir != ""
   190  		if configSupplied {
   191  			return cfgpath
   192  		}
   193  		err := os.MkdirAll(cfgdir, os.ModePerm)
   194  		if err == nil {
   195  			return cfgpath
   196  		}
   197  	}
   198  
   199  	// Assume .rclone.conf can be written to user's home directory.
   200  	if homeconf != "" {
   201  		return homeconf
   202  	}
   203  
   204  	// Default to ./.rclone.conf (current working directory) if everything else fails.
   205  	if !configSupplied {
   206  		fs.Errorf(nil, "Couldn't find home directory or read HOME or XDG_CONFIG_HOME environment variables.")
   207  		fs.Errorf(nil, "Defaulting to storing config in current directory.")
   208  		fs.Errorf(nil, "Use --config flag to workaround.")
   209  		fs.Errorf(nil, "Error was: %v", err)
   210  	}
   211  	return hiddenConfigFileName
   212  }
   213  
   214  // LoadConfig loads the config file
   215  func LoadConfig() {
   216  	// Load configuration file.
   217  	var err error
   218  	configFile, err = loadConfigFile()
   219  	if err == errorConfigFileNotFound {
   220  		fs.Logf(nil, "Config file %q not found - using defaults", ConfigPath)
   221  		configFile, _ = goconfig.LoadFromReader(&bytes.Buffer{})
   222  	} else if err != nil {
   223  		log.Fatalf("Failed to load config file %q: %v", ConfigPath, err)
   224  	} else {
   225  		fs.Debugf(nil, "Using config file from %q", ConfigPath)
   226  	}
   227  
   228  	// Start the token bucket limiter
   229  	accounting.StartTokenBucket()
   230  
   231  	// Start the bandwidth update ticker
   232  	accounting.StartTokenTicker()
   233  
   234  	// Start the transactions per second limiter
   235  	fshttp.StartHTTPTokenBucket()
   236  }
   237  
   238  var errorConfigFileNotFound = errors.New("config file not found")
   239  
   240  // loadConfigFile will load a config file, and
   241  // automatically decrypt it.
   242  func loadConfigFile() (*goconfig.ConfigFile, error) {
   243  	var usingPasswordCommand bool
   244  
   245  	b, err := ioutil.ReadFile(ConfigPath)
   246  	if err != nil {
   247  		if os.IsNotExist(err) {
   248  			return nil, errorConfigFileNotFound
   249  		}
   250  		return nil, err
   251  	}
   252  	// Find first non-empty line
   253  	r := bufio.NewReader(bytes.NewBuffer(b))
   254  	for {
   255  		line, _, err := r.ReadLine()
   256  		if err != nil {
   257  			if err == io.EOF {
   258  				return goconfig.LoadFromReader(bytes.NewBuffer(b))
   259  			}
   260  			return nil, err
   261  		}
   262  		l := strings.TrimSpace(string(line))
   263  		if len(l) == 0 || strings.HasPrefix(l, ";") || strings.HasPrefix(l, "#") {
   264  			continue
   265  		}
   266  		// First non-empty or non-comment must be ENCRYPT_V0
   267  		if l == "RCLONE_ENCRYPT_V0:" {
   268  			break
   269  		}
   270  		if strings.HasPrefix(l, "RCLONE_ENCRYPT_V") {
   271  			return nil, errors.New("unsupported configuration encryption - update rclone for support")
   272  		}
   273  		return goconfig.LoadFromReader(bytes.NewBuffer(b))
   274  	}
   275  
   276  	if len(configKey) == 0 {
   277  		if len(fs.Config.PasswordCommand) != 0 {
   278  			var stdout bytes.Buffer
   279  			var stderr bytes.Buffer
   280  
   281  			cmd := exec.Command(fs.Config.PasswordCommand[0], fs.Config.PasswordCommand[1:]...)
   282  
   283  			cmd.Stdout = &stdout
   284  			cmd.Stderr = &stderr
   285  			cmd.Stdin = os.Stdin
   286  
   287  			if err := cmd.Run(); err != nil {
   288  				// One does not always get the stderr returned in the wrapped error.
   289  				fs.Errorf(nil, "Using --password-command returned: %v", err)
   290  				if ers := strings.TrimSpace(stderr.String()); ers != "" {
   291  					fs.Errorf(nil, "--password-command stderr: %s", ers)
   292  				}
   293  				return nil, errors.Wrap(err, "password command failed")
   294  			}
   295  			if pass := strings.Trim(stdout.String(), "\r\n"); pass != "" {
   296  				err := setConfigPassword(pass)
   297  				if err != nil {
   298  					return nil, errors.Wrap(err, "incorrect password")
   299  				}
   300  			} else {
   301  				return nil, errors.New("password-command returned empty string")
   302  			}
   303  
   304  			if len(configKey) == 0 {
   305  				return nil, errors.New("unable to decrypt configuration: incorrect password")
   306  			}
   307  			usingPasswordCommand = true
   308  		} else {
   309  			usingPasswordCommand = false
   310  
   311  			envpw := os.Getenv("RCLONE_CONFIG_PASS")
   312  
   313  			if envpw != "" {
   314  				err := setConfigPassword(envpw)
   315  				if err != nil {
   316  					fs.Errorf(nil, "Using RCLONE_CONFIG_PASS returned: %v", err)
   317  				} else {
   318  					fs.Debugf(nil, "Using RCLONE_CONFIG_PASS password.")
   319  				}
   320  			}
   321  		}
   322  	}
   323  
   324  	// Encrypted content is base64 encoded.
   325  	dec := base64.NewDecoder(base64.StdEncoding, r)
   326  	box, err := ioutil.ReadAll(dec)
   327  	if err != nil {
   328  		return nil, errors.Wrap(err, "failed to load base64 encoded data")
   329  	}
   330  	if len(box) < 24+secretbox.Overhead {
   331  		return nil, errors.New("Configuration data too short")
   332  	}
   333  
   334  	var out []byte
   335  	for {
   336  		if envKeyFile := os.Getenv("_RCLONE_CONFIG_KEY_FILE"); len(envKeyFile) > 0 {
   337  			fs.Debugf(nil, "attempting to obtain configKey from temp file %s", envKeyFile)
   338  			obscuredKey, err := ioutil.ReadFile(envKeyFile)
   339  			if err != nil {
   340  				errRemove := os.Remove(envKeyFile)
   341  				if errRemove != nil {
   342  					log.Fatalf("unable to read obscured config key and unable to delete the temp file: %v", err)
   343  				}
   344  				log.Fatalf("unable to read obscured config key: %v", err)
   345  			}
   346  			errRemove := os.Remove(envKeyFile)
   347  			if errRemove != nil {
   348  				log.Fatalf("unable to delete temp file with configKey: %v", err)
   349  			}
   350  			configKey = []byte(obscure.MustReveal(string(obscuredKey)))
   351  			fs.Debugf(nil, "using _RCLONE_CONFIG_KEY_FILE for configKey")
   352  		} else {
   353  			if len(configKey) == 0 {
   354  				if usingPasswordCommand {
   355  					return nil, errors.New("using --password-command derived password, unable to decrypt configuration")
   356  				}
   357  				if !fs.Config.AskPassword {
   358  					return nil, errors.New("unable to decrypt configuration and not allowed to ask for password - set RCLONE_CONFIG_PASS to your configuration password")
   359  				}
   360  				getConfigPassword("Enter configuration password:")
   361  			}
   362  		}
   363  
   364  		// Nonce is first 24 bytes of the ciphertext
   365  		var nonce [24]byte
   366  		copy(nonce[:], box[:24])
   367  		var key [32]byte
   368  		copy(key[:], configKey[:32])
   369  
   370  		// Attempt to decrypt
   371  		var ok bool
   372  		out, ok = secretbox.Open(nil, box[24:], &nonce, &key)
   373  		if ok {
   374  			break
   375  		}
   376  
   377  		// Retry
   378  		fs.Errorf(nil, "Couldn't decrypt configuration, most likely wrong password.")
   379  		configKey = nil
   380  	}
   381  	return goconfig.LoadFromReader(bytes.NewBuffer(out))
   382  }
   383  
   384  // checkPassword normalises and validates the password
   385  func checkPassword(password string) (string, error) {
   386  	if !utf8.ValidString(password) {
   387  		return "", errors.New("password contains invalid utf8 characters")
   388  	}
   389  	// Check for leading/trailing whitespace
   390  	trimmedPassword := strings.TrimSpace(password)
   391  	// Warn user if password has leading+trailing whitespace
   392  	if len(password) != len(trimmedPassword) {
   393  		_, _ = fmt.Fprintln(os.Stderr, "Your password contains leading/trailing whitespace - in previous versions of rclone this was stripped")
   394  	}
   395  	// Normalize to reduce weird variations.
   396  	password = norm.NFKC.String(password)
   397  	if len(password) == 0 || len(trimmedPassword) == 0 {
   398  		return "", errors.New("no characters in password")
   399  	}
   400  	return password, nil
   401  }
   402  
   403  // GetPassword asks the user for a password with the prompt given.
   404  func GetPassword(prompt string) string {
   405  	_, _ = fmt.Fprintln(PasswordPromptOutput, prompt)
   406  	for {
   407  		_, _ = fmt.Fprint(PasswordPromptOutput, "password:")
   408  		password := ReadPassword()
   409  		password, err := checkPassword(password)
   410  		if err == nil {
   411  			return password
   412  		}
   413  		_, _ = fmt.Fprintf(os.Stderr, "Bad password: %v\n", err)
   414  	}
   415  }
   416  
   417  // ChangePassword will query the user twice for the named password. If
   418  // the same password is entered it is returned.
   419  func ChangePassword(name string) string {
   420  	for {
   421  		a := GetPassword(fmt.Sprintf("Enter %s password:", name))
   422  		b := GetPassword(fmt.Sprintf("Confirm %s password:", name))
   423  		if a == b {
   424  			return a
   425  		}
   426  		fmt.Println("Passwords do not match!")
   427  	}
   428  }
   429  
   430  // getConfigPassword will query the user for a password the
   431  // first time it is required.
   432  func getConfigPassword(q string) {
   433  	if len(configKey) != 0 {
   434  		return
   435  	}
   436  	for {
   437  		password := GetPassword(q)
   438  		err := setConfigPassword(password)
   439  		if err == nil {
   440  			return
   441  		}
   442  		_, _ = fmt.Fprintln(os.Stderr, "Error:", err)
   443  	}
   444  }
   445  
   446  // setConfigPassword will set the configKey to the hash of
   447  // the password. If the length of the password is
   448  // zero after trimming+normalization, an error is returned.
   449  func setConfigPassword(password string) error {
   450  	password, err := checkPassword(password)
   451  	if err != nil {
   452  		return err
   453  	}
   454  	// Create SHA256 has of the password
   455  	sha := sha256.New()
   456  	_, err = sha.Write([]byte("[" + password + "][rclone-config]"))
   457  	if err != nil {
   458  		return err
   459  	}
   460  	configKey = sha.Sum(nil)
   461  	if PassConfigKeyForDaemonization {
   462  		tempFile, err := ioutil.TempFile("", "rclone")
   463  		if err != nil {
   464  			log.Fatalf("cannot create temp file to store configKey: %v", err)
   465  		}
   466  		_, err = tempFile.WriteString(obscure.MustObscure(string(configKey)))
   467  		if err != nil {
   468  			errRemove := os.Remove(tempFile.Name())
   469  			if errRemove != nil {
   470  				log.Fatalf("error writing configKey to temp file and also error deleting it: %v", err)
   471  			}
   472  			log.Fatalf("error writing configKey to temp file: %v", err)
   473  		}
   474  		err = tempFile.Close()
   475  		if err != nil {
   476  			errRemove := os.Remove(tempFile.Name())
   477  			if errRemove != nil {
   478  				log.Fatalf("error closing temp file with configKey and also error deleting it: %v", err)
   479  			}
   480  			log.Fatalf("error closing temp file with configKey: %v", err)
   481  		}
   482  		fs.Debugf(nil, "saving configKey to temp file")
   483  		err = os.Setenv("_RCLONE_CONFIG_KEY_FILE", tempFile.Name())
   484  		if err != nil {
   485  			errRemove := os.Remove(tempFile.Name())
   486  			if errRemove != nil {
   487  				log.Fatalf("unable to set environment variable _RCLONE_CONFIG_KEY_FILE and unable to delete the temp file: %v", err)
   488  			}
   489  			log.Fatalf("unable to set environment variable _RCLONE_CONFIG_KEY_FILE: %v", err)
   490  		}
   491  	}
   492  	return nil
   493  }
   494  
   495  // changeConfigPassword will query the user twice
   496  // for a password. If the same password is entered
   497  // twice the key is updated.
   498  func changeConfigPassword() {
   499  	err := setConfigPassword(ChangePassword("NEW configuration"))
   500  	if err != nil {
   501  		fmt.Printf("Failed to set config password: %v\n", err)
   502  		return
   503  	}
   504  }
   505  
   506  // saveConfig saves configuration file.
   507  // if configKey has been set, the file will be encrypted.
   508  func saveConfig() error {
   509  	dir, name := filepath.Split(ConfigPath)
   510  	err := os.MkdirAll(dir, os.ModePerm)
   511  	if err != nil {
   512  		return errors.Wrap(err, "failed to create config directory")
   513  	}
   514  	f, err := ioutil.TempFile(dir, name)
   515  	if err != nil {
   516  		return errors.Errorf("Failed to create temp file for new config: %v", err)
   517  	}
   518  	defer func() {
   519  		if err := os.Remove(f.Name()); err != nil && !os.IsNotExist(err) {
   520  			fs.Errorf(nil, "Failed to remove temp config file: %v", err)
   521  		}
   522  	}()
   523  
   524  	var buf bytes.Buffer
   525  	err = goconfig.SaveConfigData(getConfigData(), &buf)
   526  	if err != nil {
   527  		return errors.Errorf("Failed to save config file: %v", err)
   528  	}
   529  
   530  	if len(configKey) == 0 {
   531  		if _, err := buf.WriteTo(f); err != nil {
   532  			return errors.Errorf("Failed to write temp config file: %v", err)
   533  		}
   534  	} else {
   535  		_, _ = fmt.Fprintln(f, "# Encrypted rclone configuration File")
   536  		_, _ = fmt.Fprintln(f, "")
   537  		_, _ = fmt.Fprintln(f, "RCLONE_ENCRYPT_V0:")
   538  
   539  		// Generate new nonce and write it to the start of the ciphertext
   540  		var nonce [24]byte
   541  		n, _ := rand.Read(nonce[:])
   542  		if n != 24 {
   543  			return errors.Errorf("nonce short read: %d", n)
   544  		}
   545  		enc := base64.NewEncoder(base64.StdEncoding, f)
   546  		_, err = enc.Write(nonce[:])
   547  		if err != nil {
   548  			return errors.Errorf("Failed to write temp config file: %v", err)
   549  		}
   550  
   551  		var key [32]byte
   552  		copy(key[:], configKey[:32])
   553  
   554  		b := secretbox.Seal(nil, buf.Bytes(), &nonce, &key)
   555  		_, err = enc.Write(b)
   556  		if err != nil {
   557  			return errors.Errorf("Failed to write temp config file: %v", err)
   558  		}
   559  		_ = enc.Close()
   560  	}
   561  
   562  	_ = f.Sync()
   563  	err = f.Close()
   564  	if err != nil {
   565  		return errors.Errorf("Failed to close config file: %v", err)
   566  	}
   567  
   568  	var fileMode os.FileMode = 0600
   569  	info, err := os.Stat(ConfigPath)
   570  	if err != nil {
   571  		fs.Debugf(nil, "Using default permissions for config file: %v", fileMode)
   572  	} else if info.Mode() != fileMode {
   573  		fs.Debugf(nil, "Keeping previous permissions for config file: %v", info.Mode())
   574  		fileMode = info.Mode()
   575  	}
   576  
   577  	attemptCopyGroup(ConfigPath, f.Name())
   578  
   579  	err = os.Chmod(f.Name(), fileMode)
   580  	if err != nil {
   581  		fs.Errorf(nil, "Failed to set permissions on config file: %v", err)
   582  	}
   583  
   584  	if err = os.Rename(ConfigPath, ConfigPath+".old"); err != nil && !os.IsNotExist(err) {
   585  		return errors.Errorf("Failed to move previous config to backup location: %v", err)
   586  	}
   587  	if err = os.Rename(f.Name(), ConfigPath); err != nil {
   588  		return errors.Errorf("Failed to move newly written config from %s to final location: %v", f.Name(), err)
   589  	}
   590  	if err := os.Remove(ConfigPath + ".old"); err != nil && !os.IsNotExist(err) {
   591  		fs.Errorf(nil, "Failed to remove backup config file: %v", err)
   592  	}
   593  	return nil
   594  }
   595  
   596  // SaveConfig calling function which saves configuration file.
   597  // if saveConfig returns error trying again after sleep.
   598  func SaveConfig() {
   599  	var err error
   600  	for i := 0; i < fs.Config.LowLevelRetries+1; i++ {
   601  		if err = saveConfig(); err == nil {
   602  			return
   603  		}
   604  		waitingTimeMs := mathrand.Intn(1000)
   605  		time.Sleep(time.Duration(waitingTimeMs) * time.Millisecond)
   606  	}
   607  	log.Fatalf("Failed to save config after %d tries: %v", fs.Config.LowLevelRetries, err)
   608  
   609  	return
   610  }
   611  
   612  // SetValueAndSave sets the key to the value and saves just that
   613  // value in the config file.  It loads the old config file in from
   614  // disk first and overwrites the given value only.
   615  func SetValueAndSave(name, key, value string) (err error) {
   616  	// Set the value in config in case we fail to reload it
   617  	getConfigData().SetValue(name, key, value)
   618  	// Reload the config file
   619  	reloadedConfigFile, err := loadConfigFile()
   620  	if err == errorConfigFileNotFound {
   621  		// Config file not written yet so ignore reload
   622  		return nil
   623  	} else if err != nil {
   624  		return err
   625  	}
   626  	_, err = reloadedConfigFile.GetSection(name)
   627  	if err != nil {
   628  		// Section doesn't exist yet so ignore reload
   629  		return nil
   630  	}
   631  	// Update the config file with the reloaded version
   632  	configFile = reloadedConfigFile
   633  	// Set the value in the reloaded version
   634  	reloadedConfigFile.SetValue(name, key, value)
   635  	// Save it again
   636  	SaveConfig()
   637  	return nil
   638  }
   639  
   640  // FileGetFresh reads the config key under section return the value or
   641  // an error if the config file was not found or that value couldn't be
   642  // read.
   643  func FileGetFresh(section, key string) (value string, err error) {
   644  	reloadedConfigFile, err := loadConfigFile()
   645  	if err != nil {
   646  		return "", err
   647  	}
   648  	return reloadedConfigFile.GetValue(section, key)
   649  }
   650  
   651  // ShowRemotes shows an overview of the config file
   652  func ShowRemotes() {
   653  	remotes := getConfigData().GetSectionList()
   654  	if len(remotes) == 0 {
   655  		return
   656  	}
   657  	sort.Strings(remotes)
   658  	fmt.Printf("%-20s %s\n", "Name", "Type")
   659  	fmt.Printf("%-20s %s\n", "====", "====")
   660  	for _, remote := range remotes {
   661  		fmt.Printf("%-20s %s\n", remote, FileGet(remote, "type"))
   662  	}
   663  }
   664  
   665  // ChooseRemote chooses a remote name
   666  func ChooseRemote() string {
   667  	remotes := getConfigData().GetSectionList()
   668  	sort.Strings(remotes)
   669  	return Choose("remote", remotes, nil, false)
   670  }
   671  
   672  // ReadLine reads some input
   673  var ReadLine = func() string {
   674  	buf := bufio.NewReader(os.Stdin)
   675  	line, err := buf.ReadString('\n')
   676  	if err != nil {
   677  		log.Fatalf("Failed to read line: %v", err)
   678  	}
   679  	return strings.TrimSpace(line)
   680  }
   681  
   682  // ReadNonEmptyLine prints prompt and calls Readline until non empty
   683  func ReadNonEmptyLine(prompt string) string {
   684  	result := ""
   685  	for result == "" {
   686  		fmt.Print(prompt)
   687  		result = strings.TrimSpace(ReadLine())
   688  	}
   689  	return result
   690  }
   691  
   692  // CommandDefault - choose one.  If return is pressed then it will
   693  // chose the defaultIndex if it is >= 0
   694  func CommandDefault(commands []string, defaultIndex int) byte {
   695  	opts := []string{}
   696  	for i, text := range commands {
   697  		def := ""
   698  		if i == defaultIndex {
   699  			def = " (default)"
   700  		}
   701  		fmt.Printf("%c) %s%s\n", text[0], text[1:], def)
   702  		opts = append(opts, text[:1])
   703  	}
   704  	optString := strings.Join(opts, "")
   705  	optHelp := strings.Join(opts, "/")
   706  	for {
   707  		fmt.Printf("%s> ", optHelp)
   708  		result := strings.ToLower(ReadLine())
   709  		if len(result) == 0 && defaultIndex >= 0 {
   710  			return optString[defaultIndex]
   711  		}
   712  		if len(result) != 1 {
   713  			continue
   714  		}
   715  		i := strings.Index(optString, string(result[0]))
   716  		if i >= 0 {
   717  			return result[0]
   718  		}
   719  	}
   720  }
   721  
   722  // Command - choose one
   723  func Command(commands []string) byte {
   724  	return CommandDefault(commands, -1)
   725  }
   726  
   727  // Confirm asks the user for Yes or No and returns true or false
   728  //
   729  // If the user presses enter then the Default will be used
   730  func Confirm(Default bool) bool {
   731  	defaultIndex := 0
   732  	if !Default {
   733  		defaultIndex = 1
   734  	}
   735  	return CommandDefault([]string{"yYes", "nNo"}, defaultIndex) == 'y'
   736  }
   737  
   738  // ConfirmWithConfig asks the user for Yes or No and returns true or
   739  // false.
   740  //
   741  // If AutoConfirm is set, it will look up the value in m and return
   742  // that, but if it isn't set then it will return the Default value
   743  // passed in
   744  func ConfirmWithConfig(m configmap.Getter, configName string, Default bool) bool {
   745  	if fs.Config.AutoConfirm {
   746  		configString, ok := m.Get(configName)
   747  		if ok {
   748  			configValue, err := strconv.ParseBool(configString)
   749  			if err != nil {
   750  				fs.Errorf(nil, "Failed to parse config parameter %s=%q as boolean - using default %v: %v", configName, configString, Default, err)
   751  			} else {
   752  				Default = configValue
   753  			}
   754  		}
   755  		answer := "No"
   756  		if Default {
   757  			answer = "Yes"
   758  		}
   759  		fmt.Printf("Auto confirm is set: answering %s, override by setting config parameter %s=%v\n", answer, configName, !Default)
   760  		return Default
   761  	}
   762  	return Confirm(Default)
   763  }
   764  
   765  // Choose one of the defaults or type a new string if newOk is set
   766  func Choose(what string, defaults, help []string, newOk bool) string {
   767  	valueDescription := "an existing"
   768  	if newOk {
   769  		valueDescription = "your own"
   770  	}
   771  	fmt.Printf("Choose a number from below, or type in %s value\n", valueDescription)
   772  	attributes := []string{terminal.HiRedFg, terminal.HiGreenFg}
   773  	for i, text := range defaults {
   774  		var lines []string
   775  		if help != nil {
   776  			parts := strings.Split(help[i], "\n")
   777  			lines = append(lines, parts...)
   778  		}
   779  		lines = append(lines, fmt.Sprintf("%q", text))
   780  		pos := i + 1
   781  		terminal.WriteString(attributes[i%len(attributes)])
   782  		if len(lines) == 1 {
   783  			fmt.Printf("%2d > %s\n", pos, text)
   784  		} else {
   785  			mid := (len(lines) - 1) / 2
   786  			for i, line := range lines {
   787  				var sep rune
   788  				switch i {
   789  				case 0:
   790  					sep = '/'
   791  				case len(lines) - 1:
   792  					sep = '\\'
   793  				default:
   794  					sep = '|'
   795  				}
   796  				number := "  "
   797  				if i == mid {
   798  					number = fmt.Sprintf("%2d", pos)
   799  				}
   800  				fmt.Printf("%s %c %s\n", number, sep, line)
   801  			}
   802  		}
   803  		terminal.WriteString(terminal.Reset)
   804  	}
   805  	for {
   806  		fmt.Printf("%s> ", what)
   807  		result := ReadLine()
   808  		i, err := strconv.Atoi(result)
   809  		if err != nil {
   810  			if newOk {
   811  				return result
   812  			}
   813  			for _, v := range defaults {
   814  				if result == v {
   815  					return result
   816  				}
   817  			}
   818  			continue
   819  		}
   820  		if i >= 1 && i <= len(defaults) {
   821  			return defaults[i-1]
   822  		}
   823  	}
   824  }
   825  
   826  // ChooseNumber asks the user to enter a number between min and max
   827  // inclusive prompting them with what.
   828  func ChooseNumber(what string, min, max int) int {
   829  	for {
   830  		fmt.Printf("%s> ", what)
   831  		result := ReadLine()
   832  		i, err := strconv.Atoi(result)
   833  		if err != nil {
   834  			fmt.Printf("Bad number: %v\n", err)
   835  			continue
   836  		}
   837  		if i < min || i > max {
   838  			fmt.Printf("Out of range - %d to %d inclusive\n", min, max)
   839  			continue
   840  		}
   841  		return i
   842  	}
   843  }
   844  
   845  // ShowRemote shows the contents of the remote
   846  func ShowRemote(name string) {
   847  	fmt.Printf("--------------------\n")
   848  	fmt.Printf("[%s]\n", name)
   849  	fs := MustFindByName(name)
   850  	for _, key := range getConfigData().GetKeyList(name) {
   851  		isPassword := false
   852  		for _, option := range fs.Options {
   853  			if option.Name == key && option.IsPassword {
   854  				isPassword = true
   855  				break
   856  			}
   857  		}
   858  		value := FileGet(name, key)
   859  		if isPassword && value != "" {
   860  			fmt.Printf("%s = *** ENCRYPTED ***\n", key)
   861  		} else {
   862  			fmt.Printf("%s = %s\n", key, value)
   863  		}
   864  	}
   865  	fmt.Printf("--------------------\n")
   866  }
   867  
   868  // OkRemote prints the contents of the remote and ask if it is OK
   869  func OkRemote(name string) bool {
   870  	ShowRemote(name)
   871  	switch i := CommandDefault([]string{"yYes this is OK", "eEdit this remote", "dDelete this remote"}, 0); i {
   872  	case 'y':
   873  		return true
   874  	case 'e':
   875  		return false
   876  	case 'd':
   877  		getConfigData().DeleteSection(name)
   878  		return true
   879  	default:
   880  		fs.Errorf(nil, "Bad choice %c", i)
   881  	}
   882  	return false
   883  }
   884  
   885  // MustFindByName finds the RegInfo for the remote name passed in or
   886  // exits with a fatal error.
   887  func MustFindByName(name string) *fs.RegInfo {
   888  	fsType := FileGet(name, "type")
   889  	if fsType == "" {
   890  		log.Fatalf("Couldn't find type of fs for %q", name)
   891  	}
   892  	return fs.MustFind(fsType)
   893  }
   894  
   895  // RemoteConfig runs the config helper for the remote if needed
   896  func RemoteConfig(name string) {
   897  	fmt.Printf("Remote config\n")
   898  	f := MustFindByName(name)
   899  	if f.Config != nil {
   900  		m := fs.ConfigMap(f, name)
   901  		f.Config(name, m)
   902  	}
   903  }
   904  
   905  // matchProvider returns true if provider matches the providerConfig string.
   906  //
   907  // The providerConfig string can either be a list of providers to
   908  // match, or if it starts with "!" it will be a list of providers not
   909  // to match.
   910  //
   911  // If either providerConfig or provider is blank then it will return true
   912  func matchProvider(providerConfig, provider string) bool {
   913  	if providerConfig == "" || provider == "" {
   914  		return true
   915  	}
   916  	negate := false
   917  	if strings.HasPrefix(providerConfig, "!") {
   918  		providerConfig = providerConfig[1:]
   919  		negate = true
   920  	}
   921  	providers := strings.Split(providerConfig, ",")
   922  	matched := false
   923  	for _, p := range providers {
   924  		if p == provider {
   925  			matched = true
   926  			break
   927  		}
   928  	}
   929  	if negate {
   930  		return !matched
   931  	}
   932  	return matched
   933  }
   934  
   935  // ChooseOption asks the user to choose an option
   936  func ChooseOption(o *fs.Option, name string) string {
   937  	var subProvider = getConfigData().MustValue(name, fs.ConfigProvider, "")
   938  	fmt.Println(o.Help)
   939  	if o.IsPassword {
   940  		actions := []string{"yYes type in my own password", "gGenerate random password"}
   941  		defaultAction := -1
   942  		if !o.Required {
   943  			defaultAction = len(actions)
   944  			actions = append(actions, "nNo leave this optional password blank")
   945  		}
   946  		var password string
   947  		var err error
   948  		switch i := CommandDefault(actions, defaultAction); i {
   949  		case 'y':
   950  			password = ChangePassword("the")
   951  		case 'g':
   952  			for {
   953  				fmt.Printf("Password strength in bits.\n64 is just about memorable\n128 is secure\n1024 is the maximum\n")
   954  				bits := ChooseNumber("Bits", 64, 1024)
   955  				password, err = Password(bits)
   956  				if err != nil {
   957  					log.Fatalf("Failed to make password: %v", err)
   958  				}
   959  				fmt.Printf("Your password is: %s\n", password)
   960  				fmt.Printf("Use this password? Please note that an obscured version of this \npassword (and not the " +
   961  					"password itself) will be stored under your \nconfiguration file, so keep this generated password " +
   962  					"in a safe place.\n")
   963  				if Confirm(true) {
   964  					break
   965  				}
   966  			}
   967  		case 'n':
   968  			return ""
   969  		default:
   970  			fs.Errorf(nil, "Bad choice %c", i)
   971  		}
   972  		return obscure.MustObscure(password)
   973  	}
   974  	what := fmt.Sprintf("%T value", o.Default)
   975  	switch o.Default.(type) {
   976  	case bool:
   977  		what = "boolean value (true or false)"
   978  	case fs.SizeSuffix:
   979  		what = "size with suffix k,M,G,T"
   980  	case fs.Duration:
   981  		what = "duration s,m,h,d,w,M,y"
   982  	case int, int8, int16, int32, int64:
   983  		what = "signed integer"
   984  	case uint, byte, uint16, uint32, uint64:
   985  		what = "unsigned integer"
   986  	}
   987  	var in string
   988  	for {
   989  		fmt.Printf("Enter a %s. Press Enter for the default (%q).\n", what, fmt.Sprint(o.Default))
   990  		if len(o.Examples) > 0 {
   991  			var values []string
   992  			var help []string
   993  			for _, example := range o.Examples {
   994  				if matchProvider(example.Provider, subProvider) {
   995  					values = append(values, example.Value)
   996  					help = append(help, example.Help)
   997  				}
   998  			}
   999  			in = Choose(o.Name, values, help, true)
  1000  		} else {
  1001  			fmt.Printf("%s> ", o.Name)
  1002  			in = ReadLine()
  1003  		}
  1004  		if in == "" {
  1005  			if o.Required && fmt.Sprint(o.Default) == "" {
  1006  				fmt.Printf("This value is required and it has no default.\n")
  1007  				continue
  1008  			}
  1009  			break
  1010  		}
  1011  		newIn, err := configstruct.StringToInterface(o.Default, in)
  1012  		if err != nil {
  1013  			fmt.Printf("Failed to parse %q: %v\n", in, err)
  1014  			continue
  1015  		}
  1016  		in = fmt.Sprint(newIn) // canonicalise
  1017  		break
  1018  	}
  1019  	return in
  1020  }
  1021  
  1022  // Suppress the confirm prompts and return a function to undo that
  1023  func suppressConfirm() func() {
  1024  	old := fs.Config.AutoConfirm
  1025  	fs.Config.AutoConfirm = true
  1026  	return func() {
  1027  		fs.Config.AutoConfirm = old
  1028  	}
  1029  }
  1030  
  1031  // UpdateRemote adds the keyValues passed in to the remote of name.
  1032  // keyValues should be key, value pairs.
  1033  func UpdateRemote(name string, keyValues rc.Params, doObscure, noObscure bool) error {
  1034  	if doObscure && noObscure {
  1035  		return errors.New("can't use --obscure and --no-obscure together")
  1036  	}
  1037  	err := fspath.CheckConfigName(name)
  1038  	if err != nil {
  1039  		return err
  1040  	}
  1041  	defer suppressConfirm()()
  1042  
  1043  	// Work out which options need to be obscured
  1044  	needsObscure := map[string]struct{}{}
  1045  	if !noObscure {
  1046  		if fsType := FileGet(name, "type"); fsType != "" {
  1047  			if ri, err := fs.Find(fsType); err != nil {
  1048  				fs.Debugf(nil, "Couldn't find fs for type %q", fsType)
  1049  			} else {
  1050  				for _, opt := range ri.Options {
  1051  					if opt.IsPassword {
  1052  						needsObscure[opt.Name] = struct{}{}
  1053  					}
  1054  				}
  1055  			}
  1056  		} else {
  1057  			fs.Debugf(nil, "UpdateRemote: Couldn't find fs type")
  1058  		}
  1059  	}
  1060  
  1061  	// Set the config
  1062  	for k, v := range keyValues {
  1063  		vStr := fmt.Sprint(v)
  1064  		// Obscure parameter if necessary
  1065  		if _, ok := needsObscure[k]; ok {
  1066  			_, err := obscure.Reveal(vStr)
  1067  			if err != nil || doObscure {
  1068  				// If error => not already obscured, so obscure it
  1069  				// or we are forced to obscure
  1070  				vStr, err = obscure.Obscure(vStr)
  1071  				if err != nil {
  1072  					return errors.Wrap(err, "UpdateRemote: obscure failed")
  1073  				}
  1074  			}
  1075  		}
  1076  		getConfigData().SetValue(name, k, vStr)
  1077  	}
  1078  	RemoteConfig(name)
  1079  	SaveConfig()
  1080  	return nil
  1081  }
  1082  
  1083  // CreateRemote creates a new remote with name, provider and a list of
  1084  // parameters which are key, value pairs.  If update is set then it
  1085  // adds the new keys rather than replacing all of them.
  1086  func CreateRemote(name string, provider string, keyValues rc.Params, doObscure, noObscure bool) error {
  1087  	err := fspath.CheckConfigName(name)
  1088  	if err != nil {
  1089  		return err
  1090  	}
  1091  	// Delete the old config if it exists
  1092  	getConfigData().DeleteSection(name)
  1093  	// Set the type
  1094  	getConfigData().SetValue(name, "type", provider)
  1095  	// Set the remaining values
  1096  	return UpdateRemote(name, keyValues, doObscure, noObscure)
  1097  }
  1098  
  1099  // PasswordRemote adds the keyValues passed in to the remote of name.
  1100  // keyValues should be key, value pairs.
  1101  func PasswordRemote(name string, keyValues rc.Params) error {
  1102  	err := fspath.CheckConfigName(name)
  1103  	if err != nil {
  1104  		return err
  1105  	}
  1106  	defer suppressConfirm()()
  1107  	for k, v := range keyValues {
  1108  		keyValues[k] = obscure.MustObscure(fmt.Sprint(v))
  1109  	}
  1110  	return UpdateRemote(name, keyValues, false, true)
  1111  }
  1112  
  1113  // JSONListProviders prints all the providers and options in JSON format
  1114  func JSONListProviders() error {
  1115  	b, err := json.MarshalIndent(fs.Registry, "", "    ")
  1116  	if err != nil {
  1117  		return errors.Wrap(err, "failed to marshal examples")
  1118  	}
  1119  	_, err = os.Stdout.Write(b)
  1120  	if err != nil {
  1121  		return errors.Wrap(err, "failed to write providers list")
  1122  	}
  1123  	return nil
  1124  }
  1125  
  1126  // fsOption returns an Option describing the possible remotes
  1127  func fsOption() *fs.Option {
  1128  	o := &fs.Option{
  1129  		Name:    "Storage",
  1130  		Help:    "Type of storage to configure.",
  1131  		Default: "",
  1132  	}
  1133  	for _, item := range fs.Registry {
  1134  		example := fs.OptionExample{
  1135  			Value: item.Name,
  1136  			Help:  item.Description,
  1137  		}
  1138  		o.Examples = append(o.Examples, example)
  1139  	}
  1140  	o.Examples.Sort()
  1141  	return o
  1142  }
  1143  
  1144  // NewRemoteName asks the user for a name for a new remote
  1145  func NewRemoteName() (name string) {
  1146  	for {
  1147  		fmt.Printf("name> ")
  1148  		name = ReadLine()
  1149  		_, err := getConfigData().GetSection(name)
  1150  		if err == nil {
  1151  			fmt.Printf("Remote %q already exists.\n", name)
  1152  			continue
  1153  		}
  1154  		err = fspath.CheckConfigName(name)
  1155  		switch {
  1156  		case name == "":
  1157  			fmt.Printf("Can't use empty name.\n")
  1158  		case driveletter.IsDriveLetter(name):
  1159  			fmt.Printf("Can't use %q as it can be confused with a drive letter.\n", name)
  1160  		case err != nil:
  1161  			fmt.Printf("Can't use %q as %v.\n", name, err)
  1162  		default:
  1163  			return name
  1164  		}
  1165  	}
  1166  }
  1167  
  1168  // editOptions edits the options.  If new is true then it just allows
  1169  // entry and doesn't show any old values.
  1170  func editOptions(ri *fs.RegInfo, name string, isNew bool) {
  1171  	fmt.Printf("** See help for %s backend at: https://rclone.org/%s/ **\n\n", ri.Name, ri.FileName())
  1172  	hasAdvanced := false
  1173  	for _, advanced := range []bool{false, true} {
  1174  		if advanced {
  1175  			if !hasAdvanced {
  1176  				break
  1177  			}
  1178  			fmt.Printf("Edit advanced config? (y/n)\n")
  1179  			if !Confirm(false) {
  1180  				break
  1181  			}
  1182  		}
  1183  		for _, option := range ri.Options {
  1184  			isVisible := option.Hide&fs.OptionHideConfigurator == 0
  1185  			hasAdvanced = hasAdvanced || (option.Advanced && isVisible)
  1186  			if option.Advanced != advanced {
  1187  				continue
  1188  			}
  1189  			subProvider := getConfigData().MustValue(name, fs.ConfigProvider, "")
  1190  			if matchProvider(option.Provider, subProvider) && isVisible {
  1191  				if !isNew {
  1192  					fmt.Printf("Value %q = %q\n", option.Name, FileGet(name, option.Name))
  1193  					fmt.Printf("Edit? (y/n)>\n")
  1194  					if !Confirm(false) {
  1195  						continue
  1196  					}
  1197  				}
  1198  				FileSet(name, option.Name, ChooseOption(&option, name))
  1199  			}
  1200  		}
  1201  	}
  1202  }
  1203  
  1204  // NewRemote make a new remote from its name
  1205  func NewRemote(name string) {
  1206  	var (
  1207  		newType string
  1208  		ri      *fs.RegInfo
  1209  		err     error
  1210  	)
  1211  
  1212  	// Set the type first
  1213  	for {
  1214  		newType = ChooseOption(fsOption(), name)
  1215  		ri, err = fs.Find(newType)
  1216  		if err != nil {
  1217  			fmt.Printf("Bad remote %q: %v\n", newType, err)
  1218  			continue
  1219  		}
  1220  		break
  1221  	}
  1222  	getConfigData().SetValue(name, "type", newType)
  1223  
  1224  	editOptions(ri, name, true)
  1225  	RemoteConfig(name)
  1226  	if OkRemote(name) {
  1227  		SaveConfig()
  1228  		return
  1229  	}
  1230  	EditRemote(ri, name)
  1231  }
  1232  
  1233  // EditRemote gets the user to edit a remote
  1234  func EditRemote(ri *fs.RegInfo, name string) {
  1235  	ShowRemote(name)
  1236  	fmt.Printf("Edit remote\n")
  1237  	for {
  1238  		editOptions(ri, name, false)
  1239  		if OkRemote(name) {
  1240  			break
  1241  		}
  1242  	}
  1243  	SaveConfig()
  1244  	RemoteConfig(name)
  1245  }
  1246  
  1247  // DeleteRemote gets the user to delete a remote
  1248  func DeleteRemote(name string) {
  1249  	getConfigData().DeleteSection(name)
  1250  	SaveConfig()
  1251  }
  1252  
  1253  // copyRemote asks the user for a new remote name and copies name into
  1254  // it. Returns the new name.
  1255  func copyRemote(name string) string {
  1256  	newName := NewRemoteName()
  1257  	// Copy the keys
  1258  	for _, key := range getConfigData().GetKeyList(name) {
  1259  		value := getConfigData().MustValue(name, key, "")
  1260  		getConfigData().SetValue(newName, key, value)
  1261  	}
  1262  	return newName
  1263  }
  1264  
  1265  // RenameRemote renames a config section
  1266  func RenameRemote(name string) {
  1267  	fmt.Printf("Enter new name for %q remote.\n", name)
  1268  	newName := copyRemote(name)
  1269  	if name != newName {
  1270  		getConfigData().DeleteSection(name)
  1271  		SaveConfig()
  1272  	}
  1273  }
  1274  
  1275  // CopyRemote copies a config section
  1276  func CopyRemote(name string) {
  1277  	fmt.Printf("Enter name for copy of %q remote.\n", name)
  1278  	copyRemote(name)
  1279  	SaveConfig()
  1280  }
  1281  
  1282  // ShowConfigLocation prints the location of the config file in use
  1283  func ShowConfigLocation() {
  1284  	if _, err := os.Stat(ConfigPath); os.IsNotExist(err) {
  1285  		fmt.Println("Configuration file doesn't exist, but rclone will use this path:")
  1286  	} else {
  1287  		fmt.Println("Configuration file is stored at:")
  1288  	}
  1289  	fmt.Printf("%s\n", ConfigPath)
  1290  }
  1291  
  1292  // ShowConfig prints the (unencrypted) config options
  1293  func ShowConfig() {
  1294  	var buf bytes.Buffer
  1295  	if err := goconfig.SaveConfigData(getConfigData(), &buf); err != nil {
  1296  		log.Fatalf("Failed to serialize config: %v", err)
  1297  	}
  1298  	str := buf.String()
  1299  	if str == "" {
  1300  		str = "; empty config\n"
  1301  	}
  1302  	fmt.Printf("%s", str)
  1303  }
  1304  
  1305  // EditConfig edits the config file interactively
  1306  func EditConfig() {
  1307  	for {
  1308  		haveRemotes := len(getConfigData().GetSectionList()) != 0
  1309  		what := []string{"eEdit existing remote", "nNew remote", "dDelete remote", "rRename remote", "cCopy remote", "sSet configuration password", "qQuit config"}
  1310  		if haveRemotes {
  1311  			fmt.Printf("Current remotes:\n\n")
  1312  			ShowRemotes()
  1313  			fmt.Printf("\n")
  1314  		} else {
  1315  			fmt.Printf("No remotes found - make a new one\n")
  1316  			// take 2nd item and last 2 items of menu list
  1317  			what = append(what[1:2], what[len(what)-2:]...)
  1318  		}
  1319  		switch i := Command(what); i {
  1320  		case 'e':
  1321  			name := ChooseRemote()
  1322  			fs := MustFindByName(name)
  1323  			EditRemote(fs, name)
  1324  		case 'n':
  1325  			NewRemote(NewRemoteName())
  1326  		case 'd':
  1327  			name := ChooseRemote()
  1328  			DeleteRemote(name)
  1329  		case 'r':
  1330  			RenameRemote(ChooseRemote())
  1331  		case 'c':
  1332  			CopyRemote(ChooseRemote())
  1333  		case 's':
  1334  			SetPassword()
  1335  		case 'q':
  1336  			return
  1337  
  1338  		}
  1339  	}
  1340  }
  1341  
  1342  // SetPassword will allow the user to modify the current
  1343  // configuration encryption settings.
  1344  func SetPassword() {
  1345  	for {
  1346  		if len(configKey) > 0 {
  1347  			fmt.Println("Your configuration is encrypted.")
  1348  			what := []string{"cChange Password", "uUnencrypt configuration", "qQuit to main menu"}
  1349  			switch i := Command(what); i {
  1350  			case 'c':
  1351  				changeConfigPassword()
  1352  				SaveConfig()
  1353  				fmt.Println("Password changed")
  1354  				continue
  1355  			case 'u':
  1356  				configKey = nil
  1357  				SaveConfig()
  1358  				continue
  1359  			case 'q':
  1360  				return
  1361  			}
  1362  
  1363  		} else {
  1364  			fmt.Println("Your configuration is not encrypted.")
  1365  			fmt.Println("If you add a password, you will protect your login information to cloud services.")
  1366  			what := []string{"aAdd Password", "qQuit to main menu"}
  1367  			switch i := Command(what); i {
  1368  			case 'a':
  1369  				changeConfigPassword()
  1370  				SaveConfig()
  1371  				fmt.Println("Password set")
  1372  				continue
  1373  			case 'q':
  1374  				return
  1375  			}
  1376  		}
  1377  	}
  1378  }
  1379  
  1380  // Authorize is for remote authorization of headless machines.
  1381  //
  1382  // It expects 1 or 3 arguments
  1383  //
  1384  //   rclone authorize "fs name"
  1385  //   rclone authorize "fs name" "client id" "client secret"
  1386  func Authorize(args []string, noAutoBrowser bool) {
  1387  	defer suppressConfirm()()
  1388  	switch len(args) {
  1389  	case 1, 3:
  1390  	default:
  1391  		log.Fatalf("Invalid number of arguments: %d", len(args))
  1392  	}
  1393  	newType := args[0]
  1394  	f := fs.MustFind(newType)
  1395  	if f.Config == nil {
  1396  		log.Fatalf("Can't authorize fs %q", newType)
  1397  	}
  1398  	// Name used for temporary fs
  1399  	name := "**temp-fs**"
  1400  
  1401  	// Make sure we delete it
  1402  	defer DeleteRemote(name)
  1403  
  1404  	// Indicate that we are running rclone authorize
  1405  	getConfigData().SetValue(name, ConfigAuthorize, "true")
  1406  	if noAutoBrowser {
  1407  		getConfigData().SetValue(name, ConfigAuthNoBrowser, "true")
  1408  	}
  1409  
  1410  	if len(args) == 3 {
  1411  		getConfigData().SetValue(name, ConfigClientID, args[1])
  1412  		getConfigData().SetValue(name, ConfigClientSecret, args[2])
  1413  	}
  1414  
  1415  	m := fs.ConfigMap(f, name)
  1416  	f.Config(name, m)
  1417  }
  1418  
  1419  // FileGetFlag gets the config key under section returning the
  1420  // the value and true if found and or ("", false) otherwise
  1421  func FileGetFlag(section, key string) (string, bool) {
  1422  	newValue, err := getConfigData().GetValue(section, key)
  1423  	return newValue, err == nil
  1424  }
  1425  
  1426  // FileGet gets the config key under section returning the
  1427  // default or empty string if not set.
  1428  //
  1429  // It looks up defaults in the environment if they are present
  1430  func FileGet(section, key string, defaultVal ...string) string {
  1431  	envKey := fs.ConfigToEnv(section, key)
  1432  	newValue, found := os.LookupEnv(envKey)
  1433  	if found {
  1434  		defaultVal = []string{newValue}
  1435  	}
  1436  	return getConfigData().MustValue(section, key, defaultVal...)
  1437  }
  1438  
  1439  // FileSet sets the key in section to value.  It doesn't save
  1440  // the config file.
  1441  func FileSet(section, key, value string) {
  1442  	if value != "" {
  1443  		getConfigData().SetValue(section, key, value)
  1444  	} else {
  1445  		FileDeleteKey(section, key)
  1446  	}
  1447  }
  1448  
  1449  // FileDeleteKey deletes the config key in the config file.
  1450  // It returns true if the key was deleted,
  1451  // or returns false if the section or key didn't exist.
  1452  func FileDeleteKey(section, key string) bool {
  1453  	return getConfigData().DeleteKey(section, key)
  1454  }
  1455  
  1456  var matchEnv = regexp.MustCompile(`^RCLONE_CONFIG_(.*?)_TYPE=.*$`)
  1457  
  1458  // FileRefresh ensures the latest configFile is loaded from disk
  1459  func FileRefresh() error {
  1460  	reloadedConfigFile, err := loadConfigFile()
  1461  	if err != nil {
  1462  		return err
  1463  	}
  1464  	configFile = reloadedConfigFile
  1465  	return nil
  1466  }
  1467  
  1468  // FileSections returns the sections in the config file
  1469  // including any defined by environment variables.
  1470  func FileSections() []string {
  1471  	sections := getConfigData().GetSectionList()
  1472  	for _, item := range os.Environ() {
  1473  		matches := matchEnv.FindStringSubmatch(item)
  1474  		if len(matches) == 2 {
  1475  			sections = append(sections, strings.ToLower(matches[1]))
  1476  		}
  1477  	}
  1478  	return sections
  1479  }
  1480  
  1481  // DumpRcRemote dumps the config for a single remote
  1482  func DumpRcRemote(name string) (dump rc.Params) {
  1483  	params := rc.Params{}
  1484  	for _, key := range getConfigData().GetKeyList(name) {
  1485  		params[key] = FileGet(name, key)
  1486  	}
  1487  	return params
  1488  }
  1489  
  1490  // DumpRcBlob dumps all the config as an unstructured blob suitable
  1491  // for the rc
  1492  func DumpRcBlob() (dump rc.Params) {
  1493  	dump = rc.Params{}
  1494  	for _, name := range getConfigData().GetSectionList() {
  1495  		dump[name] = DumpRcRemote(name)
  1496  	}
  1497  	return dump
  1498  }
  1499  
  1500  // Dump dumps all the config as a JSON file
  1501  func Dump() error {
  1502  	dump := DumpRcBlob()
  1503  	b, err := json.MarshalIndent(dump, "", "    ")
  1504  	if err != nil {
  1505  		return errors.Wrap(err, "failed to marshal config dump")
  1506  	}
  1507  	_, err = os.Stdout.Write(b)
  1508  	if err != nil {
  1509  		return errors.Wrap(err, "failed to write config dump")
  1510  	}
  1511  	return nil
  1512  }
  1513  
  1514  // makeCacheDir returns a directory to use for caching.
  1515  //
  1516  // Code borrowed from go stdlib until it is made public
  1517  func makeCacheDir() (dir string) {
  1518  	// Compute default location.
  1519  	switch runtime.GOOS {
  1520  	case "windows":
  1521  		dir = os.Getenv("LocalAppData")
  1522  
  1523  	case "darwin":
  1524  		dir = os.Getenv("HOME")
  1525  		if dir != "" {
  1526  			dir += "/Library/Caches"
  1527  		}
  1528  
  1529  	case "plan9":
  1530  		dir = os.Getenv("home")
  1531  		if dir != "" {
  1532  			// Plan 9 has no established per-user cache directory,
  1533  			// but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix.
  1534  			dir += "/lib/cache"
  1535  		}
  1536  
  1537  	default: // Unix
  1538  		// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  1539  		dir = os.Getenv("XDG_CACHE_HOME")
  1540  		if dir == "" {
  1541  			dir = os.Getenv("HOME")
  1542  			if dir != "" {
  1543  				dir += "/.cache"
  1544  			}
  1545  		}
  1546  	}
  1547  
  1548  	// if no dir found then use TempDir - we will have a cachedir!
  1549  	if dir == "" {
  1550  		dir = os.TempDir()
  1551  	}
  1552  	return filepath.Join(dir, "rclone")
  1553  }