2016-08-05 18:12:27 +02:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2019-08-12 12:08:07 +02:00
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sort"
|
2020-05-18 20:53:13 +02:00
|
|
|
"strings"
|
2018-11-04 19:23:12 +01:00
|
|
|
|
2019-08-12 12:08:07 +02:00
|
|
|
"github.com/pkg/errors"
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/cmd"
|
2019-08-12 12:08:07 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs/config"
|
2019-10-11 17:55:04 +02:00
|
|
|
"github.com/rclone/rclone/fs/config/flags"
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs/rc"
|
2016-08-05 18:12:27 +02:00
|
|
|
"github.com/spf13/cobra"
|
2020-05-12 15:24:53 +02:00
|
|
|
"github.com/spf13/pflag"
|
2016-08-05 18:12:27 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2017-10-14 12:50:41 +02:00
|
|
|
cmd.Root.AddCommand(configCommand)
|
|
|
|
configCommand.AddCommand(configEditCommand)
|
|
|
|
configCommand.AddCommand(configFileCommand)
|
|
|
|
configCommand.AddCommand(configShowCommand)
|
|
|
|
configCommand.AddCommand(configDumpCommand)
|
|
|
|
configCommand.AddCommand(configProvidersCommand)
|
|
|
|
configCommand.AddCommand(configCreateCommand)
|
|
|
|
configCommand.AddCommand(configUpdateCommand)
|
|
|
|
configCommand.AddCommand(configDeleteCommand)
|
2017-11-15 15:44:45 +01:00
|
|
|
configCommand.AddCommand(configPasswordCommand)
|
2019-08-12 12:08:07 +02:00
|
|
|
configCommand.AddCommand(configReconnectCommand)
|
|
|
|
configCommand.AddCommand(configDisconnectCommand)
|
|
|
|
configCommand.AddCommand(configUserInfoCommand)
|
2016-08-05 18:12:27 +02:00
|
|
|
}
|
|
|
|
|
2017-10-14 12:50:41 +02:00
|
|
|
var configCommand = &cobra.Command{
|
|
|
|
Use: "config",
|
2016-08-05 18:12:27 +02:00
|
|
|
Short: `Enter an interactive configuration session.`,
|
2017-10-14 12:50:41 +02:00
|
|
|
Long: `Enter an interactive configuration session where you can setup new
|
|
|
|
remotes and manage existing ones. You may also set or remove a
|
|
|
|
password to protect your configuration.
|
|
|
|
`,
|
|
|
|
Run: func(command *cobra.Command, args []string) {
|
|
|
|
cmd.CheckArgs(0, 0, command, args)
|
2018-01-12 17:30:54 +01:00
|
|
|
config.EditConfig()
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
2017-09-19 17:59:19 +02:00
|
|
|
|
2017-10-14 12:50:41 +02:00
|
|
|
var configEditCommand = &cobra.Command{
|
|
|
|
Use: "edit",
|
|
|
|
Short: configCommand.Short,
|
|
|
|
Long: configCommand.Long,
|
|
|
|
Run: configCommand.Run,
|
|
|
|
}
|
2017-09-19 17:59:19 +02:00
|
|
|
|
2017-10-14 12:50:41 +02:00
|
|
|
var configFileCommand = &cobra.Command{
|
|
|
|
Use: "file",
|
|
|
|
Short: `Show path of configuration file in use.`,
|
2016-08-05 18:12:27 +02:00
|
|
|
Run: func(command *cobra.Command, args []string) {
|
2017-10-14 12:50:41 +02:00
|
|
|
cmd.CheckArgs(0, 0, command, args)
|
2018-01-12 17:30:54 +01:00
|
|
|
config.ShowConfigLocation()
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configShowCommand = &cobra.Command{
|
|
|
|
Use: "show [<remote>]",
|
|
|
|
Short: `Print (decrypted) config file, or the config for a single remote.`,
|
|
|
|
Run: func(command *cobra.Command, args []string) {
|
|
|
|
cmd.CheckArgs(0, 1, command, args)
|
2017-09-19 17:59:19 +02:00
|
|
|
if len(args) == 0 {
|
2018-01-12 17:30:54 +01:00
|
|
|
config.ShowConfig()
|
2017-10-14 12:50:41 +02:00
|
|
|
} else {
|
2020-05-18 20:53:13 +02:00
|
|
|
name := strings.TrimRight(args[0], ":")
|
|
|
|
config.ShowRemote(name)
|
2017-09-19 17:59:19 +02:00
|
|
|
}
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configDumpCommand = &cobra.Command{
|
|
|
|
Use: "dump",
|
|
|
|
Short: `Dump the config file as JSON.`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(0, 0, command, args)
|
2018-01-12 17:30:54 +01:00
|
|
|
return config.Dump()
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configProvidersCommand = &cobra.Command{
|
|
|
|
Use: "providers",
|
|
|
|
Short: `List in JSON format all the providers and options.`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(0, 0, command, args)
|
2018-01-12 17:30:54 +01:00
|
|
|
return config.JSONListProviders()
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-05-12 15:24:53 +02:00
|
|
|
var (
|
|
|
|
configObscure bool
|
|
|
|
configNoObscure bool
|
|
|
|
)
|
|
|
|
|
|
|
|
const configPasswordHelp = `
|
|
|
|
If any of the parameters passed is a password field, then rclone will
|
|
|
|
automatically obscure them if they aren't already obscured before
|
|
|
|
putting them in the config file.
|
|
|
|
|
|
|
|
**NB** If the password parameter is 22 characters or longer and
|
|
|
|
consists only of base64 characters then rclone can get confused about
|
|
|
|
whether the password is already obscured or not and put unobscured
|
|
|
|
passwords into the config file. If you want to be 100% certain that
|
|
|
|
the passwords get obscured then use the "--obscure" flag, or if you
|
|
|
|
are 100% certain you are already passing obscured passwords then use
|
|
|
|
"--no-obscure". You can also set osbscured passwords using the
|
|
|
|
"rclone config password" command.
|
|
|
|
`
|
|
|
|
|
2017-10-14 12:50:41 +02:00
|
|
|
var configCreateCommand = &cobra.Command{
|
2020-05-22 13:22:52 +02:00
|
|
|
Use: "create `name` `type` [`key` `value`]*",
|
2017-10-14 12:50:41 +02:00
|
|
|
Short: `Create a new remote with name, type and options.`,
|
|
|
|
Long: `
|
2020-05-22 13:22:52 +02:00
|
|
|
Create a new remote of ` + "`name`" + ` with ` + "`type`" + ` and options. The options
|
|
|
|
should be passed in pairs of ` + "`key` `value`" + `.
|
2017-10-14 12:50:41 +02:00
|
|
|
|
|
|
|
For example to make a swift remote of name myremote using auto config
|
|
|
|
you would do:
|
|
|
|
|
|
|
|
rclone config create myremote swift env_auth true
|
2019-01-17 16:01:13 +01:00
|
|
|
|
|
|
|
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.
|
2020-05-12 15:24:53 +02:00
|
|
|
` + configPasswordHelp + `
|
2019-01-17 16:01:13 +01:00
|
|
|
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
|
2017-10-14 12:50:41 +02:00
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(2, 256, command, args)
|
2018-11-04 19:23:12 +01:00
|
|
|
in, err := argsToMap(args[2:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-12 15:24:53 +02:00
|
|
|
err = config.CreateRemote(args[0], args[1], in, configObscure, configNoObscure)
|
2018-11-04 19:23:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
config.ShowRemote(args[0])
|
|
|
|
return nil
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-05-12 15:24:53 +02:00
|
|
|
func init() {
|
|
|
|
for _, cmdFlags := range []*pflag.FlagSet{configCreateCommand.Flags(), configUpdateCommand.Flags()} {
|
|
|
|
flags.BoolVarP(cmdFlags, &configObscure, "obscure", "", false, "Force any passwords to be obscured.")
|
|
|
|
flags.BoolVarP(cmdFlags, &configNoObscure, "no-obscure", "", false, "Force any passwords not to be obscured.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-14 12:50:41 +02:00
|
|
|
var configUpdateCommand = &cobra.Command{
|
2020-05-22 13:22:52 +02:00
|
|
|
Use: "update `name` [`key` `value`]+",
|
2017-10-14 12:50:41 +02:00
|
|
|
Short: `Update options in an existing remote.`,
|
|
|
|
Long: `
|
|
|
|
Update an existing remote's options. The options should be passed in
|
2020-05-22 13:22:52 +02:00
|
|
|
in pairs of ` + "`key` `value`" + `.
|
2017-10-14 12:50:41 +02:00
|
|
|
|
2019-06-10 18:58:47 +02:00
|
|
|
For example to update the env_auth field of a remote of name myremote
|
|
|
|
you would do:
|
2017-10-14 12:50:41 +02:00
|
|
|
|
|
|
|
rclone config update myremote swift env_auth true
|
2020-05-12 15:24:53 +02:00
|
|
|
` + configPasswordHelp + `
|
2020-02-10 16:07:40 +01:00
|
|
|
If the remote uses OAuth the token will be updated, if you don't
|
2019-01-17 16:01:13 +01:00
|
|
|
require this add an extra parameter thus:
|
|
|
|
|
|
|
|
rclone config update myremote swift env_auth true config_refresh_token false
|
2017-10-14 12:50:41 +02:00
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(3, 256, command, args)
|
2018-11-04 19:23:12 +01:00
|
|
|
in, err := argsToMap(args[1:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-12 15:24:53 +02:00
|
|
|
err = config.UpdateRemote(args[0], in, configObscure, configNoObscure)
|
2018-11-04 19:23:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
config.ShowRemote(args[0])
|
|
|
|
return nil
|
2017-10-14 12:50:41 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configDeleteCommand = &cobra.Command{
|
2020-05-22 13:22:52 +02:00
|
|
|
Use: "delete `name`",
|
|
|
|
Short: "Delete an existing remote `name`.",
|
2017-10-14 12:50:41 +02:00
|
|
|
Run: func(command *cobra.Command, args []string) {
|
|
|
|
cmd.CheckArgs(1, 1, command, args)
|
2018-01-12 17:30:54 +01:00
|
|
|
config.DeleteRemote(args[0])
|
2016-08-05 18:12:27 +02:00
|
|
|
},
|
|
|
|
}
|
2017-11-15 15:44:45 +01:00
|
|
|
|
|
|
|
var configPasswordCommand = &cobra.Command{
|
2020-05-22 13:22:52 +02:00
|
|
|
Use: "password `name` [`key` `value`]+",
|
2017-11-15 15:44:45 +01:00
|
|
|
Short: `Update password in an existing remote.`,
|
|
|
|
Long: `
|
|
|
|
Update an existing remote's password. The password
|
2020-05-22 13:22:52 +02:00
|
|
|
should be passed in pairs of ` + "`key` `value`" + `.
|
2017-11-15 15:44:45 +01:00
|
|
|
|
|
|
|
For example to set password of a remote of name myremote you would do:
|
|
|
|
|
|
|
|
rclone config password myremote fieldname mypassword
|
2019-06-10 18:58:47 +02:00
|
|
|
|
|
|
|
This command is obsolete now that "config update" and "config create"
|
|
|
|
both support obscuring passwords directly.
|
2017-11-15 15:44:45 +01:00
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(3, 256, command, args)
|
2018-11-04 19:23:12 +01:00
|
|
|
in, err := argsToMap(args[1:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = config.PasswordRemote(args[0], in)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
config.ShowRemote(args[0])
|
|
|
|
return nil
|
2017-11-15 15:44:45 +01:00
|
|
|
},
|
|
|
|
}
|
2018-11-04 19:23:12 +01:00
|
|
|
|
|
|
|
// This takes a list of arguments in key value key value form and
|
|
|
|
// converts it into a map
|
|
|
|
func argsToMap(args []string) (out rc.Params, err error) {
|
|
|
|
if len(args)%2 != 0 {
|
|
|
|
return nil, errors.New("found key without value")
|
|
|
|
}
|
|
|
|
out = rc.Params{}
|
|
|
|
// Set the config
|
|
|
|
for i := 0; i < len(args); i += 2 {
|
|
|
|
out[args[i]] = args[i+1]
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
2019-08-12 12:08:07 +02:00
|
|
|
|
|
|
|
var configReconnectCommand = &cobra.Command{
|
|
|
|
Use: "reconnect remote:",
|
|
|
|
Short: `Re-authenticates user with remote.`,
|
|
|
|
Long: `
|
|
|
|
This reconnects remote: passed in to the cloud storage system.
|
|
|
|
|
|
|
|
To disconnect the remote use "rclone config disconnect".
|
|
|
|
|
|
|
|
This normally means going through the interactive oauth flow again.
|
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(1, 1, command, args)
|
|
|
|
fsInfo, configName, _, config, err := fs.ConfigFs(args[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if fsInfo.Config == nil {
|
|
|
|
return errors.Errorf("%s: doesn't support Reconnect", configName)
|
|
|
|
}
|
|
|
|
fsInfo.Config(configName, config)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configDisconnectCommand = &cobra.Command{
|
|
|
|
Use: "disconnect remote:",
|
|
|
|
Short: `Disconnects user from remote`,
|
|
|
|
Long: `
|
|
|
|
This disconnects the remote: passed in to the cloud storage system.
|
|
|
|
|
|
|
|
This normally means revoking the oauth token.
|
|
|
|
|
|
|
|
To reconnect use "rclone config reconnect".
|
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(1, 1, command, args)
|
|
|
|
f := cmd.NewFsSrc(args)
|
|
|
|
doDisconnect := f.Features().Disconnect
|
|
|
|
if doDisconnect == nil {
|
|
|
|
return errors.Errorf("%v doesn't support Disconnect", f)
|
|
|
|
}
|
|
|
|
err := doDisconnect(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "Disconnect call failed")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
jsonOutput bool
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2019-10-11 17:55:04 +02:00
|
|
|
flags.BoolVarP(configUserInfoCommand.Flags(), &jsonOutput, "json", "", false, "Format output as JSON")
|
2019-08-12 12:08:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var configUserInfoCommand = &cobra.Command{
|
|
|
|
Use: "userinfo remote:",
|
|
|
|
Short: `Prints info about logged in user of remote.`,
|
|
|
|
Long: `
|
|
|
|
This prints the details of the person logged in to the cloud storage
|
|
|
|
system.
|
|
|
|
`,
|
|
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
|
|
cmd.CheckArgs(1, 1, command, args)
|
|
|
|
f := cmd.NewFsSrc(args)
|
|
|
|
doUserInfo := f.Features().UserInfo
|
|
|
|
if doUserInfo == nil {
|
|
|
|
return errors.Errorf("%v doesn't support UserInfo", f)
|
|
|
|
}
|
|
|
|
u, err := doUserInfo(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "UserInfo call failed")
|
|
|
|
}
|
|
|
|
if jsonOutput {
|
|
|
|
out := json.NewEncoder(os.Stdout)
|
|
|
|
out.SetIndent("", "\t")
|
|
|
|
return out.Encode(u)
|
|
|
|
}
|
|
|
|
var keys []string
|
|
|
|
var maxKeyLen int
|
|
|
|
for key := range u {
|
|
|
|
keys = append(keys, key)
|
|
|
|
if len(key) > maxKeyLen {
|
|
|
|
maxKeyLen = len(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
for _, key := range keys {
|
|
|
|
fmt.Printf("%*s: %s\n", maxKeyLen, key, u[key])
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|