From 3afb2a47987e8da0d356d2b373b9cb1c1f496f5e Mon Sep 17 00:00:00 2001
From: Nick Craig-Wood <nick@craig-wood.com>
Date: Thu, 23 Jan 2020 14:14:58 +0000
Subject: [PATCH] config: use SpaceSepList for argument to --password-command

This is to enable arguments with spaces in.
---
 docs/content/docs.md                 | 14 +++++++++++++-
 fs/config.go                         |  2 +-
 fs/config/config.go                  |  6 ++----
 fs/config/config_test.go             |  8 ++++----
 fs/config/configflags/configflags.go |  2 +-
 5 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/docs/content/docs.md b/docs/content/docs.md
index 5e72b4bf0..a38b6eccf 100644
--- a/docs/content/docs.md
+++ b/docs/content/docs.md
@@ -880,12 +880,24 @@ Rclone will do its best to transfer the best file it has so in
 practice this should not cause a problem.  Think of `--order-by` as
 being more of a best efforts flag rather than a perfect ordering.
 
-### --password-command=STRING ###
+### --password-command SpaceSepList ###
 
 This flag supplies a program which should supply the config password
 when run. This is an alternative to rclone prompting for the password
 or setting the `RCLONE_CONFIG_PASS` variable.
 
+The argument to this should be a command with a space separated list
+of arguments. If one of the arguments has a space in then enclose it
+in `"`, if you want a literal `"` in an argument then enclose the
+argument in `"` and double the `"`. See [CSV encoding](https://godoc.org/encoding/csv)
+for more info.
+
+Eg
+
+    --password-command echo hello
+    --password-command echo "hello with space"
+    --password-command echo "hello with ""quotes"" and space"
+
 See the [Configuration Encryption](#configuration-encryption) for more info.
 
 ### -P, --progress ###
diff --git a/fs/config.go b/fs/config.go
index 8b1b66349..cb1932241 100644
--- a/fs/config.go
+++ b/fs/config.go
@@ -89,7 +89,7 @@ type ConfigInfo struct {
 	StreamingUploadCutoff  SizeSuffix
 	StatsFileNameLength    int
 	AskPassword            bool
-	PasswordCommand        string
+	PasswordCommand        SpaceSepList
 	UseServerModTime       bool
 	MaxTransfer            SizeSuffix
 	MaxBacklog             int
diff --git a/fs/config/config.go b/fs/config/config.go
index d1bb40a94..c7793f412 100644
--- a/fs/config/config.go
+++ b/fs/config/config.go
@@ -274,13 +274,11 @@ func loadConfigFile() (*goconfig.ConfigFile, error) {
 	}
 
 	if len(configKey) == 0 {
-		pwc := fs.Config.PasswordCommand
-		if pwc != "" {
+		if len(fs.Config.PasswordCommand) != 0 {
 			var stdout bytes.Buffer
 			var stderr bytes.Buffer
 
-			args := strings.Fields(pwc)
-			cmd := exec.Command(args[0], args[1:]...)
+			cmd := exec.Command(fs.Config.PasswordCommand[0], fs.Config.PasswordCommand[1:]...)
 
 			cmd.Stdout = &stdout
 			cmd.Stderr = &stderr
diff --git a/fs/config/config_test.go b/fs/config/config_test.go
index f064b0d51..1f7af607a 100644
--- a/fs/config/config_test.go
+++ b/fs/config/config_test.go
@@ -275,12 +275,12 @@ func TestConfigLoadEncryptedWithValidPassCommand(t *testing.T) {
 	oldConfig := fs.Config
 	ConfigPath = "./testdata/encrypted.conf"
 	// using fs.Config.PasswordCommand, correct password
-	fs.Config.PasswordCommand = "echo asdf"
+	fs.Config.PasswordCommand = fs.SpaceSepList{"echo", "asdf"}
 	defer func() {
 		ConfigPath = oldConfigPath
 		configKey = nil // reset password
 		fs.Config = oldConfig
-		fs.Config.PasswordCommand = ""
+		fs.Config.PasswordCommand = nil
 	}()
 
 	configKey = nil // reset password
@@ -302,12 +302,12 @@ func TestConfigLoadEncryptedWithInvalidPassCommand(t *testing.T) {
 	oldConfig := fs.Config
 	ConfigPath = "./testdata/encrypted.conf"
 	// using fs.Config.PasswordCommand, incorrect password
-	fs.Config.PasswordCommand = "echo asdf-blurfl"
+	fs.Config.PasswordCommand = fs.SpaceSepList{"echo", "asdf-blurfl"}
 	defer func() {
 		ConfigPath = oldConfigPath
 		configKey = nil // reset password
 		fs.Config = oldConfig
-		fs.Config.PasswordCommand = ""
+		fs.Config.PasswordCommand = nil
 	}()
 
 	configKey = nil // reset password
diff --git a/fs/config/configflags/configflags.go b/fs/config/configflags/configflags.go
index 2762ea929..e76ff1935 100644
--- a/fs/config/configflags/configflags.go
+++ b/fs/config/configflags/configflags.go
@@ -55,7 +55,7 @@ func AddFlags(flagSet *pflag.FlagSet) {
 	flags.BoolVarP(flagSet, &dumpBodies, "dump-bodies", "", false, "Dump HTTP headers and bodies - may contain sensitive info")
 	flags.BoolVarP(flagSet, &fs.Config.InsecureSkipVerify, "no-check-certificate", "", fs.Config.InsecureSkipVerify, "Do not verify the server SSL certificate. Insecure.")
 	flags.BoolVarP(flagSet, &fs.Config.AskPassword, "ask-password", "", fs.Config.AskPassword, "Allow prompt for password for encrypted configuration.")
-	flags.StringVarP(flagSet, &fs.Config.PasswordCommand, "password-command", "", fs.Config.PasswordCommand, "Command for supplying password for encrypted configuration.")
+	flags.FVarP(flagSet, &fs.Config.PasswordCommand, "password-command", "", "Command for supplying password for encrypted configuration.")
 	flags.BoolVarP(flagSet, &deleteBefore, "delete-before", "", false, "When synchronizing, delete files on destination before transferring")
 	flags.BoolVarP(flagSet, &deleteDuring, "delete-during", "", false, "When synchronizing, delete files during transfer")
 	flags.BoolVarP(flagSet, &deleteAfter, "delete-after", "", false, "When synchronizing, delete files on destination after transferring (default)")