269 lines
8.4 KiB
Go
269 lines
8.4 KiB
Go
// Package configflags defines the flags used by rclone. It is
|
|
// decoupled into a separate package so it can be replaced.
|
|
package configflags
|
|
|
|
// Options set by command line flags
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/config"
|
|
"github.com/rclone/rclone/fs/config/flags"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
var (
|
|
// these will get interpreted into fs.Config via SetFlags() below
|
|
verbose int
|
|
quiet bool
|
|
configPath string
|
|
cacheDir string
|
|
tempDir string
|
|
dumpHeaders bool
|
|
dumpBodies bool
|
|
deleteBefore bool
|
|
deleteDuring bool
|
|
deleteAfter bool
|
|
bindAddr string
|
|
disableFeatures string
|
|
dscp string
|
|
uploadHeaders []string
|
|
downloadHeaders []string
|
|
headers []string
|
|
metadataSet []string
|
|
)
|
|
|
|
// AddFlags adds the non filing system specific flags to the command
|
|
func AddFlags(ci *fs.ConfigInfo, flagSet *pflag.FlagSet) {
|
|
flags.AddFlagsFromOptions(flagSet, "", fs.ConfigOptionsInfo)
|
|
|
|
// Add flags we haven't converted into options yet
|
|
flags.CountVarP(flagSet, &verbose, "verbose", "v", "Print lots more stuff (repeat for more)", "Logging,Important")
|
|
flags.BoolVarP(flagSet, &quiet, "quiet", "q", false, "Print as little stuff as possible", "Logging")
|
|
flags.StringVarP(flagSet, &configPath, "config", "", config.GetConfigPath(), "Config file", "Config")
|
|
flags.StringVarP(flagSet, &cacheDir, "cache-dir", "", config.GetCacheDir(), "Directory rclone will use for caching", "Config")
|
|
flags.StringVarP(flagSet, &tempDir, "temp-dir", "", os.TempDir(), "Directory rclone will use for temporary files", "Config")
|
|
flags.BoolVarP(flagSet, &dumpHeaders, "dump-headers", "", false, "Dump HTTP headers - may contain sensitive info", "Debugging")
|
|
flags.BoolVarP(flagSet, &dumpBodies, "dump-bodies", "", false, "Dump HTTP headers and bodies - may contain sensitive info", "Debugging")
|
|
flags.BoolVarP(flagSet, &deleteBefore, "delete-before", "", false, "When synchronizing, delete files on destination before transferring", "Sync")
|
|
flags.BoolVarP(flagSet, &deleteDuring, "delete-during", "", false, "When synchronizing, delete files during transfer", "Sync")
|
|
flags.BoolVarP(flagSet, &deleteAfter, "delete-after", "", false, "When synchronizing, delete files on destination after transferring (default)", "Sync")
|
|
flags.StringVarP(flagSet, &bindAddr, "bind", "", "", "Local address to bind to for outgoing connections, IPv4, IPv6 or name", "Networking")
|
|
flags.StringVarP(flagSet, &disableFeatures, "disable", "", "", "Disable a comma separated list of features (use --disable help to see a list)", "Config")
|
|
flags.StringArrayVarP(flagSet, &uploadHeaders, "header-upload", "", nil, "Set HTTP header for upload transactions", "Networking")
|
|
flags.StringArrayVarP(flagSet, &downloadHeaders, "header-download", "", nil, "Set HTTP header for download transactions", "Networking")
|
|
flags.StringArrayVarP(flagSet, &headers, "header", "", nil, "Set HTTP header for all transactions", "Networking")
|
|
flags.StringArrayVarP(flagSet, &metadataSet, "metadata-set", "", nil, "Add metadata key=value when uploading", "Metadata")
|
|
flags.StringVarP(flagSet, &dscp, "dscp", "", "", "Set DSCP value to connections, value or name, e.g. CS1, LE, DF, AF21", "Networking")
|
|
}
|
|
|
|
// ParseHeaders converts the strings passed in via the header flags into HTTPOptions
|
|
func ParseHeaders(headers []string) []*fs.HTTPOption {
|
|
opts := []*fs.HTTPOption{}
|
|
for _, header := range headers {
|
|
parts := strings.SplitN(header, ":", 2)
|
|
if len(parts) == 1 {
|
|
log.Fatalf("Failed to parse '%s' as an HTTP header. Expecting a string like: 'Content-Encoding: gzip'", header)
|
|
}
|
|
option := &fs.HTTPOption{
|
|
Key: strings.TrimSpace(parts[0]),
|
|
Value: strings.TrimSpace(parts[1]),
|
|
}
|
|
opts = append(opts, option)
|
|
}
|
|
return opts
|
|
}
|
|
|
|
// SetFlags sets flags which aren't part of the config system
|
|
func SetFlags(ci *fs.ConfigInfo) {
|
|
// Process obsolete --dump-headers and --dump-bodies flags
|
|
if dumpHeaders {
|
|
ci.Dump |= fs.DumpHeaders
|
|
fs.Logf(nil, "--dump-headers is obsolete - please use --dump headers instead")
|
|
}
|
|
if dumpBodies {
|
|
ci.Dump |= fs.DumpBodies
|
|
fs.Logf(nil, "--dump-bodies is obsolete - please use --dump bodies instead")
|
|
}
|
|
|
|
// Process -v flag
|
|
if verbose >= 2 {
|
|
ci.LogLevel = fs.LogLevelDebug
|
|
} else if verbose >= 1 {
|
|
ci.LogLevel = fs.LogLevelInfo
|
|
}
|
|
|
|
// Process -q flag
|
|
if quiet {
|
|
if verbose > 0 {
|
|
log.Fatalf("Can't set -v and -q")
|
|
}
|
|
ci.LogLevel = fs.LogLevelError
|
|
}
|
|
|
|
// Can't set log level, -v, -q
|
|
logLevelFlag := pflag.Lookup("log-level")
|
|
if logLevelFlag != nil && logLevelFlag.Changed {
|
|
if verbose > 0 {
|
|
log.Fatalf("Can't set -v and --log-level")
|
|
}
|
|
if quiet {
|
|
log.Fatalf("Can't set -q and --log-level")
|
|
}
|
|
}
|
|
|
|
// Process --delete-before, --delete-during and --delete-after
|
|
switch {
|
|
case deleteBefore && (deleteDuring || deleteAfter),
|
|
deleteDuring && deleteAfter:
|
|
log.Fatalf(`Only one of --delete-before, --delete-during or --delete-after can be used.`)
|
|
case deleteBefore:
|
|
ci.DeleteMode = fs.DeleteModeBefore
|
|
case deleteDuring:
|
|
ci.DeleteMode = fs.DeleteModeDuring
|
|
case deleteAfter:
|
|
ci.DeleteMode = fs.DeleteModeAfter
|
|
default:
|
|
ci.DeleteMode = fs.DeleteModeDefault
|
|
}
|
|
|
|
// Process --bind into IP address
|
|
if bindAddr != "" {
|
|
addrs, err := net.LookupIP(bindAddr)
|
|
if err != nil {
|
|
log.Fatalf("--bind: Failed to parse %q as IP address: %v", bindAddr, err)
|
|
}
|
|
if len(addrs) != 1 {
|
|
log.Fatalf("--bind: Expecting 1 IP address for %q but got %d", bindAddr, len(addrs))
|
|
}
|
|
ci.BindAddr = addrs[0]
|
|
}
|
|
|
|
// Process --disable
|
|
if disableFeatures != "" {
|
|
if disableFeatures == "help" {
|
|
log.Fatalf("Possible backend features are: %s\n", strings.Join(new(fs.Features).List(), ", "))
|
|
}
|
|
ci.DisableFeatures = strings.Split(disableFeatures, ",")
|
|
}
|
|
|
|
// Process --headers-upload, --headers-download, --headers
|
|
if len(uploadHeaders) != 0 {
|
|
ci.UploadHeaders = ParseHeaders(uploadHeaders)
|
|
}
|
|
if len(downloadHeaders) != 0 {
|
|
ci.DownloadHeaders = ParseHeaders(downloadHeaders)
|
|
}
|
|
if len(headers) != 0 {
|
|
ci.Headers = ParseHeaders(headers)
|
|
}
|
|
|
|
// Process --metadata-set
|
|
if len(metadataSet) != 0 {
|
|
ci.MetadataSet = make(fs.Metadata, len(metadataSet))
|
|
for _, kv := range metadataSet {
|
|
equal := strings.IndexRune(kv, '=')
|
|
if equal < 0 {
|
|
log.Fatalf("Failed to parse '%s' as metadata key=value.", kv)
|
|
}
|
|
ci.MetadataSet[strings.ToLower(kv[:equal])] = kv[equal+1:]
|
|
}
|
|
fs.Debugf(nil, "MetadataUpload %v", ci.MetadataSet)
|
|
}
|
|
|
|
// Process --dscp
|
|
if len(dscp) != 0 {
|
|
if value, ok := parseDSCP(dscp); ok {
|
|
ci.TrafficClass = value << 2
|
|
} else {
|
|
log.Fatalf("--dscp: Invalid DSCP name: %v", dscp)
|
|
}
|
|
}
|
|
|
|
// Process --config path
|
|
if err := config.SetConfigPath(configPath); err != nil {
|
|
log.Fatalf("--config: Failed to set %q as config path: %v", configPath, err)
|
|
}
|
|
|
|
// Process --cache-dir path
|
|
if err := config.SetCacheDir(cacheDir); err != nil {
|
|
log.Fatalf("--cache-dir: Failed to set %q as cache dir: %v", cacheDir, err)
|
|
}
|
|
|
|
// Process --temp-dir path
|
|
if err := config.SetTempDir(tempDir); err != nil {
|
|
log.Fatalf("--temp-dir: Failed to set %q as temp dir: %v", tempDir, err)
|
|
}
|
|
|
|
// Process --multi-thread-streams - set whether multi-thread-streams was set
|
|
multiThreadStreamsFlag := pflag.Lookup("multi-thread-streams")
|
|
ci.MultiThreadSet = multiThreadStreamsFlag != nil && multiThreadStreamsFlag.Changed
|
|
|
|
// Reload any changes
|
|
if err := ci.Reload(context.Background()); err != nil {
|
|
log.Fatalf("Failed to reload config changes: %v", err)
|
|
}
|
|
}
|
|
|
|
// parseHeaders converts DSCP names to value
|
|
func parseDSCP(dscp string) (uint8, bool) {
|
|
if s, err := strconv.ParseUint(dscp, 10, 6); err == nil {
|
|
return uint8(s), true
|
|
}
|
|
dscp = strings.ToUpper(dscp)
|
|
switch dscp {
|
|
case "BE":
|
|
fallthrough
|
|
case "DF":
|
|
fallthrough
|
|
case "CS0":
|
|
return 0x00, true
|
|
case "CS1":
|
|
return 0x08, true
|
|
case "AF11":
|
|
return 0x0A, true
|
|
case "AF12":
|
|
return 0x0C, true
|
|
case "AF13":
|
|
return 0x0E, true
|
|
case "CS2":
|
|
return 0x10, true
|
|
case "AF21":
|
|
return 0x12, true
|
|
case "AF22":
|
|
return 0x14, true
|
|
case "AF23":
|
|
return 0x16, true
|
|
case "CS3":
|
|
return 0x18, true
|
|
case "AF31":
|
|
return 0x1A, true
|
|
case "AF32":
|
|
return 0x1C, true
|
|
case "AF33":
|
|
return 0x1E, true
|
|
case "CS4":
|
|
return 0x20, true
|
|
case "AF41":
|
|
return 0x22, true
|
|
case "AF42":
|
|
return 0x24, true
|
|
case "AF43":
|
|
return 0x26, true
|
|
case "CS5":
|
|
return 0x28, true
|
|
case "EF":
|
|
return 0x2E, true
|
|
case "CS6":
|
|
return 0x30, true
|
|
case "LE":
|
|
return 0x01, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|