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 }