diff --git a/api/api.go b/api/api.go index 50553bb..f6cd7c2 100644 --- a/api/api.go +++ b/api/api.go @@ -1,260 +1,260 @@ package api import ( "fmt" "github.com/atotto/clipboard" writeas "github.com/writeas/go-writeas/v2" "github.com/writeas/web-core/posts" "github.com/writeas/writeas-cli/config" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) func client(userAgent string, tor bool) *writeas.Client { var client *writeas.Client if tor { client = writeas.NewTorClient(TorPort) } else { if config.IsDev() { client = writeas.NewDevClient() } else { client = writeas.NewClient() } } client.UserAgent = userAgent return client } func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { var client *writeas.Client if config.IsTor(c) { client = writeas.NewTorClient(TorPort) } else { if config.IsDev() { client = writeas.NewDevClient() } else { client = writeas.NewClient() } } client.UserAgent = config.UserAgent(c) // TODO: load user into var shared across the app - u, _ := config.LoadUser(c, c.GlobalString("user")) + u, _ := config.LoadUser(c) if u != nil { client.SetToken(u.AccessToken) } else if authRequired { return nil, fmt.Errorf("Not currently logged in. Authenticate with: writeas auth ") } return client, nil } // DoFetch retrieves the Write.as post with the given friendlyID, // optionally via the Tor hidden service. func DoFetch(friendlyID, ua string, tor bool) error { cl := client(ua, tor) p, err := cl.GetPost(friendlyID) if err != nil { return err } if p.Title != "" { fmt.Printf("# %s\n\n", string(p.Title)) } fmt.Printf("%s\n", string(p.Content)) return nil } // DoFetchPosts retrieves all remote posts for the // authenticated user func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) { cl, err := NewClient(c, true) if err != nil { return nil, err } posts, err := cl.GetUserPosts() if err != nil { return nil, err } return *posts, nil } // DoPost creates a Write.as post, returning an error if it was // unsuccessful. func DoPost(c *cli.Context, post []byte, font string, encrypt, tor, code bool) (*writeas.Post, error) { cl, _ := NewClient(c, false) pp := &writeas.PostParams{ Font: config.GetFont(code, font), Collection: config.Collection(c), } pp.Title, pp.Content = posts.ExtractTitle(string(post)) if lang := config.Language(c, true); lang != "" { pp.Language = &lang } p, err := cl.CreatePost(pp) if err != nil { return nil, fmt.Errorf("Unable to post: %v", err) } var url string if p.Collection != nil { url = p.Collection.URL + p.Slug } else { if tor { url = config.TorBaseURL } else if config.IsDev() { url = config.DevBaseURL } else { url = config.WriteasBaseURL } url += "/" + p.ID // Output URL in requested format if c.Bool("md") { url += ".md" } } if cl.Token() == "" { // Store post locally, since we're not authenticated AddPost(c, p.ID, p.Token) } // Copy URL to clipboard err = clipboard.WriteAll(string(url)) if err != nil { log.Errorln("writeas: Didn't copy to clipboard: %s", err) } else { log.Info(c, "Copied to clipboard.") } // Output URL fmt.Printf("%s\n", url) return p, nil } // DoFetchCollections retrieves a list of the currently logged in users // collections. func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) { cl, err := NewClient(c, true) if err != nil { if config.Debug() { log.ErrorlnQuit("could not create new client: %v", err) } return nil, fmt.Errorf("Couldn't create new client") } colls, err := cl.GetUserCollections() if err != nil { if config.Debug() { log.ErrorlnQuit("failed fetching user collections: %v", err) } return nil, fmt.Errorf("Couldn't get user collections") } out := make([]RemoteColl, len(*colls)) for i, c := range *colls { coll := RemoteColl{ Alias: c.Alias, Title: c.Title, URL: c.URL, } out[i] = coll } return out, nil } // DoUpdate updates the given post on Write.as. func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, tor, code bool) error { cl, _ := NewClient(c, false) params := writeas.PostParams{} params.Title, params.Content = posts.ExtractTitle(string(post)) if lang := config.Language(c, false); lang != "" { params.Language = &lang } if code || font != "" { params.Font = config.GetFont(code, font) } _, err := cl.UpdatePost(friendlyID, token, ¶ms) if err != nil { if config.Debug() { log.ErrorlnQuit("Problem updating: %v", err) } return fmt.Errorf("Post doesn't exist, or bad edit token given.") } if tor { log.Info(c, "Post updated via hidden service.") } else { log.Info(c, "Post updated.") } return nil } // DoDelete deletes the given post on Write.as, and removes any local references func DoDelete(c *cli.Context, friendlyID, token string, tor bool) error { cl, _ := NewClient(c, false) err := cl.DeletePost(friendlyID, token) if err != nil { if config.Debug() { log.ErrorlnQuit("Problem deleting: %v", err) } return fmt.Errorf("Post doesn't exist, or bad edit token given.") } if tor { log.Info(c, "Post deleted from hidden service.") } else { log.Info(c, "Post deleted.") } RemovePost(c.App.ExtraInfo()["configDir"], friendlyID) return nil } func DoLogIn(c *cli.Context, username, password string) error { cl := client(config.UserAgent(c), config.IsTor(c)) u, err := cl.LogIn(username, password) if err != nil { if config.Debug() { log.ErrorlnQuit("Problem logging in: %v", err) } return err } err = config.SaveUser(c, u) if err != nil { return err } log.Info(c, "Logged in as %s.\n", u.User.Username) return nil } func DoLogOut(c *cli.Context) error { cl, err := NewClient(c, true) if err != nil { return err } err = cl.LogOut() if err != nil { if config.Debug() { log.ErrorlnQuit("Problem logging out: %v", err) } return err } // delete local user file - return config.DeleteUser(c, c.GlobalString("user")) + return config.DeleteUser(c) } diff --git a/api/sync.go b/api/sync.go index 3736729..dec2794 100644 --- a/api/sync.go +++ b/api/sync.go @@ -1,131 +1,131 @@ package api import ( //"github.com/writeas/writeas-cli/sync" "fmt" "io/ioutil" "os" "path/filepath" "github.com/writeas/writeas-cli/config" "github.com/writeas/writeas-cli/fileutils" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) const ( PostFileExt = ".txt" userFilename = "writeas_user" ) func CmdPull(c *cli.Context) error { cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) if err != nil { return err } // Create posts directory if needed if cfg.Posts.Directory == "" { syncSetUp(c, cfg) } // Fetch posts cl, err := NewClient(c, true) if err != nil { return err } posts, err := cl.GetUserPosts() if err != nil { return err } for _, p := range *posts { postFilename := p.ID collDir := "" if p.Collection != nil { postFilename = p.Slug // Create directory for collection collDir = p.Collection.Alias if !fileutils.Exists(filepath.Join(cfg.Posts.Directory, collDir)) { log.Info(c, "Creating folder "+collDir) err = os.Mkdir(filepath.Join(cfg.Posts.Directory, collDir), 0755) if err != nil { log.Errorln("Error creating blog directory %s: %s. Skipping post %s.", collDir, err, postFilename) continue } } } postFilename += PostFileExt // Write file txtFile := p.Content if p.Title != "" { txtFile = "# " + p.Title + "\n\n" + txtFile } err = ioutil.WriteFile(filepath.Join(cfg.Posts.Directory, collDir, postFilename), []byte(txtFile), 0644) if err != nil { log.Errorln("Error creating file %s: %s", postFilename, err) } log.Info(c, "Saved post "+postFilename) // Update mtime and atime on files modTime := p.Updated.Local() err = os.Chtimes(filepath.Join(cfg.Posts.Directory, collDir, postFilename), modTime, modTime) if err != nil { log.Errorln("Error setting time on %s: %s", postFilename, err) } } return nil } -func syncSetUp(c *cli.Context, cfg *config.UserConfig) error { +func syncSetUp(c *cli.Context, cfg *config.Config) error { // Get user information and fail early (before we make the user do // anything), if we're going to - u, err := config.LoadUser(c, c.GlobalString("user")) + u, err := config.LoadUser(c) if err != nil { return err } // Prompt for posts directory defaultDir, err := os.Getwd() if err != nil { return err } var dir string fmt.Printf("Posts directory? [%s]: ", defaultDir) fmt.Scanln(&dir) if dir == "" { dir = defaultDir } // FIXME: This only works on non-Windows OSes (fix: https://www.reddit.com/r/golang/comments/5t3ezd/hidden_files_directories/) userFilepath := filepath.Join(dir, "."+userFilename) // Create directory if needed if !fileutils.Exists(dir) { err = os.MkdirAll(dir, 0700) if err != nil { if config.Debug() { log.Errorln("Error creating data directory: %s", err) } return err } // Create username file in directory err = ioutil.WriteFile(userFilepath, []byte(u.User.Username), 0644) fmt.Println("Created posts directory.") } // Save preference cfg.Posts.Directory = dir err = config.SaveConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]), cfg) if err != nil { if config.Debug() { log.Errorln("Unable to save config: %s", err) } return err } fmt.Println("Saved config.") return nil } diff --git a/commands/commands.go b/commands/commands.go index 04e3db6..653e466 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -1,360 +1,361 @@ package commands import ( "fmt" "io/ioutil" "os" "path/filepath" "text/tabwriter" "github.com/howeyc/gopass" "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/config" "github.com/writeas/writeas-cli/fileutils" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) func CmdPost(c *cli.Context) error { _, err := api.HandlePost(api.ReadStdIn(), c) return err } func CmdNew(c *cli.Context) error { fname, p := api.ComposeNewPost() if p == nil { // Assume composeNewPost already told us what the error was. Abort now. os.Exit(1) } // Ensure we have something to post if len(*p) == 0 { // Clean up temporary post if fname != "" { os.Remove(fname) } log.InfolnQuit("Empty post. Bye!") } _, err := api.HandlePost(*p, c) if err != nil { log.Errorln("Error posting: %s\n%s", err, config.MessageRetryCompose(fname)) return cli.NewExitError("", 1) } // Clean up temporary post if fname != "" { os.Remove(fname) } return nil } func CmdPublish(c *cli.Context) error { filename := c.Args().Get(0) if filename == "" { return cli.NewExitError("usage: writeas publish ", 1) } content, err := ioutil.ReadFile(filename) if err != nil { return err } p, err := api.HandlePost(content, c) if err != nil { return err } // Save post to posts folder cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) if cfg.Posts.Directory != "" { err = api.WritePost(cfg.Posts.Directory, p) if err != nil { return err } } return nil } func CmdDelete(c *cli.Context) error { friendlyID := c.Args().Get(0) token := c.Args().Get(1) if friendlyID == "" { return cli.NewExitError("usage: writeas delete []", 1) } - u, _ := config.LoadUser(c, c.GlobalString("user")) + u, _ := config.LoadUser(c) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) if token == "" && u == nil { log.Errorln("Couldn't find an edit token locally. Did you create this post here?") log.ErrorlnQuit("If you have an edit token, use: writeas delete %s ", friendlyID) } } tor := config.IsTor(c) if c.Int("tor-port") != 0 { api.TorPort = c.Int("tor-port") } if tor { log.Info(c, "Deleting via hidden service...") } else { log.Info(c, "Deleting...") } err := api.DoDelete(c, friendlyID, token, tor) if err != nil { return cli.NewExitError(fmt.Sprintf("Couldn't delete remote copy: %v", err), 1) } // Delete local file, if necessary cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) if cfg.Posts.Directory != "" { // TODO: handle deleting blog posts err = fileutils.DeleteFile(filepath.Join(cfg.Posts.Directory, friendlyID+api.PostFileExt)) if err != nil { return cli.NewExitError(fmt.Sprintf("Couldn't delete local copy: %v", err), 1) } } return nil } func CmdUpdate(c *cli.Context) error { friendlyID := c.Args().Get(0) token := c.Args().Get(1) if friendlyID == "" { return cli.NewExitError("usage: writeas update []", 1) } - u, _ := config.LoadUser(c, c.GlobalString("user")) + u, _ := config.LoadUser(c) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) if token == "" && u == nil { log.Errorln("Couldn't find an edit token locally. Did you create this post here?") log.ErrorlnQuit("If you have an edit token, use: writeas update %s ", friendlyID) } } // Read post body fullPost := api.ReadStdIn() tor := config.IsTor(c) if c.Int("tor-port") != 0 { api.TorPort = c.Int("tor-port") } if tor { log.Info(c, "Updating via hidden service...") } else { log.Info(c, "Updating...") } return api.DoUpdate(c, fullPost, friendlyID, token, c.String("font"), tor, c.Bool("code")) } func CmdGet(c *cli.Context) error { friendlyID := c.Args().Get(0) if friendlyID == "" { return cli.NewExitError("usage: writeas get ", 1) } tor := config.IsTor(c) if c.Int("tor-port") != 0 { api.TorPort = c.Int("tor-port") } if tor { log.Info(c, "Getting via hidden service...") } else { log.Info(c, "Getting...") } return api.DoFetch(friendlyID, config.UserAgent(c), tor) } func CmdAdd(c *cli.Context) error { friendlyID := c.Args().Get(0) token := c.Args().Get(1) if friendlyID == "" || token == "" { return cli.NewExitError("usage: writeas add ", 1) } err := api.AddPost(c, friendlyID, token) return err } func CmdListPosts(c *cli.Context) error { urls := c.Bool("url") ids := c.Bool("id") var p api.Post posts := api.GetPosts(c) tw := tabwriter.NewWriter(os.Stdout, 10, 0, 2, ' ', tabwriter.TabIndent) numPosts := len(*posts) if ids || !urls && numPosts != 0 { fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "ID", "Token") } else if numPosts != 0 { fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "URL", "Token") } else { fmt.Fprintf(tw, "No local posts found\n") } for i := range *posts { p = (*posts)[numPosts-1-i] if ids || !urls { fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", p.ID, p.EditToken) } else { fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken) } } - u, _ := config.LoadUser(c, c.GlobalString("user")) + u, _ := config.LoadUser(c) if u != nil { remotePosts, err := api.GetUserPosts(c) if err != nil { fmt.Println(err) } if len(remotePosts) > 0 { identifier := "URL" if ids || !urls { identifier = "ID" } fmt.Fprintf(tw, "\nAccount\t%s\t%s\t\n", identifier, "Title") } for _, p := range remotePosts { identifier := getPostURL(c, p.ID) if ids || !urls { identifier = p.ID } synced := "unsynced" if p.Synced { synced = "synced" } fmt.Fprintf(tw, "%s\t%s\t%s\t\n", synced, identifier, p.Title) } } return tw.Flush() } func getPostURL(c *cli.Context, slug string) string { base := config.WriteasBaseURL if config.IsDev() { base = config.DevBaseURL } ext := "" // Output URL in requested format if c.Bool("md") { ext = ".md" } return fmt.Sprintf("%s/%s%s", base, slug, ext) } func CmdCollections(c *cli.Context) error { - u, err := config.LoadUser(c, c.GlobalString("user")) + u, err := config.LoadUser(c) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } if u == nil { return cli.NewExitError("You must be authenticated to view collections.\nLog in first with: writeas auth ", 1) } colls, err := api.DoFetchCollections(c) if err != nil { return cli.NewExitError(fmt.Sprintf("Couldn't get collections for user %s: %v", u.User.Username, err), 1) } urls := c.Bool("url") tw := tabwriter.NewWriter(os.Stdout, 8, 0, 2, ' ', tabwriter.TabIndent) detail := "Title" if urls { detail = "URL" } fmt.Fprintf(tw, "%s\t%s\t\n", "Alias", detail) for _, c := range colls { dData := c.Title if urls { dData = c.URL } fmt.Fprintf(tw, "%s\t%s\t\n", c.Alias, dData) } tw.Flush() return nil } func CmdClaim(c *cli.Context) error { u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } if u == nil { return cli.NewExitError("You must be authenticated to claim local posts.\nLog in first with: writeas auth ", 1) } localPosts := api.GetPosts(c) if len(*localPosts) == 0 { return nil } log.Info(c, "Claiming %d post(s) for %s...", len(*localPosts), u.User.Username) results, err := api.ClaimPosts(c, localPosts) if err != nil { return cli.NewExitError(fmt.Sprintf("Failed to claim posts: %v", err), 1) } var okCount, errCount int for _, r := range *results { id := r.ID if id == "" { // No top-level ID, so the claim was successful id = r.Post.ID } status := fmt.Sprintf("Post %s...", id) if r.ErrorMessage != "" { log.Errorln("%serror: %v", status, r.ErrorMessage) errCount++ } else { log.Info(c, "%sOK", status) okCount++ // only delete local if successful api.RemovePost(c.App.ExtraInfo()["configDir"], id) } } log.Info(c, "%d claimed, %d failed", okCount, errCount) return nil } func CmdAuth(c *cli.Context) error { // Check configuration - u, err := config.LoadUser(c, c.GlobalString("user")) + u, err := config.LoadUser(c) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } if u != nil && u.AccessToken != "" { return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: writeas logout", 1) } // Validate arguments and get password + // TODO: after global config, check for default user username := c.Args().Get(0) if username == "" { return cli.NewExitError("usage: writeas auth ", 1) } fmt.Print("Password: ") pass, err := gopass.GetPasswdMasked() if err != nil { return cli.NewExitError(fmt.Sprintf("error reading password: %v", err), 1) } // Validate password if len(pass) == 0 { return cli.NewExitError("Please enter your password.", 1) } err = api.DoLogIn(c, username, string(pass)) if err != nil { return cli.NewExitError(fmt.Sprintf("error logging in: %v", err), 1) } return nil } func CmdLogOut(c *cli.Context) error { return api.DoLogOut(c) } diff --git a/config/config.go b/config/config.go index 4c9c59d..6ded22b 100644 --- a/config/config.go +++ b/config/config.go @@ -1,63 +1,74 @@ package config import ( "os" "path/filepath" ini "gopkg.in/ini.v1" ) const ( - UserConfigFile = "config.ini" + // ConfigFile is the full filename for application configuration files + ConfigFile = "config.ini" ) type ( + // APIConfig is not currently used APIConfig struct { } + // PostsConfig stores the directory for the user post cache PostsConfig struct { Directory string `ini:"directory"` } - UserConfig struct { - API APIConfig `ini:"api"` - Posts PostsConfig `ini:"posts"` + // DefaultConfig stores the default host and user to authenticate with + DefaultConfig struct { + Host string `ini:"host"` + User string `ini:"user"` + } + + // Config represents the entire base configuration + Config struct { + API APIConfig `ini:"api"` + Default DefaultConfig `ini:"default"` + Posts PostsConfig `ini:"posts"` } ) -func LoadConfig(dataDir string) (*UserConfig, error) { +func LoadConfig(dataDir string) (*Config, error) { // TODO: load config to var shared across app - cfg, err := ini.LooseLoad(filepath.Join(dataDir, UserConfigFile)) + cfg, err := ini.LooseLoad(filepath.Join(dataDir, ConfigFile)) if err != nil { return nil, err } // Parse INI file - uc := &UserConfig{} + uc := &Config{} err = cfg.MapTo(uc) if err != nil { return nil, err } return uc, nil } -func SaveConfig(dataDir string, uc *UserConfig) error { +func SaveConfig(dataDir string, uc *Config) error { cfg := ini.Empty() err := ini.ReflectFrom(cfg, uc) if err != nil { return err } - return cfg.SaveTo(filepath.Join(dataDir, UserConfigFile)) + return cfg.SaveTo(filepath.Join(dataDir, ConfigFile)) } var editors = []string{"WRITEAS_EDITOR", "EDITOR"} func GetConfiguredEditor() string { for _, v := range editors { if e := os.Getenv(v); e != "" { return e } } return "" } diff --git a/config/options.go b/config/options.go index 57f08cd..78d3335 100644 --- a/config/options.go +++ b/config/options.go @@ -1,71 +1,81 @@ package config import ( "net/url" "github.com/cloudfoundry/jibber_jabber" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) // Application constants. const ( Version = "2.0" defaultUserAgent = "writeas-cli v" + Version // Defaults for posts on Write.as. DefaultFont = PostFontMono WriteasBaseURL = "https://write.as" DevBaseURL = "https://development.write.as" TorBaseURL = "http://writeas7pm7rcdqg.onion" ) func UserAgent(c *cli.Context) string { ua := c.String("user-agent") if ua == "" { return defaultUserAgent } return ua + " (" + defaultUserAgent + ")" } func IsTor(c *cli.Context) bool { return c.Bool("tor") || c.Bool("t") } func Language(c *cli.Context, auto bool) string { if l := c.String("lang"); l != "" { return l } if !auto { return "" } // Automatically detect language l, err := jibber_jabber.DetectLanguage() if err != nil { log.Info(c, "Language detection failed: %s", err) return "" } return l } func Collection(c *cli.Context) string { if coll := c.String("c"); coll != "" { return coll } if coll := c.String("b"); coll != "" { return coll } return "" } -// HostDirectory returns the sub directory string for the host flag if set +// HostDirectory returns the sub directory string for the host. Order of +// precedence is a host flag if any, then the configured default, if any func HostDirectory(c *cli.Context) (string, error) { - if host := c.GlobalString("host"); host != "" { - u, err := url.Parse(host) + cfg, err := LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return "", err + } + // flag takes precedence over defaults + if hostFlag := c.GlobalString("host"); hostFlag != "" { + u, err := url.Parse(hostFlag) if err != nil { - return "", err // TODO + return "", err } return u.Hostname(), nil } - return "", nil + u, err := url.Parse(cfg.Default.Host) + if err != nil { + return "", err + } + return u.Hostname(), nil } diff --git a/config/user.go b/config/user.go index 89cd9f7..1ba9b15 100644 --- a/config/user.go +++ b/config/user.go @@ -1,75 +1,97 @@ package config import ( "encoding/json" "io/ioutil" "path/filepath" writeas "github.com/writeas/go-writeas/v2" "github.com/writeas/writeas-cli/fileutils" "gopkg.in/urfave/cli.v1" ) -func LoadUser(c *cli.Context, username string) (*writeas.AuthUser, error) { +func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { dir, err := userHostDir(c) if err != nil { return nil, err } + username, err := currentUser(c) + if err != nil { + return nil, err + } fname := filepath.Join(dir, username+".json") userJSON, err := ioutil.ReadFile(fname) if err != nil { if !fileutils.Exists(fname) { // Don't return a file-not-found error return nil, nil } return nil, err } // Parse JSON file u := &writeas.AuthUser{} err = json.Unmarshal(userJSON, u) if err != nil { return nil, err } return u, nil } -func DeleteUser(c *cli.Context, username string) error { +func DeleteUser(c *cli.Context) error { dir, err := userHostDir(c) if err != nil { return err } + username, err := currentUser(c) + if err != nil { + return err + } + return fileutils.DeleteFile(filepath.Join(dir, username+".json")) } func SaveUser(c *cli.Context, u *writeas.AuthUser) error { // Marshal struct into pretty-printed JSON userJSON, err := json.MarshalIndent(u, "", " ") if err != nil { return err } dir, err := userHostDir(c) if err != nil { return err } DirMustExist(dir) // Save file err = ioutil.WriteFile(filepath.Join(dir, u.User.Username+".json"), userJSON, 0600) if err != nil { return err } return nil } // userHostDir returns the path to the user data directory with the host based // subpath if the host flag is set func userHostDir(c *cli.Context) (string, error) { dataDir := UserDataDir(c.App.ExtraInfo()["configDir"]) hostDir, err := HostDirectory(c) if err != nil { return "", err } return filepath.Join(dataDir, hostDir), nil } + +func currentUser(c *cli.Context) (string, error) { + cfg, err := LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return "", err + } + + if c.GlobalString("user") != "" { + return c.GlobalString("user"), nil + } + + return cfg.Default.User, nil +}