diff --git a/backend/drive/drive.go b/backend/drive/drive.go index a8c2cea53..ec4a5141b 100644 --- a/backend/drive/drive.go +++ b/backend/drive/drive.go @@ -718,12 +718,16 @@ func parseExtensions(extensionsIn ...string) (extensions, mimeTypes []string, er // Figure out if the user wants to use a team drive func configTeamDrive(opt *Options, m configmap.Mapper, name string) error { + // Stop if we are running non-interactive config + if fs.Config.AutoConfirm { + return nil + } if opt.TeamDriveID == "" { fmt.Printf("Configure this as a team drive?\n") } else { fmt.Printf("Change current team drive ID %q?\n", opt.TeamDriveID) } - if !config.ConfirmWithDefault(false) { + if !config.Confirm() { return nil } client, err := createOAuthClient(opt, name, m) diff --git a/backend/onedrive/onedrive.go b/backend/onedrive/onedrive.go index 44a5eb8ca..890a7d48e 100644 --- a/backend/onedrive/onedrive.go +++ b/backend/onedrive/onedrive.go @@ -75,9 +75,8 @@ func init() { return } - // Are we running headless? - if automatic, _ := m.Get(config.ConfigAutomatic); automatic != "" { - // Yes, okay we are done + // Stop if we are running non-interactive config + if fs.Config.AutoConfirm { return } @@ -199,7 +198,7 @@ func init() { fmt.Printf("Found drive '%s' of type '%s', URL: %s\nIs that okay?\n", rootItem.Name, rootItem.ParentReference.DriveType, rootItem.WebURL) // This does not work, YET :) - if !config.Confirm() { + if !config.ConfirmWithConfig(m, "config_drive_ok", true) { log.Fatalf("Cancelled by user") } diff --git a/cmd/config/config.go b/cmd/config/config.go index 659b23d4d..f6c0c6234 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -93,6 +93,15 @@ For example to make a swift remote of name myremote using auto config you would do: rclone config create myremote swift env_auth true + +Note that if the config process would normally ask a question the +default is taken. Each time that happens rclone will print a message +saying how to affect the value taken. + +So for example if you wanted to configure a Google Drive remote but +using remote authorization you would do this: + + rclone config create mydrive drive config_is_local false `, RunE: func(command *cobra.Command, args []string) error { cmd.CheckArgs(2, 256, command, args) @@ -119,6 +128,11 @@ in pairs of . For example to update the env_auth field of a remote of name myremote you would do: rclone config update myremote swift env_auth true + +If the remote uses oauth the token will be updated, if you don't +require this add an extra parameter thus: + + rclone config update myremote swift env_auth true config_refresh_token false `, RunE: func(command *cobra.Command, args []string) error { cmd.CheckArgs(3, 256, command, args) diff --git a/fs/config/config.go b/fs/config/config.go index da915bbeb..7076937d6 100644 --- a/fs/config/config.go +++ b/fs/config/config.go @@ -27,6 +27,7 @@ import ( "github.com/Unknwon/goconfig" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/accounting" + "github.com/ncw/rclone/fs/config/configmap" "github.com/ncw/rclone/fs/config/configstruct" "github.com/ncw/rclone/fs/config/obscure" "github.com/ncw/rclone/fs/driveletter" @@ -57,8 +58,8 @@ const ( // ConfigTokenURL is the config key used to store the token server endpoint ConfigTokenURL = "token_url" - // ConfigAutomatic indicates that we want non-interactive configuration - ConfigAutomatic = "config_automatic" + // ConfigAuthorize indicates that we just want "rclone authorize" + ConfigAuthorize = "config_authorize" ) // Global @@ -639,21 +640,38 @@ func Command(commands []string) byte { } } -// ConfirmWithDefault asks the user for Yes or No and returns true or false. -// -// If AutoConfirm is set, it will return the Default value passed in -func ConfirmWithDefault(Default bool) bool { - if fs.Config.AutoConfirm { - return Default - } - return Command([]string{"yYes", "nNo"}) == 'y' -} - // Confirm asks the user for Yes or No and returns true or false // // If AutoConfirm is set, it will return true func Confirm() bool { - return ConfirmWithDefault(true) + return Command([]string{"yYes", "nNo"}) == 'y' +} + +// ConfirmWithConfig asks the user for Yes or No and returns true or +// false. +// +// If AutoConfirm is set, it will look up the value in m and return +// that, but if it isn't set then it will return the Default value +// passed in +func ConfirmWithConfig(m configmap.Getter, configName string, Default bool) bool { + if fs.Config.AutoConfirm { + configString, ok := m.Get(configName) + if ok { + configValue, err := strconv.ParseBool(configString) + if err != nil { + fs.Errorf(nil, "Failed to parse config parameter %s=%q as boolean - using default %v: %v", configName, configString, Default, err) + } else { + Default = configValue + } + } + answer := "No" + if Default { + answer = "Yes" + } + fmt.Printf("Auto confirm is set: answering %s, override by setting config parameter %s=%v\n", answer, configName, !Default) + return Default + } + return Confirm() } // Choose one of the defaults or type a new string if newOk is set @@ -943,8 +961,6 @@ func CreateRemote(name string, provider string, keyValues rc.Params) error { getConfigData().DeleteSection(name) // Set the type getConfigData().SetValue(name, "type", provider) - // Show this is automatically configured - getConfigData().SetValue(name, ConfigAutomatic, "yes") // Set the remaining values return UpdateRemote(name, keyValues) } @@ -1227,6 +1243,7 @@ func SetPassword() { // rclone authorize "fs name" // rclone authorize "fs name" "client id" "client secret" func Authorize(args []string) { + defer suppressConfirm()() switch len(args) { case 1, 3: default: @@ -1243,8 +1260,8 @@ func Authorize(args []string) { // Make sure we delete it defer DeleteRemote(name) - // Indicate that we want fully automatic configuration. - getConfigData().SetValue(name, ConfigAutomatic, "yes") + // Indicate that we are running rclone authorize + getConfigData().SetValue(name, ConfigAuthorize, "true") if len(args) == 3 { getConfigData().SetValue(name, ConfigClientID, args[1]) getConfigData().SetValue(name, ConfigClientSecret, args[2]) diff --git a/lib/oauthutil/oauthutil.go b/lib/oauthutil/oauthutil.go index 5f619a801..c6fb2b817 100644 --- a/lib/oauthutil/oauthutil.go +++ b/lib/oauthutil/oauthutil.go @@ -358,18 +358,26 @@ func ConfigErrorCheck(id, name string, m configmap.Mapper, errorHandler func(*ht func doConfig(id, name string, m configmap.Mapper, errorHandler func(*http.Request) AuthError, oauthConfig *oauth2.Config, offline bool, opts []oauth2.AuthCodeOption) error { oauthConfig, changed := overrideCredentials(name, m, oauthConfig) - auto, ok := m.Get(config.ConfigAutomatic) - automatic := ok && auto != "" + authorizeOnlyValue, ok := m.Get(config.ConfigAuthorize) + authorizeOnly := ok && authorizeOnlyValue != "" // set if being run by "rclone authorize" // See if already have a token tokenString, ok := m.Get("token") if ok && tokenString != "" { fmt.Printf("Already have a token - refresh?\n") - if !config.Confirm() { + if !config.ConfirmWithConfig(m, "config_refresh_token", true) { return nil } } + // Ask the user whether they are using a local machine + isLocal := func() bool { + fmt.Printf("Use auto config?\n") + fmt.Printf(" * Say Y if not sure\n") + fmt.Printf(" * Say N if you are working on a remote or headless machine\n") + return config.ConfirmWithConfig(m, "config_is_local", true) + } + // Detect whether we should use internal web server useWebServer := false switch oauthConfig.RedirectURL { @@ -378,14 +386,10 @@ func doConfig(id, name string, m configmap.Mapper, errorHandler func(*http.Reque fmt.Printf("Make sure your Redirect URL is set to %q in your custom config.\n", oauthConfig.RedirectURL) } useWebServer = true - if automatic { + if authorizeOnly { break } - fmt.Printf("Use auto config?\n") - fmt.Printf(" * Say Y if not sure\n") - fmt.Printf(" * Say N if you are working on a remote or headless machine\n") - auto := config.Confirm() - if !auto { + if !isLocal() { fmt.Printf("For this to work, you will need rclone available on a machine that has a web browser available.\n") fmt.Printf("Execute the following on your machine:\n") if changed { @@ -407,12 +411,9 @@ func doConfig(id, name string, m configmap.Mapper, errorHandler func(*http.Reque return PutToken(name, m, token, true) } case TitleBarRedirectURL: - useWebServer = automatic - if !automatic { - fmt.Printf("Use auto config?\n") - fmt.Printf(" * Say Y if not sure\n") - fmt.Printf(" * Say N if you are working on a remote or headless machine or Y didn't work\n") - useWebServer = config.Confirm() + useWebServer = authorizeOnly + if !authorizeOnly { + useWebServer = isLocal() } if useWebServer { // copy the config and set to use the internal webserver @@ -479,12 +480,12 @@ func doConfig(id, name string, m configmap.Mapper, errorHandler func(*http.Reque } // Print code if we do automatic retrieval - if automatic { + if authorizeOnly { result, err := json.Marshal(token) if err != nil { return errors.Wrap(err, "failed to marshal token") } - fmt.Printf("Paste the following into your remote machine --->\n%s\n<---End paste", result) + fmt.Printf("Paste the following into your remote machine --->\n%s\n<---End paste\n", result) } return PutToken(name, m, token, true) }