diff --git a/cmd/consts.go b/cmd/consts.go new file mode 100644 index 0000000..3735762 --- /dev/null +++ b/cmd/consts.go @@ -0,0 +1,17 @@ +package cmd + +const ( + // Config file name and location. + configFileName = "auth.json" + appName = "dbxcli" + configBase = ".config" + + // Environment variable names. + tokensEnv = "DROPBOX_TOKENS" + personalAppKeyEnv = "DROPBOX_PERSONAL_APP_KEY" + personalAppSecretEnv = "DROPBOX_PERSONAL_APP_SECRET" + teamAccessAppKeyEnv = "DROPBOX_TEAM_APP_KEY" + teamAccessAppSecretEnv = "DROPBOX_TEAM_APP_SECRET" + teamManageAppKeyEnv = "DROPBOX_MANAGE_APP_KEY" + teamManageAppSecretEnv = "DROPBOX_MANAGE_APP_SECRET" +) diff --git a/cmd/helpers.go b/cmd/helpers.go new file mode 100644 index 0000000..7140aba --- /dev/null +++ b/cmd/helpers.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "path" +) + +// configFile returns the path to the config file. +func configFile() (string, error) { + dir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("user homedir: %w", err) + } + + return path.Join(dir, configBase, appName, configFileName), nil +} + +// readTokens returns a token map read from either the +// DROPBOX_TOKENS environment variable or the filePath, in that +// order. +func readTokens(filePath string) (TokenMap, error) { + var data []byte + if envTokens := os.Getenv(tokensEnv); envTokens != "" { + data = []byte(envTokens) + } else { + var err error + if data, err = os.ReadFile(filePath); err != nil { + if os.IsNotExist(err) { + return make(TokenMap), nil + } + return nil, fmt.Errorf("read tokens: %w", err) + } + } + + var tokens TokenMap + if err := json.Unmarshal(data, &tokens); err != nil { + return nil, fmt.Errorf("decode tokens: %w", err) + } + + return tokens, nil +} diff --git a/cmd/logout.go b/cmd/logout.go index d48dedd..4bf4288 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -15,22 +15,20 @@ package cmd import ( + "fmt" "os" - "path" "github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox" "github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/auth" - "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" ) // Command logout revokes all saved API tokens and deletes auth.json. func logout(cmd *cobra.Command, args []string) error { - dir, err := homedir.Dir() + filePath, err := configFile() if err != nil { - return err + return fmt.Errorf("config file: %w", err) } - filePath := path.Join(dir, ".config", "dbxcli", configFileName) tokMap, err := readTokens(filePath) if err != nil { diff --git a/cmd/put.go b/cmd/put.go index ac3988f..a98a2bf 100644 --- a/cmd/put.go +++ b/cmd/put.go @@ -19,7 +19,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "os" "path" @@ -96,7 +95,7 @@ func uploadChunked(dbx files.Client, r io.Reader, commitInfo *files.CommitInfo, written := int64(0) for written < sizeTotal { - data, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: chunkSize}) + data, err := io.ReadAll(&io.LimitedReader{R: r, N: chunkSize}) if err != nil { return err } diff --git a/cmd/root.go b/cmd/root.go index 18da322..634e9b9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,9 +17,7 @@ package cmd import ( "encoding/json" "fmt" - "io/ioutil" "os" - "path" "path/filepath" "strings" @@ -27,13 +25,11 @@ import ( "github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox" "github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/files" - "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "golang.org/x/net/context" ) const ( - configFileName = "auth.json" tokenPersonal = "personal" tokenTeamAccess = "teamAccess" tokenTeamManage = "teamManage" @@ -107,38 +103,22 @@ func makeRelocationArg(s string, d string) (arg *files.RelocationArg, err error) return } -func readTokens(filePath string) (TokenMap, error) { - b, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, err - } - - var tokens TokenMap - if json.Unmarshal(b, &tokens) != nil { - return nil, err - } - - return tokens, nil -} - -func writeTokens(filePath string, tokens TokenMap) { - // Check if file exists - if _, err := os.Stat(filePath); os.IsNotExist(err) { - // Doesn't exist; lets create it - err = os.MkdirAll(filepath.Dir(filePath), 0700) - if err != nil { - return - } +func writeTokens(filePath string, tokens TokenMap) error { + // Ensure config directory exists. + if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { + return fmt.Errorf("create config directory: %w", err) } - // At this point, file must exist. Lets (over)write it. b, err := json.Marshal(tokens) if err != nil { - return + return fmt.Errorf("encode tokens: %w", err) } - if err = ioutil.WriteFile(filePath, b, 0600); err != nil { - return + + if err = os.WriteFile(filePath, b, 0600); err != nil { + return fmt.Errorf("write config: %w", err) } + + return nil } func tokenType(cmd *cobra.Command) string { @@ -151,29 +131,30 @@ func tokenType(cmd *cobra.Command) string { return tokenPersonal } -func initDbx(cmd *cobra.Command, args []string) (err error) { +func initDbx(cmd *cobra.Command, args []string) error { verbose, _ := cmd.Flags().GetBool("verbose") asMember, _ := cmd.Flags().GetString("as-member") domain, _ := cmd.Flags().GetString("domain") - dir, err := homedir.Dir() - if err != nil { - return - } - filePath := path.Join(dir, ".config", "dbxcli", configFileName) tokType := tokenType(cmd) conf := oauthConfig(tokType, domain) + filePath, err := configFile() + if err != nil { + return fmt.Errorf("config file: %w", err) + } + tokenMap, err := readTokens(filePath) - if tokenMap == nil { - tokenMap = make(TokenMap) + if err != nil { + return fmt.Errorf("read tokens: %w", err) } + if tokenMap[domain] == nil { tokenMap[domain] = make(map[string]string) } - tokens := tokenMap[domain] - if err != nil || tokens[tokType] == "" { + tokens := tokenMap[domain] + if tokens[tokType] == "" { fmt.Printf("1. Go to %v\n", conf.AuthCodeURL("state")) fmt.Printf("2. Click \"Allow\" (you might have to log in first).\n") fmt.Printf("3. Copy the authorization code.\n") @@ -181,16 +162,20 @@ func initDbx(cmd *cobra.Command, args []string) (err error) { var code string if _, err = fmt.Scan(&code); err != nil { - return + return fmt.Errorf("read code: %w", err) } + var token *oauth2.Token ctx := context.Background() token, err = conf.Exchange(ctx, code) if err != nil { - return + return fmt.Errorf("token exchange: %w", err) } + tokens[tokType] = token.AccessToken - writeTokens(filePath, tokenMap) + if err := writeTokens(filePath, tokenMap); err != nil { + return err + } } logLevel := dropbox.LogOff @@ -208,7 +193,7 @@ func initDbx(cmd *cobra.Command, args []string) (err error) { URLGenerator: nil, } - return + return nil } // RootCmd represents the base command when called without any subcommands @@ -236,10 +221,10 @@ func init() { RootCmd.PersistentFlags().String("domain", "", "Override default Dropbox domain, useful for testing") RootCmd.PersistentFlags().MarkHidden("domain") - personalAppKey = getEnv("DROPBOX_PERSONAL_APP_KEY", personalAppKey) - personalAppSecret = getEnv("DROPBOX_PERSONAL_APP_SECRET", personalAppSecret) - teamAccessAppKey = getEnv("DROPBOX_TEAM_APP_KEY", teamAccessAppKey) - teamAccessAppSecret = getEnv("DROPBOX_TEAM_APP_SECRET", teamAccessAppSecret) - teamManageAppKey = getEnv("DROPBOX_MANAGE_APP_KEY", teamManageAppKey) - teamManageAppSecret = getEnv("DROPBOX_MANAGE_APP_SECRET", teamAccessAppSecret) + personalAppKey = getEnv(personalAppKeyEnv, personalAppKey) + personalAppSecret = getEnv(personalAppSecretEnv, personalAppSecret) + teamAccessAppKey = getEnv(teamAccessAppKeyEnv, teamAccessAppKey) + teamAccessAppSecret = getEnv(teamAccessAppSecretEnv, teamAccessAppSecret) + teamManageAppKey = getEnv(teamManageAppKeyEnv, teamManageAppKey) + teamManageAppSecret = getEnv(teamManageAppSecretEnv, teamAccessAppSecret) } diff --git a/go.mod b/go.mod index 68ffcf0..2706704 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.1 github.com/dustin/go-humanize v1.0.0 github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85 github.com/spf13/pflag v1.0.3 // indirect diff --git a/go.sum b/go.sum index 3183f3c..28a43c2 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e h1:Qa6dnn8DlasdXRnacluu8HzPts0S1I9zvvUPDbBnXFI= github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e/go.mod h1:waEya8ee1Ro/lgxpVhkJI4BVASzkm3UZqkx/cFJiYHM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=