From ece87e913398d8c7f423db37337fd37984238834 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 13:19:44 -0700 Subject: [PATCH 01/63] add new wf binary for community instances this creates a second binary, wf, which will have additonal functionality for hosts other than write.as. also moves global flags into their own slice in new file adds `host, H` global flag inlcudes gitignore for binary --- cmd/wf/.gitignore | 1 + cmd/wf/config_nix.go | 7 ++ cmd/wf/config_win.go | 7 ++ cmd/wf/flags.go | 30 ++++++ cmd/wf/main.go | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 311 insertions(+) create mode 100644 cmd/wf/.gitignore create mode 100644 cmd/wf/config_nix.go create mode 100644 cmd/wf/config_win.go create mode 100644 cmd/wf/flags.go create mode 100644 cmd/wf/main.go diff --git a/cmd/wf/.gitignore b/cmd/wf/.gitignore new file mode 100644 index 0000000..89b7a51 --- /dev/null +++ b/cmd/wf/.gitignore @@ -0,0 +1 @@ +wf \ No newline at end of file diff --git a/cmd/wf/config_nix.go b/cmd/wf/config_nix.go new file mode 100644 index 0000000..4abd334 --- /dev/null +++ b/cmd/wf/config_nix.go @@ -0,0 +1,7 @@ +// +build !windows + +package main + +var appInfo = map[string]string{ + "configDir": ".writefreely", +} diff --git a/cmd/wf/config_win.go b/cmd/wf/config_win.go new file mode 100644 index 0000000..e44b45b --- /dev/null +++ b/cmd/wf/config_win.go @@ -0,0 +1,7 @@ +// +build windows + +package main + +var appInfo = map[string]string{ + "configDir": "WriteFreely", +} diff --git a/cmd/wf/flags.go b/cmd/wf/flags.go new file mode 100644 index 0000000..03ed17c --- /dev/null +++ b/cmd/wf/flags.go @@ -0,0 +1,30 @@ +package main + +import ( + "gopkg.in/urfave/cli.v1" +) + +var globalFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Perform action on Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + cli.StringFlag{ + Name: "user-agent", + Usage: "Sets the User-Agent for API requests", + Value: "", + }, + cli.StringFlag{ + Name: "host, H", + Usage: "Operate against a custom hostname", + }, +} diff --git a/cmd/wf/main.go b/cmd/wf/main.go new file mode 100644 index 0000000..8b78771 --- /dev/null +++ b/cmd/wf/main.go @@ -0,0 +1,266 @@ +package main + +import ( + "os" + + "github.com/writeas/writeas-cli/api" + "github.com/writeas/writeas-cli/commands" + "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/log" + cli "gopkg.in/urfave/cli.v1" +) + +func main() { + initialize(appInfo["configDir"]) + cli.VersionFlag = cli.BoolFlag{ + Name: "version, V", + Usage: "print the version", + } + + // Run the app + app := cli.NewApp() + app.Name = "wf" + app.Version = config.Version + app.Usage = "Publish text quickly" + // TODO: who is the author? the contributors? link to GH? + app.Authors = []cli.Author{ + { + Name: "Write.as", + Email: "hello@write.as", + }, + } + app.ExtraInfo = func() map[string]string { + return appInfo + } + app.Action = commands.CmdPost + app.Flags = globalFlags + app.Commands = []cli.Command{ + { + Name: "post", + Usage: "Alias for default action: create post from stdin", + Action: commands.CmdPost, + Flags: config.PostFlags, + Description: `Create a new post on Write.as from stdin. + + Use the --code flag to indicate that the post should use syntax + highlighting. Or use the --font [value] argument to set the post's + appearance, where [value] is mono, monospace (default), wrap (monospace + font with word wrapping), serif, or sans.`, + }, + { + Name: "new", + Usage: "Compose a new post from the command-line and publish", + Description: `An alternative to piping data to the program. + + On Windows, this will use 'copy con' to start reading what you input from the + prompt. Press F6 or Ctrl-Z then Enter to end input. + On *nix, this will use the best available text editor, starting with the + value set to the WRITEAS_EDITOR or EDITOR environment variable, or vim, or + finally nano. + + Use the --code flag to indicate that the post should use syntax + highlighting. Or use the --font [value] argument to set the post's + appearance, where [value] is mono, monospace (default), wrap (monospace + font with word wrapping), serif, or sans. + + If posting fails for any reason, 'writeas' will show you the temporary file + location and how to pipe it to 'writeas' to retry.`, + Action: commands.CmdNew, + Flags: config.PostFlags, + }, + { + Name: "publish", + Usage: "Publish a file to Write.as", + Action: commands.CmdPublish, + Flags: config.PostFlags, + }, + { + Name: "delete", + Usage: "Delete a post", + Action: commands.CmdDelete, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Delete via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + { + Name: "update", + Usage: "Update (overwrite) a post", + Action: commands.CmdUpdate, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Update via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "code", + Usage: "Specifies this post is code", + }, + cli.StringFlag{ + Name: "font", + Usage: "Sets post font to given value", + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + { + Name: "get", + Usage: "Read a raw post", + Action: commands.CmdGet, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Get from Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + { + Name: "add", + Usage: "Add an existing post locally", + Description: `A way to add an existing post to your local store for easy editing later. + + This requires a post ID (from https://write.as/[ID]) and an Edit Token + (exported from another Write.as client, such as the Android app). +`, + Action: commands.CmdAdd, + }, + { + Name: "posts", + Usage: "List all of your posts", + Description: "This will list only local posts when not currently authenticated. To list remote posts as well, first run: writeas auth .", + Action: commands.CmdListPosts, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "id", + Usage: "Show list with post IDs (default)", + }, + cli.BoolFlag{ + Name: "md", + Usage: "Use with --url to return URLs with Markdown enabled", + }, + cli.BoolFlag{ + Name: "url", + Usage: "Show list with URLs", + }, + }, + }, + { + Name: "fetch", + Usage: "Fetch authenticated user's Write.as posts", + Action: api.CmdPull, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Authenticate via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + { + Name: "auth", + Usage: "Authenticate with Write.as", + Action: commands.CmdAuth, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Authenticate via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + { + Name: "logout", + Usage: "Log out of Write.as", + Action: commands.CmdLogOut, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Authenticate via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, + } + + cli.CommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + writeas {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if .Flags}} + +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + app.Run(os.Args) +} + +func initialize(dataDirName string) { + // Ensure we have a data directory to use + if !config.DataDirExists(dataDirName) { + err := config.CreateDataDir(dataDirName) + if err != nil { + if config.Debug() { + panic(err) + } else { + log.Errorln("Error creating data directory: %s", err) + return + } + } + } +} From 834bc2a440553ba6c2a02b3fbdbacb0395221d2b Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 14:27:29 -0700 Subject: [PATCH 02/63] add helper to parse host based config dir --- config/options.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/config/options.go b/config/options.go index 1abf9ee..57f08cd 100644 --- a/config/options.go +++ b/config/options.go @@ -1,6 +1,8 @@ package config import ( + "net/url" + "github.com/cloudfoundry/jibber_jabber" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" @@ -54,3 +56,16 @@ func Collection(c *cli.Context) string { } return "" } + +// HostDirectory returns the sub directory string for the host flag if set +func HostDirectory(c *cli.Context) (string, error) { + if host := c.GlobalString("host"); host != "" { + u, err := url.Parse(host) + if err != nil { + return "", err // TODO + } + return u.Hostname(), nil + } + + return "", nil +} From 35292d25f547c82faac28bc08e13499616406c08 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 14:27:55 -0700 Subject: [PATCH 03/63] remove unused global flags --- cmd/wf/flags.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cmd/wf/flags.go b/cmd/wf/flags.go index 03ed17c..dac0803 100644 --- a/cmd/wf/flags.go +++ b/cmd/wf/flags.go @@ -14,15 +14,6 @@ var globalFlags = []cli.Flag{ Usage: "Use a different port to connect to Tor", Value: 9150, }, - cli.BoolFlag{ - Name: "verbose, v", - Usage: "Make the operation more talkative", - }, - cli.StringFlag{ - Name: "user-agent", - Usage: "Sets the User-Agent for API requests", - Value: "", - }, cli.StringFlag{ Name: "host, H", Usage: "Operate against a custom hostname", From 30ecf6cc76de5b29cab1f6f1e54e5ac7181c2455 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 14:31:18 -0700 Subject: [PATCH 04/63] use global flags in writeas cli --- cmd/writeas/flags.go | 21 +++++++++++++++++++++ cmd/writeas/main.go | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 cmd/writeas/flags.go diff --git a/cmd/writeas/flags.go b/cmd/writeas/flags.go new file mode 100644 index 0000000..dac0803 --- /dev/null +++ b/cmd/writeas/flags.go @@ -0,0 +1,21 @@ +package main + +import ( + "gopkg.in/urfave/cli.v1" +) + +var globalFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "tor, t", + Usage: "Perform action on Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.StringFlag{ + Name: "host, H", + Usage: "Operate against a custom hostname", + }, +} diff --git a/cmd/writeas/main.go b/cmd/writeas/main.go index c113d19..7bdcc6e 100644 --- a/cmd/writeas/main.go +++ b/cmd/writeas/main.go @@ -31,7 +31,7 @@ func main() { return appInfo } app.Action = commands.CmdPost - app.Flags = config.PostFlags + app.Flags = globalFlags app.Commands = []cli.Command{ { Name: "post", From d6c4b2c392ad3922afee0bbaf7bc2ca44c70475a Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 15:17:30 -0700 Subject: [PATCH 05/63] store/load user with host specific sub folder only if host flag supplied for writefreely binary, writeas unaffected --- api/api.go | 4 ++-- api/sync.go | 8 ++++---- cmd/wf/main.go | 18 +----------------- cmd/writeas/main.go | 18 +----------------- commands/commands.go | 8 ++++---- config/directories.go | 26 ++++++++++++++++++++++---- config/user.go | 27 +++++++++++++++++++++++---- 7 files changed, 57 insertions(+), 52 deletions(-) diff --git a/api/api.go b/api/api.go index d3fa3fc..032d69f 100644 --- a/api/api.go +++ b/api/api.go @@ -42,7 +42,7 @@ func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { } client.UserAgent = config.UserAgent(c) // TODO: load user into var shared across the app - u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, _ := config.LoadUser(c) if u != nil { client.SetToken(u.AccessToken) } else if authRequired { @@ -235,7 +235,7 @@ func DoLogIn(c *cli.Context, username, password string) error { return err } - err = config.SaveUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]), u) + err = config.SaveUser(c, u) if err != nil { return err } diff --git a/api/sync.go b/api/sync.go index d687f24..070b5e0 100644 --- a/api/sync.go +++ b/api/sync.go @@ -25,7 +25,7 @@ func CmdPull(c *cli.Context) error { } // Create posts directory if needed if cfg.Posts.Directory == "" { - syncSetUp(c.App.ExtraInfo()["configDir"], cfg) + syncSetUp(c, cfg) } // Fetch posts @@ -79,10 +79,10 @@ func CmdPull(c *cli.Context) error { return nil } -func syncSetUp(path string, cfg *config.UserConfig) error { +func syncSetUp(c *cli.Context, cfg *config.UserConfig) error { // Get user information and fail early (before we make the user do // anything), if we're going to - u, err := config.LoadUser(config.UserDataDir(path)) + u, err := config.LoadUser(c) if err != nil { return err } @@ -118,7 +118,7 @@ func syncSetUp(path string, cfg *config.UserConfig) error { // Save preference cfg.Posts.Directory = dir - err = config.SaveConfig(config.UserDataDir(path), cfg) + err = config.SaveConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]), cfg) if err != nil { if config.Debug() { log.Errorln("Unable to save config: %s", err) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 8b78771..4dbbf7e 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -6,12 +6,11 @@ import ( "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/commands" "github.com/writeas/writeas-cli/config" - "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) func main() { - initialize(appInfo["configDir"]) + config.DirMustExist(appInfo["configDir"]) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", Usage: "print the version", @@ -249,18 +248,3 @@ OPTIONS: ` app.Run(os.Args) } - -func initialize(dataDirName string) { - // Ensure we have a data directory to use - if !config.DataDirExists(dataDirName) { - err := config.CreateDataDir(dataDirName) - if err != nil { - if config.Debug() { - panic(err) - } else { - log.Errorln("Error creating data directory: %s", err) - return - } - } - } -} diff --git a/cmd/writeas/main.go b/cmd/writeas/main.go index 7bdcc6e..111d1e5 100644 --- a/cmd/writeas/main.go +++ b/cmd/writeas/main.go @@ -5,12 +5,11 @@ import ( "github.com/writeas/writeas-cli/commands" "github.com/writeas/writeas-cli/config" - "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) func main() { - initialize(appInfo["configDir"]) + config.DirMustExist(appInfo["configDir"]) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", Usage: "print the version", @@ -248,18 +247,3 @@ OPTIONS: ` app.Run(os.Args) } - -func initialize(dataDirName string) { - // Ensure we have a data directory to use - if !config.DataDirExists(dataDirName) { - err := config.CreateDataDir(dataDirName) - if err != nil { - if config.Debug() { - panic(err) - } else { - log.Errorln("Error creating data directory: %s", err) - return - } - } - } -} diff --git a/commands/commands.go b/commands/commands.go index 0a4ba6a..7e51455 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -71,7 +71,7 @@ func CmdDelete(c *cli.Context) error { return cli.NewExitError("usage: writeas delete []", 1) } - u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, _ := config.LoadUser(c) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) @@ -107,7 +107,7 @@ func CmdUpdate(c *cli.Context) error { return cli.NewExitError("usage: writeas update []", 1) } - u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, _ := config.LoadUser(c) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) @@ -186,7 +186,7 @@ func CmdListPosts(c *cli.Context) error { fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken) } } - u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, _ := config.LoadUser(c) if u != nil { remotePosts, err := api.GetUserPosts(c) if err != nil { @@ -302,7 +302,7 @@ func CmdClaim(c *cli.Context) error { func CmdAuth(c *cli.Context) error { // Check configuration - u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, err := config.LoadUser(c) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } diff --git a/config/directories.go b/config/directories.go index 8373835..a24cc31 100644 --- a/config/directories.go +++ b/config/directories.go @@ -5,16 +5,34 @@ import ( "path/filepath" "github.com/writeas/writeas-cli/fileutils" + "github.com/writeas/writeas-cli/log" ) func UserDataDir(dataDirName string) string { return filepath.Join(parentDataDir(), dataDirName) } -func DataDirExists(dataDirName string) bool { - return fileutils.Exists(UserDataDir(dataDirName)) +func dataDirExists(dataDirName string) bool { + return fileutils.Exists(dataDirName) } -func CreateDataDir(dataDirName string) error { - return os.Mkdir(UserDataDir(dataDirName), 0700) +func createDataDir(dataDirName string) error { + return os.Mkdir(dataDirName, 0700) +} + +// DirMustExist checks for a directory, creates it if not found and either +// panics or logs and error depending on the status of Debug +func DirMustExist(dataDirName string) { + // Ensure we have a data directory to use + if !dataDirExists(dataDirName) { + err := createDataDir(dataDirName) + if err != nil { + if Debug() { + panic(err) + } else { + log.Errorln("Error creating data directory: %s", err) + return + } + } + } } diff --git a/config/user.go b/config/user.go index 21dd8f9..6722452 100644 --- a/config/user.go +++ b/config/user.go @@ -7,12 +7,17 @@ import ( writeas "github.com/writeas/go-writeas/v2" "github.com/writeas/writeas-cli/fileutils" + "gopkg.in/urfave/cli.v1" ) const UserFile = "user.json" -func LoadUser(dataDir string) (*writeas.AuthUser, error) { - fname := filepath.Join(dataDir, UserFile) +func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { + dir, err := userHostDir(c) + if err != nil { + return nil, err + } + fname := filepath.Join(dir, UserFile) userJSON, err := ioutil.ReadFile(fname) if err != nil { if !fileutils.Exists(fname) { @@ -31,17 +36,31 @@ func LoadUser(dataDir string) (*writeas.AuthUser, error) { return u, nil } -func SaveUser(dataDir string, u *writeas.AuthUser) error { +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(dataDir, UserFile), userJSON, 0600) + err = ioutil.WriteFile(filepath.Join(dir, UserFile), userJSON, 0600) if err != nil { return err } return nil } + +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 +} From 79aae3105274948d799938b716099193e5c2d836 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 7 Jun 2019 15:30:27 -0700 Subject: [PATCH 06/63] delete host specific user when doing logout --- api/api.go | 11 ++--------- config/directories.go | 2 ++ config/user.go | 11 +++++++++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/api/api.go b/api/api.go index 032d69f..f6cd7c2 100644 --- a/api/api.go +++ b/api/api.go @@ -2,13 +2,11 @@ package api import ( "fmt" - "path/filepath" "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/fileutils" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) @@ -257,11 +255,6 @@ func DoLogOut(c *cli.Context) error { return err } - // Delete local user data - err = fileutils.DeleteFile(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), config.UserFile)) - if err != nil { - return err - } - - return nil + // delete local user file + return config.DeleteUser(c) } diff --git a/config/directories.go b/config/directories.go index a24cc31..e2a68f3 100644 --- a/config/directories.go +++ b/config/directories.go @@ -8,6 +8,8 @@ import ( "github.com/writeas/writeas-cli/log" ) +// UserDataDir returns a platform specific directory under the user's home +// directory func UserDataDir(dataDirName string) string { return filepath.Join(parentDataDir(), dataDirName) } diff --git a/config/user.go b/config/user.go index 6722452..52dd972 100644 --- a/config/user.go +++ b/config/user.go @@ -36,6 +36,15 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { return u, nil } +func DeleteUser(c *cli.Context) error { + dir, err := userHostDir(c) + if err != nil { + return err + } + + return fileutils.DeleteFile(filepath.Join(dir, UserFile)) +} + func SaveUser(c *cli.Context, u *writeas.AuthUser) error { // Marshal struct into pretty-printed JSON userJSON, err := json.MarshalIndent(u, "", " ") @@ -56,6 +65,8 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { 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) From be060d7ed1c3286cef05f3815e687f5ecf3180e2 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 10 Jun 2019 11:12:14 -0700 Subject: [PATCH 07/63] fix bug with creating empty dir in working dir some changes resulted in the data directory initialization creating the .writeas/.writefreely directory in the current working directory. --- cmd/wf/main.go | 2 +- cmd/writeas/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 4dbbf7e..a942e5c 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - config.DirMustExist(appInfo["configDir"]) + config.DirMustExist(config.UserDataDir(appInfo["configDir"])) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", Usage: "print the version", diff --git a/cmd/writeas/main.go b/cmd/writeas/main.go index 111d1e5..10750b4 100644 --- a/cmd/writeas/main.go +++ b/cmd/writeas/main.go @@ -9,7 +9,7 @@ import ( ) func main() { - config.DirMustExist(appInfo["configDir"]) + config.DirMustExist(config.UserDataDir(appInfo["configDir"])) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", Usage: "print the version", From 33578e83e95680b9369a05cb1c19a1c95e9e347b Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 10 Jun 2019 11:21:58 -0700 Subject: [PATCH 08/63] start multi user authentication - adds new user/u flag to wf - load and save user file based on username - removed host flag from writeas - adds hidden global flag for user to writeas to maintain compatibility --- api/api.go | 4 ++-- api/sync.go | 2 +- cmd/wf/flags.go | 4 ++++ cmd/writeas/flags.go | 5 +++-- commands/commands.go | 10 +++++----- config/user.go | 12 +++++------- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/api/api.go b/api/api.go index f6cd7c2..50553bb 100644 --- a/api/api.go +++ b/api/api.go @@ -40,7 +40,7 @@ func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { } client.UserAgent = config.UserAgent(c) // TODO: load user into var shared across the app - u, _ := config.LoadUser(c) + u, _ := config.LoadUser(c, c.GlobalString("user")) if u != nil { client.SetToken(u.AccessToken) } else if authRequired { @@ -256,5 +256,5 @@ func DoLogOut(c *cli.Context) error { } // delete local user file - return config.DeleteUser(c) + return config.DeleteUser(c, c.GlobalString("user")) } diff --git a/api/sync.go b/api/sync.go index 070b5e0..3736729 100644 --- a/api/sync.go +++ b/api/sync.go @@ -82,7 +82,7 @@ func CmdPull(c *cli.Context) error { func syncSetUp(c *cli.Context, cfg *config.UserConfig) error { // Get user information and fail early (before we make the user do // anything), if we're going to - u, err := config.LoadUser(c) + u, err := config.LoadUser(c, c.GlobalString("user")) if err != nil { return err } diff --git a/cmd/wf/flags.go b/cmd/wf/flags.go index dac0803..854b1f8 100644 --- a/cmd/wf/flags.go +++ b/cmd/wf/flags.go @@ -18,4 +18,8 @@ var globalFlags = []cli.Flag{ Name: "host, H", Usage: "Operate against a custom hostname", }, + cli.StringFlag{ + Name: "user, u", + Usage: "Use authenticated user, other than default", + }, } diff --git a/cmd/writeas/flags.go b/cmd/writeas/flags.go index dac0803..f19f830 100644 --- a/cmd/writeas/flags.go +++ b/cmd/writeas/flags.go @@ -15,7 +15,8 @@ var globalFlags = []cli.Flag{ Value: 9150, }, cli.StringFlag{ - Name: "host, H", - Usage: "Operate against a custom hostname", + Name: "user, u", + Hidden: true, + Value: "user", }, } diff --git a/commands/commands.go b/commands/commands.go index 7e51455..f67e673 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -71,7 +71,7 @@ func CmdDelete(c *cli.Context) error { return cli.NewExitError("usage: writeas delete []", 1) } - u, _ := config.LoadUser(c) + u, _ := config.LoadUser(c, c.GlobalString("user")) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) @@ -107,7 +107,7 @@ func CmdUpdate(c *cli.Context) error { return cli.NewExitError("usage: writeas update []", 1) } - u, _ := config.LoadUser(c) + u, _ := config.LoadUser(c, c.GlobalString("user")) if token == "" { // Search for the token locally token = api.TokenFromID(c, friendlyID) @@ -186,7 +186,7 @@ func CmdListPosts(c *cli.Context) error { fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken) } } - u, _ := config.LoadUser(c) + u, _ := config.LoadUser(c, c.GlobalString("user")) if u != nil { remotePosts, err := api.GetUserPosts(c) if err != nil { @@ -229,7 +229,7 @@ func getPostURL(c *cli.Context, slug string) string { } func CmdCollections(c *cli.Context) error { - u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, err := config.LoadUser(c, c.GlobalString("user")) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } @@ -302,7 +302,7 @@ func CmdClaim(c *cli.Context) error { func CmdAuth(c *cli.Context) error { // Check configuration - u, err := config.LoadUser(c) + u, err := config.LoadUser(c, c.GlobalString("user")) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } diff --git a/config/user.go b/config/user.go index 52dd972..89cd9f7 100644 --- a/config/user.go +++ b/config/user.go @@ -10,14 +10,12 @@ import ( "gopkg.in/urfave/cli.v1" ) -const UserFile = "user.json" - -func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { +func LoadUser(c *cli.Context, username string) (*writeas.AuthUser, error) { dir, err := userHostDir(c) if err != nil { return nil, err } - fname := filepath.Join(dir, UserFile) + fname := filepath.Join(dir, username+".json") userJSON, err := ioutil.ReadFile(fname) if err != nil { if !fileutils.Exists(fname) { @@ -36,13 +34,13 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { return u, nil } -func DeleteUser(c *cli.Context) error { +func DeleteUser(c *cli.Context, username string) error { dir, err := userHostDir(c) if err != nil { return err } - return fileutils.DeleteFile(filepath.Join(dir, UserFile)) + return fileutils.DeleteFile(filepath.Join(dir, username+".json")) } func SaveUser(c *cli.Context, u *writeas.AuthUser) error { @@ -58,7 +56,7 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { } DirMustExist(dir) // Save file - err = ioutil.WriteFile(filepath.Join(dir, UserFile), userJSON, 0600) + err = ioutil.WriteFile(filepath.Join(dir, u.User.Username+".json"), userJSON, 0600) if err != nil { return err } From 15f71e3714e36293895f73751323f84f0a54d309 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 10:57:27 -0700 Subject: [PATCH 09/63] multiple user authentication - now allows authentication with more than one user per host, stored as username.json inside the [host] directory. - supports a default user and host in config.ini - global flags will override the default --- api/api.go | 4 ++-- api/sync.go | 4 ++-- commands/commands.go | 11 ++++++----- config/config.go | 29 ++++++++++++++++++++--------- config/options.go | 20 +++++++++++++++----- config/user.go | 26 ++++++++++++++++++++++++-- 6 files changed, 69 insertions(+), 25 deletions(-) diff --git a/api/api.go b/api/api.go index 50553bb..f6cd7c2 100644 --- a/api/api.go +++ b/api/api.go @@ -40,7 +40,7 @@ func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { } 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 { @@ -256,5 +256,5 @@ func DoLogOut(c *cli.Context) error { } // 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 @@ -79,10 +79,10 @@ func CmdPull(c *cli.Context) error { 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 } diff --git a/commands/commands.go b/commands/commands.go index f67e673..de193fc 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -71,7 +71,7 @@ func CmdDelete(c *cli.Context) error { 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) @@ -107,7 +107,7 @@ func CmdUpdate(c *cli.Context) error { 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) @@ -186,7 +186,7 @@ func CmdListPosts(c *cli.Context) error { 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 { @@ -229,7 +229,7 @@ func getPostURL(c *cli.Context, slug string) string { } 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) } @@ -302,7 +302,7 @@ func CmdClaim(c *cli.Context) error { 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) } @@ -311,6 +311,7 @@ func CmdAuth(c *cli.Context) error { } // 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) diff --git a/config/config.go b/config/config.go index 4c9c59d..6ded22b 100644 --- a/config/config.go +++ b/config/config.go @@ -8,32 +8,43 @@ import ( ) 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 @@ -41,14 +52,14 @@ func LoadConfig(dataDir string) (*UserConfig, error) { 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"} diff --git a/config/options.go b/config/options.go index 57f08cd..78d3335 100644 --- a/config/options.go +++ b/config/options.go @@ -57,15 +57,25 @@ func Collection(c *cli.Context) string { 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 @@ -10,11 +10,15 @@ import ( "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 { @@ -34,12 +38,17 @@ func LoadUser(c *cli.Context, username string) (*writeas.AuthUser, error) { 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")) } @@ -73,3 +82,16 @@ func userHostDir(c *cli.Context) (string, error) { } 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 +} From 20919fbe0ddebc43fa805c2e9a9d2f37c26d3371 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 12:14:32 -0700 Subject: [PATCH 10/63] support any writefreely instance full support, auth and actions working by use of flags or defaults maintains backwards compatibility with write.as --- api/api.go | 66 +++++++++++++++++++++++++++++++++++++--------------- api/tor.go | 1 + commands/commands.go | 2 +- config/user.go | 9 ++++++- 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/api/api.go b/api/api.go index f6cd7c2..4540182 100644 --- a/api/api.go +++ b/api/api.go @@ -11,33 +11,55 @@ import ( cli "gopkg.in/urfave/cli.v1" ) -func client(userAgent string, tor bool) *writeas.Client { +func client(c *cli.Context, userAgent string, tor bool) (*writeas.Client, error) { var client *writeas.Client - if tor { - client = writeas.NewTorClient(TorPort) + var clientConfig writeas.Config + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return nil, fmt.Errorf("Failed to load configuration file: %v", err) + } + if c.GlobalString("host") != "" { + clientConfig.URL = c.GlobalString("host") + "/api" + } else if cfg.Default.Host != "" { + clientConfig.URL = cfg.Default.Host + "/api" + } else if config.IsDev() { + clientConfig.URL = config.DevBaseURL + "/api" } else { - if config.IsDev() { - client = writeas.NewDevClient() - } else { - client = writeas.NewClient() - } + clientConfig.URL = config.WriteasBaseURL + "/api" + } + if tor { + clientConfig.URL = config.TorBaseURL + clientConfig.TorPort = TorPort } + + client = writeas.NewClientWith(clientConfig) client.UserAgent = userAgent - return client + return client, nil } func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { var client *writeas.Client - if config.IsTor(c) { - client = writeas.NewTorClient(TorPort) + var clientConfig writeas.Config + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return nil, fmt.Errorf("Failed to load configuration file: %v", err) + } + if c.GlobalString("host") != "" { + clientConfig.URL = c.GlobalString("host") + "/api" + } else if cfg.Default.Host != "" { + clientConfig.URL = cfg.Default.Host + "/api" + } else if config.IsDev() { + clientConfig.URL = config.DevBaseURL + "/api" } else { - if config.IsDev() { - client = writeas.NewDevClient() - } else { - client = writeas.NewClient() - } + clientConfig.URL = config.WriteasBaseURL + "/api" + } + if config.IsTor(c) { + clientConfig.URL = config.TorBaseURL + clientConfig.TorPort = TorPort } + + client = writeas.NewClientWith(clientConfig) client.UserAgent = config.UserAgent(c) // TODO: load user into var shared across the app u, _ := config.LoadUser(c) @@ -52,8 +74,11 @@ func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { // 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) +func DoFetch(c *cli.Context, friendlyID, ua string, tor bool) error { + cl, err := client(c, ua, tor) + if err != nil { + return err + } p, err := cl.GetPost(friendlyID) if err != nil { @@ -223,7 +248,10 @@ func DoDelete(c *cli.Context, friendlyID, token string, tor bool) error { } func DoLogIn(c *cli.Context, username, password string) error { - cl := client(config.UserAgent(c), config.IsTor(c)) + cl, err := client(c, config.UserAgent(c), config.IsTor(c)) + if err != nil { + return err + } u, err := cl.LogIn(username, password) if err != nil { diff --git a/api/tor.go b/api/tor.go index dae5be5..d181b7f 100644 --- a/api/tor.go +++ b/api/tor.go @@ -11,6 +11,7 @@ var ( TorPort = 9150 ) +// TODO: never used? func torClient() *http.Client { dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, fmt.Sprintf("127.0.0.1:%d", TorPort)) transport := &http.Transport{Dial: dialSocksProxy} diff --git a/commands/commands.go b/commands/commands.go index de193fc..a14bd69 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -149,7 +149,7 @@ func CmdGet(c *cli.Context) error { log.Info(c, "Getting...") } - return api.DoFetch(friendlyID, config.UserAgent(c), tor) + return api.DoFetch(c, friendlyID, config.UserAgent(c), tor) } func CmdAdd(c *cli.Context) error { diff --git a/config/user.go b/config/user.go index 1ba9b15..359fcb4 100644 --- a/config/user.go +++ b/config/user.go @@ -65,7 +65,14 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { } DirMustExist(dir) // Save file - err = ioutil.WriteFile(filepath.Join(dir, u.User.Username+".json"), userJSON, 0600) + username, err := currentUser(c) + if err != nil { + return err + } + if username != "user" { + username = u.User.Username + } + err = ioutil.WriteFile(filepath.Join(dir, username+".json"), userJSON, 0600) if err != nil { return err } From 5855fd31813c223dd322b04df9945b2c4f5ffa11 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 14:27:57 -0700 Subject: [PATCH 11/63] wf: remove fetch and add blogs cmd this get's the wf binary up to date with the writeas functionality --- cmd/wf/main.go | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index a942e5c..87e74dc 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -3,7 +3,6 @@ package main import ( "os" - "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/commands" "github.com/writeas/writeas-cli/config" cli "gopkg.in/urfave/cli.v1" @@ -170,28 +169,17 @@ func main() { Usage: "Show list with URLs", }, }, - }, - { - Name: "fetch", - Usage: "Fetch authenticated user's Write.as posts", - Action: api.CmdPull, + }, { + Name: "blogs", + Usage: "List blogs", + Action: commands.CmdCollections, Flags: []cli.Flag{ cli.BoolFlag{ - Name: "tor, t", - Usage: "Authenticate via Tor hidden service", - }, - cli.IntFlag{ - Name: "tor-port", - Usage: "Use a different port to connect to Tor", - Value: 9150, - }, - cli.BoolFlag{ - Name: "verbose, v", - Usage: "Make the operation more talkative", + Name: "url", + Usage: "Show list with URLs", }, }, - }, - { + }, { Name: "auth", Usage: "Authenticate with Write.as", Action: commands.CmdAuth, From d10b3ee85a15974e9581e53da57e80e1d7aa4e71 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 14:28:50 -0700 Subject: [PATCH 12/63] store user.json in user sub folder only on wf, writeas still stores in root user config directory --- config/user.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/config/user.go b/config/user.go index 359fcb4..d47a211 100644 --- a/config/user.go +++ b/config/user.go @@ -19,7 +19,10 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { if err != nil { return nil, err } - fname := filepath.Join(dir, username+".json") + if username == "user" { + username = "" + } + fname := filepath.Join(dir, username, "user.json") userJSON, err := ioutil.ReadFile(fname) if err != nil { if !fileutils.Exists(fname) { @@ -49,7 +52,11 @@ func DeleteUser(c *cli.Context) error { return err } - return fileutils.DeleteFile(filepath.Join(dir, username+".json")) + if username == "user" { + username = "" + } + + return fileutils.DeleteFile(filepath.Join(dir, username, "user.json")) } func SaveUser(c *cli.Context, u *writeas.AuthUser) error { @@ -63,16 +70,16 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { if err != nil { return err } - DirMustExist(dir) // Save file username, err := currentUser(c) if err != nil { return err } if username != "user" { - username = u.User.Username + dir = filepath.Join(dir, u.User.Username) } - err = ioutil.WriteFile(filepath.Join(dir, username+".json"), userJSON, 0600) + DirMustExist(dir) + err = ioutil.WriteFile(filepath.Join(dir, "user.json"), userJSON, 0600) if err != nil { return err } @@ -91,7 +98,11 @@ func userHostDir(c *cli.Context) (string, error) { } func currentUser(c *cli.Context) (string, error) { - cfg, err := LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) + hostDir, err := userHostDir(c) + if err != nil { + return "", err + } + cfg, err := LoadConfig(hostDir) if err != nil { return "", err } From ea3e57a2a782f58cb38a5b79d0c71206f915d66f Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 15:16:50 -0700 Subject: [PATCH 13/63] bugfix: default user should work on other domains there was a bug where a default user at the config directory root was not being used for calls not including a flag or host level config.ini --- config/user.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/user.go b/config/user.go index d47a211..af9c710 100644 --- a/config/user.go +++ b/config/user.go @@ -106,6 +106,12 @@ func currentUser(c *cli.Context) (string, error) { if err != nil { return "", err } + if cfg.Default.User == "" { + cfg, err = LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return "", err + } + } if c.GlobalString("user") != "" { return c.GlobalString("user"), nil From 572044043a6c569f21cd81c9e2a2417e549ae0a6 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 15:41:58 -0700 Subject: [PATCH 14/63] include new claim cmd in wf also update CmdClaim for new LoadUser signiture --- cmd/wf/main.go | 11 +++++++++++ commands/commands.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 87e74dc..3ede8a6 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -180,6 +180,17 @@ func main() { }, }, }, { + Name: "claim", + Usage: "Claim local unsynced posts", + Action: commands.CmdClaim, + Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: writeas posts.", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Make the operation more talkative", + }, + }, + }, { Name: "auth", Usage: "Authenticate with Write.as", Action: commands.CmdAuth, diff --git a/commands/commands.go b/commands/commands.go index a14bd69..2c2b2da 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -259,7 +259,7 @@ func CmdCollections(c *cli.Context) error { } func CmdClaim(c *cli.Context) error { - u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, err := config.LoadUser(c) if err != nil { return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) } From 9266f6622107f77a9e592e2b2b5111af0c70ff70 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 16:26:32 -0700 Subject: [PATCH 15/63] revert global flag change I had changed the global flags variable to only be those that are global this broke some compatibility with piping from stdout into the binary. also: - binary specific configuration details have moved into the same map but in main.go. only the configDir is OS dependent. - a new key in the map is for the version so each binary can have their own --- cmd/wf/config_nix.go | 4 +--- cmd/wf/config_win.go | 4 +--- cmd/wf/flags.go | 11 +---------- cmd/wf/main.go | 8 ++++++-- cmd/writeas/config_nix.go | 4 +--- cmd/writeas/config_win.go | 4 +--- cmd/writeas/flags.go | 11 +---------- cmd/writeas/main.go | 8 ++++++-- config/options.go | 7 +++---- 9 files changed, 21 insertions(+), 40 deletions(-) diff --git a/cmd/wf/config_nix.go b/cmd/wf/config_nix.go index 4abd334..ddc1bff 100644 --- a/cmd/wf/config_nix.go +++ b/cmd/wf/config_nix.go @@ -2,6 +2,4 @@ package main -var appInfo = map[string]string{ - "configDir": ".writefreely", -} +const configDir = ".writefreely" diff --git a/cmd/wf/config_win.go b/cmd/wf/config_win.go index e44b45b..1673fa1 100644 --- a/cmd/wf/config_win.go +++ b/cmd/wf/config_win.go @@ -2,6 +2,4 @@ package main -var appInfo = map[string]string{ - "configDir": "WriteFreely", -} +const configDir = "WriteFreely" diff --git a/cmd/wf/flags.go b/cmd/wf/flags.go index 854b1f8..5245bb7 100644 --- a/cmd/wf/flags.go +++ b/cmd/wf/flags.go @@ -4,16 +4,7 @@ import ( "gopkg.in/urfave/cli.v1" ) -var globalFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "tor, t", - Usage: "Perform action on Tor hidden service", - }, - cli.IntFlag{ - Name: "tor-port", - Usage: "Use a different port to connect to Tor", - Value: 9150, - }, +var flags = []cli.Flag{ cli.StringFlag{ Name: "host, H", Usage: "Operate against a custom hostname", diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 3ede8a6..31fc450 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -9,6 +9,10 @@ import ( ) func main() { + appInfo := map[string]string{ + "configDir": configDir, + "version": "1.0", + } config.DirMustExist(config.UserDataDir(appInfo["configDir"])) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", @@ -18,7 +22,7 @@ func main() { // Run the app app := cli.NewApp() app.Name = "wf" - app.Version = config.Version + app.Version = appInfo["version"] app.Usage = "Publish text quickly" // TODO: who is the author? the contributors? link to GH? app.Authors = []cli.Author{ @@ -31,7 +35,7 @@ func main() { return appInfo } app.Action = commands.CmdPost - app.Flags = globalFlags + app.Flags = append(config.PostFlags, flags...) app.Commands = []cli.Command{ { Name: "post", diff --git a/cmd/writeas/config_nix.go b/cmd/writeas/config_nix.go index 6b3cb86..6c0ed02 100644 --- a/cmd/writeas/config_nix.go +++ b/cmd/writeas/config_nix.go @@ -2,6 +2,4 @@ package main -var appInfo = map[string]string{ - "configDir": ".writeas", -} +const configDir = ".writeas" diff --git a/cmd/writeas/config_win.go b/cmd/writeas/config_win.go index 9a7eea1..43d2bca 100644 --- a/cmd/writeas/config_win.go +++ b/cmd/writeas/config_win.go @@ -2,6 +2,4 @@ package main -var appInfo = map[string]string{ - "configDir": "Write.as", -} +const configDir = "Write.as" diff --git a/cmd/writeas/flags.go b/cmd/writeas/flags.go index f19f830..5fdaf8a 100644 --- a/cmd/writeas/flags.go +++ b/cmd/writeas/flags.go @@ -4,16 +4,7 @@ import ( "gopkg.in/urfave/cli.v1" ) -var globalFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "tor, t", - Usage: "Perform action on Tor hidden service", - }, - cli.IntFlag{ - Name: "tor-port", - Usage: "Use a different port to connect to Tor", - Value: 9150, - }, +var flags = []cli.Flag{ cli.StringFlag{ Name: "user, u", Hidden: true, diff --git a/cmd/writeas/main.go b/cmd/writeas/main.go index 10750b4..d94dfca 100644 --- a/cmd/writeas/main.go +++ b/cmd/writeas/main.go @@ -9,6 +9,10 @@ import ( ) func main() { + appInfo := map[string]string{ + "configDir": configDir, + "version": "2.0", + } config.DirMustExist(config.UserDataDir(appInfo["configDir"])) cli.VersionFlag = cli.BoolFlag{ Name: "version, V", @@ -18,7 +22,7 @@ func main() { // Run the app app := cli.NewApp() app.Name = "writeas" - app.Version = config.Version + app.Version = appInfo["version"] app.Usage = "Publish text quickly" app.Authors = []cli.Author{ { @@ -30,7 +34,7 @@ func main() { return appInfo } app.Action = commands.CmdPost - app.Flags = globalFlags + app.Flags = append(config.PostFlags, flags...) app.Commands = []cli.Command{ { Name: "post", diff --git a/config/options.go b/config/options.go index 78d3335..5bd0a18 100644 --- a/config/options.go +++ b/config/options.go @@ -10,8 +10,7 @@ import ( // Application constants. const ( - Version = "2.0" - defaultUserAgent = "writeas-cli v" + Version + defaultUserAgent = "writeas-cli v" // Defaults for posts on Write.as. DefaultFont = PostFontMono WriteasBaseURL = "https://write.as" @@ -22,9 +21,9 @@ const ( func UserAgent(c *cli.Context) string { ua := c.String("user-agent") if ua == "" { - return defaultUserAgent + return defaultUserAgent + c.App.ExtraInfo()["version"] } - return ua + " (" + defaultUserAgent + ")" + return ua + " (" + defaultUserAgent + c.App.ExtraInfo()["version"] + ")" } func IsTor(c *cli.Context) bool { From d7e477a22a56e4ef848ba748920fe1b1e4a0701a Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 16:32:48 -0700 Subject: [PATCH 16/63] include new claim cmd and host/user flag in docs --- GUIDE.md | 27 ++++++++++++++++----------- README.md | 26 +++++++++++++++----------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/GUIDE.md b/GUIDE.md index 864ae45..24f5e26 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -24,24 +24,29 @@ COMMANDS: posts List all of your posts claim Claim local unsynced posts blogs List blogs + claim Claim local unsynced posts auth Authenticate with Write.as logout Log out of Write.as help, h Shows a list of commands or help for one command GLOBAL OPTIONS: - -c value, -b value Optional blog to post to - --tor, -t Perform action on Tor hidden service - --tor-port value Use a different port to connect to Tor (default: 9150) - --code Specifies this post is code - --md Returns post URL with Markdown enabled - --verbose, -v Make the operation more talkative - --font value Sets post font to given value (default: "mono") - --lang value Sets post language to given ISO 639-1 language code - --user-agent value Sets the User-Agent for API requests - --help, -h show help - --version, -V print the version + -c value, -b value Optional blog to post to + --tor, -t Perform action on Tor hidden service + --tor-port value Use a different port to connect to Tor (default: 9150) + --code Specifies this post is code + --md Returns post URL with Markdown enabled + --verbose, -v Make the operation more talkative + --font value Sets post font to given value (default: "mono") + --lang value Sets post language to given ISO 639-1 language code + --user-agent value Sets the User-Agent for API requests + --host value, -H value Operate against a custom hostname + --user value, -u value Use authenticated user, other than default + --help, -h show help + --version, -V print the version ``` +> Note: the host and user flags are only available in `wf` the community edition + #### Share something By default, `writeas` creates a post with a `monospace` typeface that doesn't word wrap (scrolls horizontally). It will return a single line with a URL, and automatically copy that URL to the clipboard: diff --git a/README.md b/README.md index 65841f4..e648af0 100644 --- a/README.md +++ b/README.md @@ -81,19 +81,23 @@ COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: - -c value, -b value Optional blog to post to - --tor, -t Perform action on Tor hidden service - --tor-port value Use a different port to connect to Tor (default: 9150) - --code Specifies this post is code - --md Returns post URL with Markdown enabled - --verbose, -v Make the operation more talkative - --font value Sets post font to given value (default: "mono") - --lang value Sets post language to given ISO 639-1 language code - --user-agent value Sets the User-Agent for API requests - --help, -h show help - --version, -V print the version + -c value, -b value Optional blog to post to + --tor, -t Perform action on Tor hidden service + --tor-port value Use a different port to connect to Tor (default: 9150) + --code Specifies this post is code + --md Returns post URL with Markdown enabled + --verbose, -v Make the operation more talkative + --font value Sets post font to given value (default: "mono") + --lang value Sets post language to given ISO 639-1 language code + --user-agent value Sets the User-Agent for API requests + --host value, -H value Operate against a custom hostname + --user value, -u value Use authenticated user, other than default + --help, -h show help + --version, -V print the version ``` +> Note: the host and user flags are only available in `wf` the community edition + ## Contributing to the CLI For a complete guide to contributing, see the [Contribution Guide](.github/CONTRIBUTING.md). From 844ce0708ecf0a2ad674ab913898fc4d5b9ba0fb Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 11 Jun 2019 17:55:17 -0700 Subject: [PATCH 17/63] local posts should use host dir also CmdPost should return an exit error if there is one --- api/api.go | 14 +++++++++++--- api/posts.go | 18 +++++++++++++----- commands/commands.go | 7 +++++-- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/api/api.go b/api/api.go index 4540182..f4999b1 100644 --- a/api/api.go +++ b/api/api.go @@ -126,14 +126,22 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, tor, code bool) ( return nil, fmt.Errorf("Unable to post: %v", err) } + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return nil, fmt.Errorf("Couldn't check for config file: %v", err) + } var url string if p.Collection != nil { url = p.Collection.URL + p.Slug } else { - if tor { - url = config.TorBaseURL + if c.GlobalString("host") != "" { + url = c.GlobalString("host") + } else if cfg.Default.Host != "" { + url = cfg.Default.Host } else if config.IsDev() { url = config.DevBaseURL + } else if tor { + url = config.TorBaseURL } else { url = config.WriteasBaseURL } @@ -242,7 +250,7 @@ func DoDelete(c *cli.Context, friendlyID, token string, tor bool) error { } else { log.Info(c, "Post deleted.") } - RemovePost(c.App.ExtraInfo()["configDir"], friendlyID) + RemovePost(c, friendlyID) return nil } diff --git a/api/posts.go b/api/posts.go index 379a2c6..cef808f 100644 --- a/api/posts.go +++ b/api/posts.go @@ -42,7 +42,11 @@ type RemotePost struct { } func AddPost(c *cli.Context, id, token string) error { - f, err := os.OpenFile(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600) + hostDir, err := config.HostDirectory(c) + if err != nil { + return fmt.Errorf("Error checking for host directory: %v", err) + } + f, err := os.OpenFile(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), hostDir, postsFile), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600) if err != nil { return fmt.Errorf("Error creating local posts list: %s", err) } @@ -76,7 +80,8 @@ func ClaimPosts(c *cli.Context, localPosts *[]Post) (*[]writeas.ClaimPostResult, } func TokenFromID(c *cli.Context, id string) string { - post := fileutils.FindLine(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile), id) + hostDir, _ := config.HostDirectory(c) + post := fileutils.FindLine(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), hostDir, postsFile), id) if post == "" { return "" } @@ -89,12 +94,15 @@ func TokenFromID(c *cli.Context, id string) string { return parts[1] } -func RemovePost(path, id string) { - fileutils.RemoveLine(filepath.Join(config.UserDataDir(path), postsFile), id) +func RemovePost(c *cli.Context, id string) { + hostDir, _ := config.HostDirectory(c) + fullPath := filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), hostDir, postsFile) + fileutils.RemoveLine(fullPath, id) } func GetPosts(c *cli.Context) *[]Post { - lines := fileutils.ReadData(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile)) + hostDir, _ := config.HostDirectory(c) + lines := fileutils.ReadData(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), hostDir, postsFile)) posts := []Post{} diff --git a/commands/commands.go b/commands/commands.go index 2c2b2da..dff931b 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -15,7 +15,10 @@ import ( func CmdPost(c *cli.Context) error { _, err := api.HandlePost(api.ReadStdIn(), c) - return err + if err != nil { + cli.NewExitError(fmt.Sprintf("Could not post: %v", err), 1) + } + return nil } func CmdNew(c *cli.Context) error { @@ -293,7 +296,7 @@ func CmdClaim(c *cli.Context) error { log.Info(c, "%sOK", status) okCount++ // only delete local if successful - api.RemovePost(c.App.ExtraInfo()["configDir"], id) + api.RemovePost(c, id) } } log.Info(c, "%d claimed, %d failed", okCount, errCount) From 2412d21fcecc751453bfce163d063b326d0f38d1 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 12 Jun 2019 19:58:02 -0700 Subject: [PATCH 18/63] use v2.0.1 go-writeas for inclusion of collection in post parameters --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 346f6ef..ed00dac 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect - github.com/writeas/go-writeas/v2 v2.0.0 + github.com/writeas/go-writeas/v2 v2.0.1 github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f // indirect github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect diff --git a/go.sum b/go.sum index d135529..72edf90 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpke github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/writeas/go-writeas/v2 v2.0.0 h1:KjDI5bQSAIH0IzkKW3uGoY98I1A4DrBsSqBklgyOvHw= github.com/writeas/go-writeas/v2 v2.0.0/go.mod h1:9sjczQJKmru925fLzg0usrU1R1tE4vBmQtGnItUMR0M= +github.com/writeas/go-writeas/v2 v2.0.1 h1:2ptcSFARmmiQh6/3Bj3dAMAvSPgntf+k2IJwV9CxpSU= +github.com/writeas/go-writeas/v2 v2.0.1/go.mod h1:9sjczQJKmru925fLzg0usrU1R1tE4vBmQtGnItUMR0M= github.com/writeas/impart v0.0.0-20180808220913-fef51864677b h1:vsZIsYneuNwXMsnh0lKviEVc8WeIqBG4RTmGWU86HpI= github.com/writeas/impart v0.0.0-20180808220913-fef51864677b/go.mod h1:sUkQZZHJfrVNsoD4QbkrYrRSQtCN3SaUPWKdohmFKT8= github.com/writeas/impart v1.1.0 h1:nPnoO211VscNkp/gnzir5UwCDEvdHThL5uELU60NFSE= From 3f8b0d0c6e03db25fa8c438d64d8dea4f8f6794e Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 12 Jun 2019 20:06:42 -0700 Subject: [PATCH 19/63] default to user collection new posts, when no flag is specified for blog or collection, will now default to the user collection. that is the username. --- config/options.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/options.go b/config/options.go index 5bd0a18..aed4c9a 100644 --- a/config/options.go +++ b/config/options.go @@ -53,6 +53,10 @@ func Collection(c *cli.Context) string { if coll := c.String("b"); coll != "" { return coll } + u, _ := LoadUser(c) + if u != nil { + return u.User.Username + } return "" } From c7bff5e86b1e3db4f1e5920f1d7ba2042db16cf4 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 13 Jun 2019 10:17:24 -0700 Subject: [PATCH 20/63] revert to go-writeas v2.0.0 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed00dac..346f6ef 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect - github.com/writeas/go-writeas/v2 v2.0.1 + github.com/writeas/go-writeas/v2 v2.0.0 github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f // indirect github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect From 83ad65c27d8c04e3218f788cc19d4a0dc088198c Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 24 Jun 2019 20:37:07 -0700 Subject: [PATCH 21/63] add config.TorURL select tor URL based on provided host flag, default host in config or default write.as onion address. in that order of precedence. --- api/api.go | 2 +- config/options.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index eaffaca..b8bda5a 100644 --- a/api/api.go +++ b/api/api.go @@ -28,7 +28,7 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { clientConfig.URL = config.WriteasBaseURL + "/api" } if config.IsTor(c) { - clientConfig.URL = config.TorBaseURL + clientConfig.URL = config.TorURL(c) clientConfig.TorPort = config.TorPort(c) } diff --git a/config/options.go b/config/options.go index fc7813f..047c5c4 100644 --- a/config/options.go +++ b/config/options.go @@ -2,6 +2,7 @@ package config import ( "net/url" + "strings" "github.com/cloudfoundry/jibber_jabber" "github.com/writeas/writeas-cli/log" @@ -38,6 +39,18 @@ func TorPort(c *cli.Context) int { return torPort } +func TorURL(c *cli.Context) string { + flagHost := c.String("host") + if flagHost != "" && strings.HasSuffix(flagHost, "onion") { + return flagHost + } + cfg, _ := LoadConfig(c.App.ExtraInfo()["configDir"]) + if cfg != nil && cfg.Default.Host != "" && strings.HasSuffix(cfg.Default.Host, "onion") { + return cfg.Default.Host + } + return TorBaseURL +} + func Language(c *cli.Context, auto bool) string { if l := c.String("lang"); l != "" { return l From a18df8880f79981c630f34843a822a6f231e4200 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 24 Jun 2019 20:38:41 -0700 Subject: [PATCH 22/63] update wf binary flags to include tor all network calls should respect tor when set --- cmd/wf/main.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 31fc450..e731bf5 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -157,7 +157,7 @@ func main() { { Name: "posts", Usage: "List all of your posts", - Description: "This will list only local posts when not currently authenticated. To list remote posts as well, first run: writeas auth .", + Description: "This will list only local posts.", Action: commands.CmdListPosts, Flags: []cli.Flag{ cli.BoolFlag{ @@ -172,6 +172,10 @@ func main() { Name: "url", Usage: "Show list with URLs", }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "Show verbose post listing, including Edit Tokens", + }, }, }, { Name: "blogs", @@ -179,6 +183,15 @@ func main() { Action: commands.CmdCollections, Flags: []cli.Flag{ cli.BoolFlag{ + Name: "tor, t", + Usage: "Authenticate via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ Name: "url", Usage: "Show list with URLs", }, @@ -190,6 +203,15 @@ func main() { Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: writeas posts.", Flags: []cli.Flag{ cli.BoolFlag{ + Name: "tor, t", + Usage: "Authenticate via Tor hidden service", + }, + cli.IntFlag{ + Name: "tor-port", + Usage: "Use a different port to connect to Tor", + Value: 9150, + }, + cli.BoolFlag{ Name: "verbose, v", Usage: "Make the operation more talkative", }, From ce3f8b5945540e75fb371af810428a3b3bf7a9ff Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 24 Jun 2019 20:47:12 -0700 Subject: [PATCH 23/63] update note about user and host flags previously was calling the new `wf` binary the community edition, now just called `writefreely` --- GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUIDE.md b/GUIDE.md index 848e060..fabd68d 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -45,7 +45,7 @@ GLOBAL OPTIONS: --version, -V print the version ``` -> Note: the host and user flags are only available in `wf` the community edition +> Note: the host and user flags are only available in `writefreely`. #### Share something From 4614de5ad98bdbca41a0ca12a716b0e50292c722 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 26 Jun 2019 15:12:52 -0400 Subject: [PATCH 24/63] Update GUIDE to reflect `posts` command changes --- GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUIDE.md b/GUIDE.md index 5ed6e63..72d51b7 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -86,7 +86,7 @@ dev My Dev Log #### List posts -This lists all posts you've published from your device +This lists all anonymous posts you've published. If authenticated, it will include posts on your account as well as any local / unclaimed posts. Pass the `--url` flag to show the list with full post URLs, and the `--md` flag to return URLs with Markdown enabled. From 56c402f3038d82559edb457d52248c651bf9d736 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 26 Jun 2019 15:27:14 -0400 Subject: [PATCH 25/63] Use go-writeas v2.0.2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 346f6ef..90d8f91 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect - github.com/writeas/go-writeas/v2 v2.0.0 + github.com/writeas/go-writeas/v2 v2.0.2 github.com/writeas/saturday v0.0.0-20170402010311-f455b05c043f // indirect github.com/writeas/web-core v0.0.0-20181111165528-05f387ffa1b3 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect diff --git a/go.sum b/go.sum index d135529..86825a8 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/writeas/go-writeas/v2 v2.0.0 h1:KjDI5bQSAIH0IzkKW3uGoY98I1A4DrBsSqBklgyOvHw= -github.com/writeas/go-writeas/v2 v2.0.0/go.mod h1:9sjczQJKmru925fLzg0usrU1R1tE4vBmQtGnItUMR0M= +github.com/writeas/go-writeas/v2 v2.0.2 h1:akvdMg89U5oBJiCkBwOXljVLTqP354uN6qnG2oOMrbk= +github.com/writeas/go-writeas/v2 v2.0.2/go.mod h1:9sjczQJKmru925fLzg0usrU1R1tE4vBmQtGnItUMR0M= github.com/writeas/impart v0.0.0-20180808220913-fef51864677b h1:vsZIsYneuNwXMsnh0lKviEVc8WeIqBG4RTmGWU86HpI= github.com/writeas/impart v0.0.0-20180808220913-fef51864677b/go.mod h1:sUkQZZHJfrVNsoD4QbkrYrRSQtCN3SaUPWKdohmFKT8= github.com/writeas/impart v1.1.0 h1:nPnoO211VscNkp/gnzir5UwCDEvdHThL5uELU60NFSE= From e5296c338778673134deaeb532b601955bd5193e Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 26 Jun 2019 15:34:44 -0400 Subject: [PATCH 26/63] Update README for v2.0 --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 65841f4..fc8f93c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ writeas-cli Command line interface for [Write.as](https://write.as). Works on Windows, macOS, and Linux. -**NOTE: the `master` branch is currently unstable while we prepare the v2.0 release! You should install via official release channel, or build from the `v1.2` tag.** - ## Features * Publish anonymously to Write.as @@ -26,10 +24,10 @@ The easiest way to get the CLI is to download a pre-built executable for your OS Get the latest version for your operating system as a standalone executable. **Windows**
-Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v1.2/writeas_1.2_windows_amd64.zip) or [32-bit](https://github.com/writeas/writeas-cli/releases/download/v1.2/writeas_1.2_windows_386.zip) executable and put it somewhere in your `%PATH%`. +Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_windows_amd64.zip) or [32-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_windows_386.zip) executable and put it somewhere in your `%PATH%`. **macOS**
-Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v1.2/writeas_1.2_darwin_amd64.tar.gz) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. +Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_darwin_amd64.tar.gz) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. **Debian-based Linux**
```bash @@ -39,7 +37,7 @@ sudo apt-get update && sudo apt-get install writeas-cli ``` **Linux (other)**
-Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v1.2/writeas_1.2_linux_amd64.tar.gz) or [32-bit](https://github.com/writeas/writeas-cli/releases/download/v1.2/writeas_1.2_linux_386.tar.gz) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. +Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_linux_amd64.tar.gz) or [32-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_linux_386.tar.gz) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. ### Go get it ```bash From 94f847b8b9ae046cdba1cffb4ec10f46d9a23b5a Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 26 Jun 2019 15:42:54 -0400 Subject: [PATCH 27/63] Fix macOS download link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc8f93c..149ea10 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Get the latest version for your operating system as a standalone executable. Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_windows_amd64.zip) or [32-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_windows_386.zip) executable and put it somewhere in your `%PATH%`. **macOS**
-Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_darwin_amd64.tar.gz) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. +Download the [64-bit](https://github.com/writeas/writeas-cli/releases/download/v2.0.0/writeas_2.0.0_darwin_amd64.zip) executable and put it somewhere in your `$PATH`, like `/usr/local/bin`. **Debian-based Linux**
```bash From 33c18cbe5d206f6cb044ce1722dc9ab4e6ab94a2 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 1 Jul 2019 16:16:18 -0400 Subject: [PATCH 28/63] Change mentions of Write.as in wf cmd to WriteFreely Particularly, in command descriptions. --- cmd/wf/main.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index e731bf5..7bb709d 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -23,7 +23,7 @@ func main() { app := cli.NewApp() app.Name = "wf" app.Version = appInfo["version"] - app.Usage = "Publish text quickly" + app.Usage = "Publish to any WriteFreely instance from the command-line." // TODO: who is the author? the contributors? link to GH? app.Authors = []cli.Author{ { @@ -42,7 +42,7 @@ func main() { Usage: "Alias for default action: create post from stdin", Action: commands.CmdPost, Flags: config.PostFlags, - Description: `Create a new post on Write.as from stdin. + Description: `Create a new post on WriteFreely from stdin. Use the --code flag to indicate that the post should use syntax highlighting. Or use the --font [value] argument to set the post's @@ -65,14 +65,14 @@ func main() { appearance, where [value] is mono, monospace (default), wrap (monospace font with word wrapping), serif, or sans. - If posting fails for any reason, 'writeas' will show you the temporary file - location and how to pipe it to 'writeas' to retry.`, + If posting fails for any reason, 'wf' will show you the temporary file + location and how to pipe it to 'wf' to retry.`, Action: commands.CmdNew, Flags: config.PostFlags, }, { Name: "publish", - Usage: "Publish a file to Write.as", + Usage: "Publish a file", Action: commands.CmdPublish, Flags: config.PostFlags, }, @@ -149,8 +149,8 @@ func main() { Usage: "Add an existing post locally", Description: `A way to add an existing post to your local store for easy editing later. - This requires a post ID (from https://write.as/[ID]) and an Edit Token - (exported from another Write.as client, such as the Android app). + This requires a post ID (from e.g. https://write.as/[ID]) and an Edit Token + (exported from another WriteFreely client, such as the Android app). `, Action: commands.CmdAdd, }, @@ -200,7 +200,7 @@ func main() { Name: "claim", Usage: "Claim local unsynced posts", Action: commands.CmdClaim, - Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: writeas posts.", + Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: wf posts.", Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -218,7 +218,7 @@ func main() { }, }, { Name: "auth", - Usage: "Authenticate with Write.as", + Usage: "Authenticate with a WriteFreely instance", Action: commands.CmdAuth, Flags: []cli.Flag{ cli.BoolFlag{ @@ -238,7 +238,7 @@ func main() { }, { Name: "logout", - Usage: "Log out of Write.as", + Usage: "Log out of a WriteFreely instance", Action: commands.CmdLogOut, Flags: []cli.Flag{ cli.BoolFlag{ @@ -262,7 +262,7 @@ func main() { {{.Name}} - {{.Usage}} USAGE: - writeas {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + wf {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .Flags}} From 8129d8440d88b4d27ab43df7615ba1d037eed8bc Mon Sep 17 00:00:00 2001 From: Rob j Loranger Date: Wed, 3 Jul 2019 18:59:32 +0000 Subject: [PATCH 29/63] CmdListPosts: update use of LoadUser config.LoadUser now only takes a *cli.Context --- commands/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/commands.go b/commands/commands.go index fcac3e2..bd6a493 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -192,7 +192,7 @@ func CmdListPosts(c *cli.Context) error { posts := api.GetPosts(c) - u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + u, _ := config.LoadUser(c) if u != nil { if config.IsTor(c) { log.Info(c, "Getting posts via hidden service...") From 5212fa12c8dc6f35ae133d2ee3b80f0caf82cd7b Mon Sep 17 00:00:00 2001 From: Rob j Loranger Date: Wed, 3 Jul 2019 19:01:19 +0000 Subject: [PATCH 30/63] fix LoadUser directory does not exist config.LoadUser was not ensuring the directory exists before trying to load any already authenticated user, fixed using config.DirMustExist --- config/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/user.go b/config/user.go index af9c710..04aa6be 100644 --- a/config/user.go +++ b/config/user.go @@ -15,6 +15,7 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { if err != nil { return nil, err } + DirMustExist(dir) username, err := currentUser(c) if err != nil { return nil, err From a53cbb16b5c5afcd162a921b5ad8fafb159ac660 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Sun, 21 Jul 2019 10:26:57 -0400 Subject: [PATCH 31/63] Show correct executable name in user messages --- api/api.go | 5 +++-- commands/commands.go | 23 ++++++++++++----------- config/files_nix.go | 3 ++- config/files_win.go | 4 +++- executable/executable.go | 13 +++++++++++++ 5 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 executable/executable.go diff --git a/api/api.go b/api/api.go index b309993..31e90e7 100644 --- a/api/api.go +++ b/api/api.go @@ -7,6 +7,7 @@ import ( writeas "github.com/writeas/go-writeas/v2" "github.com/writeas/web-core/posts" "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/executable" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) @@ -39,7 +40,7 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { if u != nil { client.SetToken(u.AccessToken) } else if authRequired { - return nil, fmt.Errorf("Not currently logged in. Authenticate with: writeas auth ") + return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") } return client, nil @@ -136,7 +137,7 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writ // Copy URL to clipboard err = clipboard.WriteAll(string(url)) if err != nil { - log.Errorln("writeas: Didn't copy to clipboard: %s", err) + log.Errorln(executable.Name()+": Didn't copy to clipboard: %s", err) } else { log.Info(c, "Copied to clipboard.") } diff --git a/commands/commands.go b/commands/commands.go index bd6a493..41b0662 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -9,6 +9,7 @@ import ( "github.com/howeyc/gopass" "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/executable" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) @@ -67,7 +68,7 @@ func CmdNew(c *cli.Context) error { func CmdPublish(c *cli.Context) error { filename := c.Args().Get(0) if filename == "" { - return cli.NewExitError("usage: writeas publish ", 1) + return cli.NewExitError("usage: "+executable.Name()+" publish ", 1) } content, err := ioutil.ReadFile(filename) if err != nil { @@ -92,7 +93,7 @@ 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) + return cli.NewExitError("usage: "+executable.Name()+" delete []", 1) } u, _ := config.LoadUser(c) @@ -101,7 +102,7 @@ func CmdDelete(c *cli.Context) error { 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) + log.ErrorlnQuit("If you have an edit token, use: "+executable.Name()+" delete %s ", friendlyID) } } @@ -124,7 +125,7 @@ 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) + return cli.NewExitError("usage: "+executable.Name()+" update []", 1) } u, _ := config.LoadUser(c) @@ -133,7 +134,7 @@ func CmdUpdate(c *cli.Context) error { 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) + log.ErrorlnQuit("If you have an edit token, use: "+executable.Name()+" update %s ", friendlyID) } } @@ -155,7 +156,7 @@ func CmdUpdate(c *cli.Context) error { func CmdGet(c *cli.Context) error { friendlyID := c.Args().Get(0) if friendlyID == "" { - return cli.NewExitError("usage: writeas get ", 1) + return cli.NewExitError("usage: "+executable.Name()+" get ", 1) } if config.IsTor(c) { @@ -175,7 +176,7 @@ 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) + return cli.NewExitError("usage: "+executable.Name()+" add ", 1) } err := api.AddPost(c, friendlyID, token) @@ -279,7 +280,7 @@ func CmdCollections(c *cli.Context) error { 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) + return cli.NewExitError("You must be authenticated to view collections.\nLog in first with: "+executable.Name()+" auth ", 1) } if config.IsTor(c) { log.Info(c, "Getting blogs via hidden service...") @@ -314,7 +315,7 @@ func CmdClaim(c *cli.Context) error { 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) + return cli.NewExitError("You must be authenticated to claim local posts.\nLog in first with: "+executable.Name()+" auth ", 1) } localPosts := api.GetPosts(c) @@ -362,14 +363,14 @@ func CmdAuth(c *cli.Context) error { 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) + return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: "+executable.Name()+" 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) + return cli.NewExitError("usage: "+executable.Name()+" auth ", 1) } fmt.Print("Password: ") diff --git a/config/files_nix.go b/config/files_nix.go index 0c10f04..13fb338 100644 --- a/config/files_nix.go +++ b/config/files_nix.go @@ -7,6 +7,7 @@ import ( "os/exec" homedir "github.com/mitchellh/go-homedir" + "github.com/writeas/writeas-cli/executable" ) const ( @@ -39,5 +40,5 @@ func EditPostCmd(fname string) *exec.Cmd { } func MessageRetryCompose(fname string) string { - return fmt.Sprintf("To retry this post, run:\n cat %s | writeas", fname) + return fmt.Sprintf("To retry this post, run:\n cat %s | %s", fname, executable.Name()) } diff --git a/config/files_win.go b/config/files_win.go index 026b803..db2f459 100644 --- a/config/files_win.go +++ b/config/files_win.go @@ -6,6 +6,8 @@ import ( "fmt" "os" "os/exec" + + "github.com/writeas/writeas-cli/executable" ) const ( @@ -22,5 +24,5 @@ func EditPostCmd(fname string) *exec.Cmd { } func MessageRetryCompose(fname string) string { - return fmt.Sprintf("To retry this post, run:\n type %s | writeas.exe", fname) + return fmt.Sprintf("To retry this post, run:\n type %s | %s", fname, executable.Name()) } diff --git a/executable/executable.go b/executable/executable.go new file mode 100644 index 0000000..697f419 --- /dev/null +++ b/executable/executable.go @@ -0,0 +1,13 @@ +// Package executable holds utility functions that assist both CLI executables, +// writeas and wf. +package executable + +import ( + "os" + "path" +) + +func Name() string { + n := os.Args[0] + return path.Base(n) +} From 1b8655f06ad65da5f6c1810fc5e5c761f9c49e83 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 13:19:12 -0400 Subject: [PATCH 32/63] Accept --host without scheme This automatically prepends https:// to any given --host value. It also adds an --insecure flag, which will instead prepend http:// The goal with this is to save some typing and encourage operations over HTTPS. Ref T595 --- api/api.go | 17 +++++++++++++++-- config/flags.go | 4 ++++ config/options.go | 6 +----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/api.go b/api/api.go index 31e90e7..b60d9c1 100644 --- a/api/api.go +++ b/api/api.go @@ -12,6 +12,19 @@ import ( cli "gopkg.in/urfave/cli.v1" ) +func HostURL(c *cli.Context) string { + host := c.GlobalString("host") + if host == "" { + return "" + } + insecure := c.Bool("insecure") + scheme := "https://" + if insecure { + scheme = "http://" + } + return scheme + host +} + func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { var client *writeas.Client var clientConfig writeas.Config @@ -19,8 +32,8 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { if err != nil { return nil, fmt.Errorf("Failed to load configuration file: %v", err) } - if c.GlobalString("host") != "" { - clientConfig.URL = c.GlobalString("host") + "/api" + if host := HostURL(c); host != "" { + clientConfig.URL = host + "/api" } else if cfg.Default.Host != "" { clientConfig.URL = cfg.Default.Host + "/api" } else if config.IsDev() { diff --git a/config/flags.go b/config/flags.go index 2c17e23..195eb8d 100644 --- a/config/flags.go +++ b/config/flags.go @@ -12,6 +12,10 @@ var PostFlags = []cli.Flag{ Value: "", }, cli.BoolFlag{ + Name: "insecure", + Usage: "Send request insecurely.", + }, + cli.BoolFlag{ Name: "tor, t", Usage: "Perform action on Tor hidden service", }, diff --git a/config/options.go b/config/options.go index 047c5c4..adbed78 100644 --- a/config/options.go +++ b/config/options.go @@ -90,11 +90,7 @@ func HostDirectory(c *cli.Context) (string, error) { } // flag takes precedence over defaults if hostFlag := c.GlobalString("host"); hostFlag != "" { - u, err := url.Parse(hostFlag) - if err != nil { - return "", err - } - return u.Hostname(), nil + return hostFlag, nil } u, err := url.Parse(cfg.Default.Host) From 4e4ccbe2e85ac21ccd9e3a9e65b58638841c425a Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 13:24:09 -0400 Subject: [PATCH 33/63] Don't default host to Write.as in wf-cli We want to keep wf-cli service-agnostic and to make sure there's no confusing overlap between this client and writeas-cli. Ref T586 --- api/api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index b60d9c1..8fac589 100644 --- a/api/api.go +++ b/api/api.go @@ -38,8 +38,10 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { clientConfig.URL = cfg.Default.Host + "/api" } else if config.IsDev() { clientConfig.URL = config.DevBaseURL + "/api" - } else { + } else if c.App.Name == "writeas" { clientConfig.URL = config.WriteasBaseURL + "/api" + } else { + return nil, fmt.Errorf("Must supply a host. Example: %s --host example.com %s", executable.Name(), c.Command.Name) } if config.IsTor(c) { clientConfig.URL = config.TorURL(c) From c8502be442ac0f6d7e42cb1ff3154792e936af15 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 15:00:18 -0400 Subject: [PATCH 34/63] Output full URL publishing with --host flag Now that no scheme is required, we need to use the helper function to include it, instead of the raw --host value. This does that. Ref T595 --- api/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/api.go b/api/api.go index 8fac589..06985d2 100644 --- a/api/api.go +++ b/api/api.go @@ -126,8 +126,8 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writ if p.Collection != nil { url = p.Collection.URL + p.Slug } else { - if c.GlobalString("host") != "" { - url = c.GlobalString("host") + if host := HostURL(c); host != "" { + url = host } else if cfg.Default.Host != "" { url = cfg.Default.Host } else if config.IsDev() { From b55874c6e66994f3b823b515e48601d630ddadaf Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 15:04:02 -0400 Subject: [PATCH 35/63] Require authentication for post operations in wf Unlike Write.as, WriteFreely doesn't support anonymous (unauthenticated) publishing. With these changes, we reflect that in wf-cli by wrapping creating, updating, and deleting posts in a func that checks authentication state. Ref T586 --- cmd/wf/commands.go | 23 +++++++++++++++++++++++ cmd/wf/main.go | 12 ++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 cmd/wf/commands.go diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go new file mode 100644 index 0000000..ba67145 --- /dev/null +++ b/cmd/wf/commands.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + + "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/executable" + cli "gopkg.in/urfave/cli.v1" +) + +func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { + return func(c *cli.Context) error { + 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 "+action+".\nLog in first with: "+executable.Name()+" auth ", 1) + } + + return f(c) + } +} diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 7bb709d..1e2f107 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -34,13 +34,13 @@ func main() { app.ExtraInfo = func() map[string]string { return appInfo } - app.Action = commands.CmdPost + app.Action = requireAuth(commands.CmdPost, "publish") app.Flags = append(config.PostFlags, flags...) app.Commands = []cli.Command{ { Name: "post", Usage: "Alias for default action: create post from stdin", - Action: commands.CmdPost, + Action: requireAuth(commands.CmdPost, "publish"), Flags: config.PostFlags, Description: `Create a new post on WriteFreely from stdin. @@ -67,19 +67,19 @@ func main() { If posting fails for any reason, 'wf' will show you the temporary file location and how to pipe it to 'wf' to retry.`, - Action: commands.CmdNew, + Action: requireAuth(commands.CmdNew, "publish"), Flags: config.PostFlags, }, { Name: "publish", Usage: "Publish a file", - Action: commands.CmdPublish, + Action: requireAuth(commands.CmdPublish, "publish"), Flags: config.PostFlags, }, { Name: "delete", Usage: "Delete a post", - Action: commands.CmdDelete, + Action: requireAuth(commands.CmdDelete, "delete"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -99,7 +99,7 @@ func main() { { Name: "update", Usage: "Update (overwrite) a post", - Action: commands.CmdUpdate, + Action: requireAuth(commands.CmdUpdate, "update"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", From 8b1cc1411cd04c0c749723cb5076fb78af488283 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 15:07:33 -0400 Subject: [PATCH 36/63] Reflect wf-cli/writeas-cli in User-Agent Previously, this would always include writeas-cli. Ref T586 --- config/options.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/config/options.go b/config/options.go index adbed78..e478cba 100644 --- a/config/options.go +++ b/config/options.go @@ -11,7 +11,8 @@ import ( // Application constants. const ( - defaultUserAgent = "writeas-cli v" + writeasUserAgent = "writeas-cli v" + wfUserAgent = "wf-cli v" // Defaults for posts on Write.as. DefaultFont = PostFontMono WriteasBaseURL = "https://write.as" @@ -21,11 +22,16 @@ const ( ) func UserAgent(c *cli.Context) string { + client := wfUserAgent + if c.App.Name == "writeas" { + client = writeasUserAgent + } + ua := c.String("user-agent") if ua == "" { - return defaultUserAgent + c.App.ExtraInfo()["version"] + return client + c.App.ExtraInfo()["version"] } - return ua + " (" + defaultUserAgent + c.App.ExtraInfo()["version"] + ")" + return ua + " (" + client + c.App.ExtraInfo()["version"] + ")" } func IsTor(c *cli.Context) bool { From 8d1b4102b847d63d95af944fe02ca11a94ae1634 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 15:13:51 -0400 Subject: [PATCH 37/63] Automatically set WF account as default on first auth Now when running `wf auth`, we'll automatically set the given host + username as the default account in .writefreely/config.ini, so subsequent requests without an explicit --host and --user will use this account. Ref T635 T586 --- cmd/wf/commands.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/wf/main.go | 4 ++-- config/user.go | 13 ++++++---- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index ba67145..ee8152d 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -3,8 +3,11 @@ package main import ( "fmt" + "github.com/writeas/writeas-cli/api" + "github.com/writeas/writeas-cli/commands" "github.com/writeas/writeas-cli/config" "github.com/writeas/writeas-cli/executable" + "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" ) @@ -21,3 +24,69 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { return f(c) } } + +func cmdAuth(c *cli.Context) error { + err := commands.CmdAuth(c) + if err != nil { + return err + } + + // Get the username from the command, just like commands.CmdAuth does + username := c.Args().Get(0) + + // Update config if this is user's first auth + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + log.Errorln("Not saving config. Unable to load config: %s", err) + return err + } + if cfg.Default.Host == "" && cfg.Default.User == "" { + // This is user's first auth, so save defaults + cfg.Default.Host = api.HostURL(c) + cfg.Default.User = username + err = config.SaveConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]), cfg) + if err != nil { + log.Errorln("Not saving config. Unable to save config: %s", err) + return err + } + fmt.Printf("Set %s on %s as default account.\n", username, c.GlobalString("host")) + } + + return nil +} + +func cmdLogOut(c *cli.Context) error { + err := commands.CmdLogOut(c) + if err != nil { + return err + } + + // Remove this from config if it's the default account + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + log.Errorln("Not updating config. Unable to load: %s", err) + return err + } + username, err := config.CurrentUser(c) + if err != nil { + log.Errorln("Not updating config. Unable to load current user: %s", err) + return err + } + reqHost := api.HostURL(c) + if reqHost == "" { + // No --host given, so we're using the default host + reqHost = cfg.Default.Host + } + if cfg.Default.Host == reqHost && cfg.Default.User == username { + // We're logging out of default username + host, so remove from config file + cfg.Default.Host = "" + cfg.Default.User = "" + err = config.SaveConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]), cfg) + if err != nil { + log.Errorln("Not updating config. Unable to save config: %s", err) + return err + } + } + + return nil +} diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 1e2f107..140cde5 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -219,7 +219,7 @@ func main() { }, { Name: "auth", Usage: "Authenticate with a WriteFreely instance", - Action: commands.CmdAuth, + Action: cmdAuth, Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -239,7 +239,7 @@ func main() { { Name: "logout", Usage: "Log out of a WriteFreely instance", - Action: commands.CmdLogOut, + Action: cmdLogOut, Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", diff --git a/config/user.go b/config/user.go index 04aa6be..b39a4f5 100644 --- a/config/user.go +++ b/config/user.go @@ -16,7 +16,7 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { return nil, err } DirMustExist(dir) - username, err := currentUser(c) + username, err := CurrentUser(c) if err != nil { return nil, err } @@ -48,7 +48,7 @@ func DeleteUser(c *cli.Context) error { return err } - username, err := currentUser(c) + username, err := CurrentUser(c) if err != nil { return err } @@ -72,7 +72,7 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { return err } // Save file - username, err := currentUser(c) + username, err := CurrentUser(c) if err != nil { return err } @@ -98,7 +98,10 @@ func userHostDir(c *cli.Context) (string, error) { return filepath.Join(dataDir, hostDir), nil } -func currentUser(c *cli.Context) (string, error) { +// CurrentUser returns the username of the user taking action in the current +// cli.Context. +func CurrentUser(c *cli.Context) (string, error) { + // Load host-level config, if host flag is set hostDir, err := userHostDir(c) if err != nil { return "", err @@ -108,12 +111,14 @@ func currentUser(c *cli.Context) (string, error) { return "", err } if cfg.Default.User == "" { + // Load app-level config cfg, err = LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) if err != nil { return "", err } } + // Use user flag value if c.GlobalString("user") != "" { return c.GlobalString("user"), nil } From eb6332945a8793cb596cbc218e1301a0bc4c64ba Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 22 Jul 2019 15:22:36 -0400 Subject: [PATCH 38/63] Remove `add` action from wf-cli WriteFreely doesn't support non-auth'd posting like Write.as, so users won't be creating non-auth'd posts with another client that they'd need to add here. Ref T586 --- cmd/wf/main.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 140cde5..46de71d 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -145,16 +145,6 @@ func main() { }, }, { - Name: "add", - Usage: "Add an existing post locally", - Description: `A way to add an existing post to your local store for easy editing later. - - This requires a post ID (from e.g. https://write.as/[ID]) and an Edit Token - (exported from another WriteFreely client, such as the Android app). -`, - Action: commands.CmdAdd, - }, - { Name: "posts", Usage: "List all of your posts", Description: "This will list only local posts.", From 60d987eed3e24ce13683876f249744e4a851e304 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 29 Jul 2019 14:40:49 -0400 Subject: [PATCH 39/63] Clean up directories on `wf logout` On logout, this deletes host / user directories if they're empty. Ref T586 --- config/user.go | 37 ++++++++++++++++++++++++++++++++++++- fileutils/fileutils.go | 16 ++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/config/user.go b/config/user.go index b39a4f5..f1a5e9a 100644 --- a/config/user.go +++ b/config/user.go @@ -57,7 +57,42 @@ func DeleteUser(c *cli.Context) error { username = "" } - return fileutils.DeleteFile(filepath.Join(dir, username, "user.json")) + // Delete user data + err = fileutils.DeleteFile(filepath.Join(dir, username, "user.json")) + if err != nil { + return err + } + + // Do additional cleanup in wf-cli + if c.App.Name == "wf" { + // Delete user dir if it's empty + userEmpty, err := fileutils.IsEmpty(filepath.Join(dir, username)) + if err != nil { + return err + } + if !userEmpty { + return nil + } + err = fileutils.DeleteFile(filepath.Join(dir, username)) + if err != nil { + return err + } + + // Delete host dir if it's empty + hostEmpty, err := fileutils.IsEmpty(dir) + if err != nil { + return err + } + if !hostEmpty { + return nil + } + err = fileutils.DeleteFile(dir) + if err != nil { + return err + } + } + + return nil } func SaveUser(c *cli.Context, u *writeas.AuthUser) error { diff --git a/fileutils/fileutils.go b/fileutils/fileutils.go index 7e4c354..eda996b 100644 --- a/fileutils/fileutils.go +++ b/fileutils/fileutils.go @@ -3,6 +3,7 @@ package fileutils import ( "bufio" "fmt" + "io" "os" "strings" ) @@ -109,3 +110,18 @@ func FindLine(p, startsWith string) string { func DeleteFile(p string) error { return os.Remove(p) } + +// IsEmpty returns whether or not the given directory is empty +func IsEmpty(d string) (bool, error) { + f, err := os.Open(d) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, nil + } + return false, err +} From b85673b799abdb0c350ab7bfac38f31a919f5663 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 1 Aug 2019 09:32:14 -0700 Subject: [PATCH 40/63] move LoadUser call out of newClient to start on a fix for user actions when logged in with one user on a given host with the wf binary, a user should now only be loaded by the caller of newClient. newClient no longer takes a bool for authRequired, all calls updated to new signature and load the user where required. --- api/api.go | 46 ++++++++++++++++++++++++++++++---------------- api/posts.go | 11 ++++++++++- api/sync.go | 10 +++++++++- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/api/api.go b/api/api.go index 06985d2..35c10fc 100644 --- a/api/api.go +++ b/api/api.go @@ -25,7 +25,7 @@ func HostURL(c *cli.Context) string { return scheme + host } -func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { +func newClient(c *cli.Context) (*writeas.Client, error) { var client *writeas.Client var clientConfig writeas.Config cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) @@ -50,13 +50,6 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { client = writeas.NewClientWith(clientConfig) client.UserAgent = config.UserAgent(c) - // TODO: load user into var shared across the app - u, _ := config.LoadUser(c) - if u != nil { - client.SetToken(u.AccessToken) - } else if authRequired { - return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") - } return client, nil } @@ -64,7 +57,7 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { // DoFetch retrieves the Write.as post with the given friendlyID, // optionally via the Tor hidden service. func DoFetch(c *cli.Context, friendlyID string) error { - cl, err := newClient(c, false) + cl, err := newClient(c) if err != nil { return err } @@ -84,11 +77,18 @@ func DoFetch(c *cli.Context, friendlyID string) error { // DoFetchPosts retrieves all remote posts for the // authenticated user func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) { - cl, err := newClient(c, true) + cl, err := newClient(c) if err != nil { return nil, fmt.Errorf("%v", err) } + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + posts, err := cl.GetUserPosts() if err != nil { return nil, err @@ -100,7 +100,7 @@ func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) { // DoPost creates a Write.as post, returning an error if it was // unsuccessful. func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writeas.Post, error) { - cl, err := newClient(c, false) + cl, err := newClient(c) if err != nil { return nil, fmt.Errorf("%v", err) } @@ -166,7 +166,7 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writ // DoFetchCollections retrieves a list of the currently logged in users // collections. func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) { - cl, err := newClient(c, true) + cl, err := newClient(c) if err != nil { if config.Debug() { log.ErrorlnQuit("could not create client: %v", err) @@ -174,6 +174,13 @@ func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) { return nil, fmt.Errorf("Couldn't create new client") } + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + colls, err := cl.GetUserCollections() if err != nil { if config.Debug() { @@ -198,7 +205,7 @@ func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) { // DoUpdate updates the given post on Write.as. func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code bool) error { - cl, err := newClient(c, false) + cl, err := newClient(c) if err != nil { return fmt.Errorf("%v", err) } @@ -224,7 +231,7 @@ func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code // DoDelete deletes the given post on Write.as, and removes any local references func DoDelete(c *cli.Context, friendlyID, token string) error { - cl, err := newClient(c, false) + cl, err := newClient(c) if err != nil { return fmt.Errorf("%v", err) } @@ -243,7 +250,7 @@ func DoDelete(c *cli.Context, friendlyID, token string) error { } func DoLogIn(c *cli.Context, username, password string) error { - cl, err := newClient(c, false) + cl, err := newClient(c) if err != nil { return fmt.Errorf("%v", err) } @@ -265,11 +272,18 @@ func DoLogIn(c *cli.Context, username, password string) error { } func DoLogOut(c *cli.Context) error { - cl, err := newClient(c, true) + cl, err := newClient(c) if err != nil { return fmt.Errorf("%v", err) } + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else if c.App.Name == "writeas" { + return fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + err = cl.LogOut() if err != nil { if config.Debug() { diff --git a/api/posts.go b/api/posts.go index 3c1e2bf..033cac3 100644 --- a/api/posts.go +++ b/api/posts.go @@ -12,6 +12,7 @@ import ( writeas "github.com/writeas/go-writeas/v2" "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/executable" "github.com/writeas/writeas-cli/fileutils" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" @@ -64,10 +65,18 @@ func AddPost(c *cli.Context, id, token string) error { // ClaimPost adds a local post to the authenticated user's account and deletes // the local reference func ClaimPosts(c *cli.Context, localPosts *[]Post) (*[]writeas.ClaimPostResult, error) { - cl, err := newClient(c, true) + cl, err := newClient(c) if err != nil { return nil, err } + + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + postsToClaim := make([]writeas.OwnedPostParams, len(*localPosts)) for i, post := range *localPosts { postsToClaim[i] = writeas.OwnedPostParams{ diff --git a/api/sync.go b/api/sync.go index b025608..e57a31b 100644 --- a/api/sync.go +++ b/api/sync.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/writeas/writeas-cli/config" + "github.com/writeas/writeas-cli/executable" "github.com/writeas/writeas-cli/fileutils" "github.com/writeas/writeas-cli/log" cli "gopkg.in/urfave/cli.v1" @@ -28,11 +29,18 @@ func CmdPull(c *cli.Context) error { syncSetUp(c, cfg) } - cl, err := newClient(c, true) + cl, err := newClient(c) if err != nil { return err } + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + // Fetch posts posts, err := cl.GetUserPosts() if err != nil { From c218012d4243db76a53c38776320eed8065334a3 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 10:43:56 -0700 Subject: [PATCH 41/63] export UserHostDir for use outside of package --- config/user.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/user.go b/config/user.go index f1a5e9a..adbfa64 100644 --- a/config/user.go +++ b/config/user.go @@ -11,7 +11,7 @@ import ( ) func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { - dir, err := userHostDir(c) + dir, err := UserHostDir(c) if err != nil { return nil, err } @@ -43,7 +43,7 @@ func LoadUser(c *cli.Context) (*writeas.AuthUser, error) { } func DeleteUser(c *cli.Context) error { - dir, err := userHostDir(c) + dir, err := UserHostDir(c) if err != nil { return err } @@ -102,7 +102,7 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { return err } - dir, err := userHostDir(c) + dir, err := UserHostDir(c) if err != nil { return err } @@ -122,9 +122,9 @@ func SaveUser(c *cli.Context, u *writeas.AuthUser) error { return nil } -// userHostDir returns the path to the user data directory with the host based +// 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) { +func UserHostDir(c *cli.Context) (string, error) { dataDir := UserDataDir(c.App.ExtraInfo()["configDir"]) hostDir, err := HostDirectory(c) if err != nil { @@ -137,7 +137,7 @@ func userHostDir(c *cli.Context) (string, error) { // cli.Context. func CurrentUser(c *cli.Context) (string, error) { // Load host-level config, if host flag is set - hostDir, err := userHostDir(c) + hostDir, err := UserHostDir(c) if err != nil { return "", err } From 58dd3f985a7fb342acaad419ad23f2ef9427d775 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 10:44:51 -0700 Subject: [PATCH 42/63] helper func for logged in users this function checks the host based path for any logged in users and returns the number or users, a list of usernames and an error if any --- cmd/wf/commands.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index ee8152d..c252e6a 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "os" + "path/filepath" "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/commands" @@ -25,6 +27,33 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { } } +// usersLoggedIn checks for logged in users for the set host flag +// it returns the number of users and a slice of usernames +func usersLoggedIn(c *cli.Context) (int, []string, error) { + path, err := config.UserHostDir(c) + if err != nil { + return 0, nil, err + } + dir, err := os.Open(path) + if err != nil { + return 0, nil, err + } + contents, err := dir.Readdir(0) + if err != nil { + return 0, nil, err + } + var names []string + for _, file := range contents { + if file.IsDir() { + // stat user.json + if _, err := os.Stat(filepath.Join(path, file.Name(), "user.json")); err == nil { + names = append(names, file.Name()) + } + } + } + return len(names), names, nil +} + func cmdAuth(c *cli.Context) error { err := commands.CmdAuth(c) if err != nil { From 630a867a34bf6f1d84f71f8b71b2f4dfabd15164 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 10:46:55 -0700 Subject: [PATCH 43/63] update requireAuth helper, check logged in users this uses the usersLoggedIn helper to check for already logged in users, selecting the single user when only one present and returning and error when multiple are logged in. --- cmd/wf/commands.go | 18 +++++++++++++++++- cmd/wf/main.go | 8 ++++---- commands/commands.go | 6 +++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index c252e6a..a725429 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -15,9 +15,25 @@ import ( func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { return func(c *cli.Context) error { + // check for logged in users when host is provided without user + if c.GlobalIsSet("host") && !c.GlobalIsSet("user") { + // multiple users should display a list + if num, users, err := usersLoggedIn(c); num > 1 && err == nil { + return cli.NewExitError(fmt.Sprintf("Multiple logged in users, please use '-u' or '-user' to specify one of:\n%s", users), 1) + } else if num == 1 && err == nil { + // single user found for host should be set as user flag so LoadUser can + // succeed, and notify the client + if err := c.GlobalSet("user", users[0]); err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to set user flag for only logged in user at host %s: %v", users[0], err), 1) + } + fmt.Printf("Host specified without user flag, using logged in user: %s\n", users[0]) + } else if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to check for logged in users: %v", err), 1) + } + } u, err := config.LoadUser(c) if err != nil { - return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) + return cli.NewExitError(fmt.Sprintf("Couldn't load user: %v", err), 1) } if u == nil { return cli.NewExitError("You must be authenticated to "+action+".\nLog in first with: "+executable.Name()+" auth ", 1) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 46de71d..7cce4c9 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -148,7 +148,7 @@ func main() { Name: "posts", Usage: "List all of your posts", Description: "This will list only local posts.", - Action: commands.CmdListPosts, + Action: requireAuth(commands.CmdListPosts, "posts"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "id", @@ -170,7 +170,7 @@ func main() { }, { Name: "blogs", Usage: "List blogs", - Action: commands.CmdCollections, + Action: requireAuth(commands.CmdCollections, "blogs"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -189,7 +189,7 @@ func main() { }, { Name: "claim", Usage: "Claim local unsynced posts", - Action: commands.CmdClaim, + Action: requireAuth(commands.CmdClaim, "claim"), Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: wf posts.", Flags: []cli.Flag{ cli.BoolFlag{ @@ -229,7 +229,7 @@ func main() { { Name: "logout", Usage: "Log out of a WriteFreely instance", - Action: cmdLogOut, + Action: requireAuth(cmdLogOut, "logout"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", diff --git a/commands/commands.go b/commands/commands.go index 41b0662..19119b1 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -357,18 +357,18 @@ func CmdClaim(c *cli.Context) error { } func CmdAuth(c *cli.Context) error { + username := c.Args().Get(0) // Check configuration 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: "+executable.Name()+" logout", 1) + if u != nil && u.AccessToken != "" && username == u.User.Username { + return cli.NewExitError("You're already authenticated as "+u.User.Username, 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: "+executable.Name()+" auth ", 1) } From 55dcf5e79c9b30ff40d3234cd07e95e2a33cb719 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 10:58:36 -0700 Subject: [PATCH 44/63] CurrentUser only use global when needed by needed: when host flag was supplied, matches that configured and a user is set in config as well. they should be a pair. --- config/user.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/config/user.go b/config/user.go index adbfa64..f62e3d7 100644 --- a/config/user.go +++ b/config/user.go @@ -147,10 +147,17 @@ func CurrentUser(c *cli.Context) (string, error) { } if cfg.Default.User == "" { // Load app-level config - cfg, err = LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) + globalCFG, err := LoadConfig(UserDataDir(c.App.ExtraInfo()["configDir"])) if err != nil { return "", err } + // only user global defaults when both are set and hosts match + if globalCFG.Default.User != "" && + globalCFG.Default.Host != "" && + c.GlobalIsSet("host") && + globalCFG.Default.Host == c.GlobalString("host") { + cfg = globalCFG + } } // Use user flag value From 941d2343951193160840a22f0eb91eb1164b0443 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 11:09:02 -0700 Subject: [PATCH 45/63] log.Info will respect global flags --- log/logging.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/logging.go b/log/logging.go index a514a7c..42da556 100644 --- a/log/logging.go +++ b/log/logging.go @@ -10,7 +10,7 @@ import ( // Info logs general diagnostic messages, shown only when the -v or --verbose // flag is provided. func Info(c *cli.Context, s string, p ...interface{}) { - if c.Bool("v") || c.Bool("verbose") { + if c.Bool("v") || c.Bool("verbose") || c.GlobalBool("v") || c.GlobalBool("verbose") { fmt.Fprintf(os.Stderr, s+"\n", p...) } } From 72dbd1c7eff4c2f1a483b20a80a313fdc239b58c Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 2 Aug 2019 11:09:39 -0700 Subject: [PATCH 46/63] requireAuth host message verbose only --- cmd/wf/commands.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index a725429..3b64bc1 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/writeas/writeas-cli/api" "github.com/writeas/writeas-cli/commands" @@ -19,14 +20,14 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { if c.GlobalIsSet("host") && !c.GlobalIsSet("user") { // multiple users should display a list if num, users, err := usersLoggedIn(c); num > 1 && err == nil { - return cli.NewExitError(fmt.Sprintf("Multiple logged in users, please use '-u' or '-user' to specify one of:\n%s", users), 1) + return cli.NewExitError(fmt.Sprintf("Multiple logged in users, please use '-u' or '-user' to specify one of:\n%s", strings.Join(users, ", ")), 1) } else if num == 1 && err == nil { // single user found for host should be set as user flag so LoadUser can // succeed, and notify the client if err := c.GlobalSet("user", users[0]); err != nil { return cli.NewExitError(fmt.Sprintf("Failed to set user flag for only logged in user at host %s: %v", users[0], err), 1) } - fmt.Printf("Host specified without user flag, using logged in user: %s\n", users[0]) + log.Info(c, "Host specified without user flag, using logged in user: %s\n", users[0]) } else if err != nil { return cli.NewExitError(fmt.Sprintf("Failed to check for logged in users: %v", err), 1) } From 836eea7dab19b13de9004fdc75ded61a5abc0475 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 09:22:31 -0700 Subject: [PATCH 47/63] newClient must prepend scheme to default host this updates newClient to prepend the scheme https to the default host found in the global config, and only use said host when both the user and host are configured --- api/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/api.go b/api/api.go index 35c10fc..92cda2b 100644 --- a/api/api.go +++ b/api/api.go @@ -34,8 +34,8 @@ func newClient(c *cli.Context) (*writeas.Client, error) { } if host := HostURL(c); host != "" { clientConfig.URL = host + "/api" - } else if cfg.Default.Host != "" { - clientConfig.URL = cfg.Default.Host + "/api" + } else if cfg.Default.Host != "" && cfg.Default.User != "" { + clientConfig.URL = "https://" + cfg.Default.Host + "/api" } else if config.IsDev() { clientConfig.URL = config.DevBaseURL + "/api" } else if c.App.Name == "writeas" { From 44501a2e8ca94ac7a7628ce626cbc7fbfe899723 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 09:26:49 -0700 Subject: [PATCH 48/63] use default user when authenticating if both a user and host are configured, the command auth should assume that user and host when none specified --- commands/commands.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/commands/commands.go b/commands/commands.go index 19119b1..f3dd32a 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -368,9 +368,17 @@ func CmdAuth(c *cli.Context) error { } // Validate arguments and get password - // TODO: after global config, check for default user if username == "" { - return cli.NewExitError("usage: "+executable.Name()+" auth ", 1) + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to load config: %v", err), 1) + } + if cfg.Default.Host != "" && cfg.Default.User != "" { + username = cfg.Default.User + fmt.Printf("No user provided, using default user %s for host %s...\n", cfg.Default.User, cfg.Default.Host) + } else { + return cli.NewExitError("usage: "+executable.Name()+" auth ", 1) + } } fmt.Print("Password: ") From 8625e42ce7ca9a61f7f6c509f3e8282eeb06c2ed Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 09:28:09 -0700 Subject: [PATCH 49/63] do not parse url from configured host now that the scheme is not required in the configuration file, we can assume the configured value is correct. as with other uses, the configured value is only used when noth host and user are present. --- config/options.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/options.go b/config/options.go index e478cba..895f518 100644 --- a/config/options.go +++ b/config/options.go @@ -1,7 +1,6 @@ package config import ( - "net/url" "strings" "github.com/cloudfoundry/jibber_jabber" @@ -99,9 +98,9 @@ func HostDirectory(c *cli.Context) (string, error) { return hostFlag, nil } - u, err := url.Parse(cfg.Default.Host) - if err != nil { - return "", err + if cfg.Default.Host != "" && cfg.Default.User != "" { + return cfg.Default.Host, nil } - return u.Hostname(), nil + + return "", nil } From dea0eb2d4457376a3d4d30b531c322c09784d204 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 09:30:56 -0700 Subject: [PATCH 50/63] wf check for config when auth required this adds a check for configured defaults during the command pre check when authentication is required. using the values found if both are present and only if both flags are ommitted. --- cmd/wf/commands.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index 3b64bc1..4ffabca 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -31,6 +31,24 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { } else if err != nil { return cli.NewExitError(fmt.Sprintf("Failed to check for logged in users: %v", err), 1) } + } else if !c.GlobalIsSet("host") && !c.GlobalIsSet("user") { + // check for global configured pair host/user + cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to load config from file: %v", err), 1) + // set flags if found + } + // set flags if both were found in config + if cfg.Default.Host != "" && cfg.Default.User != "" { + err = c.GlobalSet("host", cfg.Default.Host) + if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to set host from global config: %v", err), 1) + } + err = c.GlobalSet("user", cfg.Default.User) + if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to set user from global config: %v", err), 1) + } + } } u, err := config.LoadUser(c) if err != nil { From abc78c7652a43e3c998416305fa2038e6c92619f Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 10:06:09 -0700 Subject: [PATCH 51/63] CurrentUser should always return the flag if set previously when the user flag was provided, the function would still try to get a user from config. flag should take precedence --- config/user.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/user.go b/config/user.go index f62e3d7..45c287d 100644 --- a/config/user.go +++ b/config/user.go @@ -136,6 +136,11 @@ func UserHostDir(c *cli.Context) (string, error) { // CurrentUser returns the username of the user taking action in the current // cli.Context. func CurrentUser(c *cli.Context) (string, error) { + // Use user flag value + if c.GlobalString("user") != "" { + return c.GlobalString("user"), nil + } + // Load host-level config, if host flag is set hostDir, err := UserHostDir(c) if err != nil { @@ -160,10 +165,5 @@ func CurrentUser(c *cli.Context) (string, error) { } } - // Use user flag value - if c.GlobalString("user") != "" { - return c.GlobalString("user"), nil - } - return cfg.Default.User, nil } From 6dbf4f98cd7c46457bd4508c182bc13c733ca288 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 8 Aug 2019 10:07:42 -0700 Subject: [PATCH 52/63] command auth should use user flag if the user flag is provided auth should attempt to authenticate with that user. --- commands/commands.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/commands/commands.go b/commands/commands.go index f3dd32a..9419cc8 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -358,6 +358,9 @@ func CmdClaim(c *cli.Context) error { func CmdAuth(c *cli.Context) error { username := c.Args().Get(0) + if username == "" && c.GlobalIsSet("user") { + username = c.GlobalString("user") + } // Check configuration u, err := config.LoadUser(c) if err != nil { From d8d41a972944afc1768fa3046227f0a9b39c5bf6 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 9 Sep 2019 09:35:51 -0700 Subject: [PATCH 53/63] check for any logged in user when no flag passed this adds a check for any logged in users on any host when neither the user or host flag are passed and not default pair is configured --- cmd/wf/commands.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index 4ffabca..6a4950b 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -48,6 +48,14 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { if err != nil { return cli.NewExitError(fmt.Sprintf("Failed to set user from global config: %v", err), 1) } + } else { + num, err := totalUsersLoggedIn(c) + if err != nil { + return cli.NewExitError(fmt.Sprintf("Failed to check for logged in users: %v", err), 1) + } else if num > 0 { + return cli.NewExitError("You are authenticated, but have no default user/host set. Supply -user and -host flags.", 1) + } + fmt.Println(num) } } u, err := config.LoadUser(c) @@ -89,6 +97,41 @@ func usersLoggedIn(c *cli.Context) (int, []string, error) { return len(names), names, nil } +// totalUsersLoggedIn checks for logged in users for any host +// it returns the number of users and an error if any +func totalUsersLoggedIn(c *cli.Context) (int, error) { + path := config.UserDataDir(c.App.ExtraInfo()["configDir"]) + dir, err := os.Open(path) + if err != nil { + return 0, err + } + contents, err := dir.Readdir(0) + if err != nil { + return 0, err + } + count := 0 + for _, file := range contents { + if file.IsDir() { + subDir, err := os.Open(filepath.Join(path, file.Name())) + if err != nil { + return 0, err + } + subContents, err := subDir.Readdir(0) + if err != nil { + return 0, err + } + for _, subFile := range subContents { + if subFile.IsDir() { + if _, err := os.Stat(filepath.Join(path, file.Name(), subFile.Name(), "user.json")); err == nil { + count++ + } + } + } + } + } + return count, nil +} + func cmdAuth(c *cli.Context) error { err := commands.CmdAuth(c) if err != nil { From 284087a32f37d939d0b49c33f4823a24b57f4814 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 9 Sep 2019 10:32:26 -0700 Subject: [PATCH 54/63] remove println --- cmd/wf/commands.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/wf/commands.go b/cmd/wf/commands.go index 6a4950b..1a9e6c4 100644 --- a/cmd/wf/commands.go +++ b/cmd/wf/commands.go @@ -55,7 +55,6 @@ func requireAuth(f cli.ActionFunc, action string) cli.ActionFunc { } else if num > 0 { return cli.NewExitError("You are authenticated, but have no default user/host set. Supply -user and -host flags.", 1) } - fmt.Println(num) } } u, err := config.LoadUser(c) From 86e9757c4b6a731d02304a86e69b0dd86eca33f0 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Mon, 9 Sep 2019 13:07:48 -0700 Subject: [PATCH 55/63] set token on new client for api post --- api/api.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/api.go b/api/api.go index 92cda2b..7d77a5b 100644 --- a/api/api.go +++ b/api/api.go @@ -105,6 +105,13 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writ return nil, fmt.Errorf("%v", err) } + u, _ := config.LoadUser(c) + if u != nil && c.App.Name == "wf" { + cl.SetToken(u.AccessToken) + } else { + return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") + } + pp := &writeas.PostParams{ Font: config.GetFont(code, font), Collection: config.Collection(c), From 96f57d52e8bc0cc821d4632e16fe2fd5dfd2bf69 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 08:55:09 -0700 Subject: [PATCH 56/63] fix user auth for writeas posting --- api/api.go | 2 +- config/user.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 7d77a5b..3761658 100644 --- a/api/api.go +++ b/api/api.go @@ -106,7 +106,7 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writ } u, _ := config.LoadUser(c) - if u != nil && c.App.Name == "wf" { + if u != nil { cl.SetToken(u.AccessToken) } else { return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth ") diff --git a/config/user.go b/config/user.go index 45c287d..f39cb73 100644 --- a/config/user.go +++ b/config/user.go @@ -136,6 +136,9 @@ func UserHostDir(c *cli.Context) (string, error) { // CurrentUser returns the username of the user taking action in the current // cli.Context. func CurrentUser(c *cli.Context) (string, error) { + if c.App.Name == "writeas" { + return "user", nil + } // Use user flag value if c.GlobalString("user") != "" { return c.GlobalString("user"), nil From 83bc1cf310965c2a6aa352ae561bbfbfbec6a300 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 09:00:20 -0700 Subject: [PATCH 57/63] fix hostname schema dir issues --- api/api.go | 10 +++++++++- config/options.go | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 3761658..ab665e5 100644 --- a/api/api.go +++ b/api/api.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "strings" "github.com/atotto/clipboard" writeas "github.com/writeas/go-writeas/v2" @@ -18,6 +19,9 @@ func HostURL(c *cli.Context) string { return "" } insecure := c.Bool("insecure") + if parts := strings.Split(host, "://"); len(parts) > 1 { + host = parts[1] + } scheme := "https://" if insecure { scheme = "http://" @@ -35,7 +39,11 @@ func newClient(c *cli.Context) (*writeas.Client, error) { if host := HostURL(c); host != "" { clientConfig.URL = host + "/api" } else if cfg.Default.Host != "" && cfg.Default.User != "" { - clientConfig.URL = "https://" + cfg.Default.Host + "/api" + if parts := strings.Split(cfg.Default.Host, "://"); len(parts) > 1 { + clientConfig.URL = cfg.Default.Host + "/api" + } else { + clientConfig.URL = "https://" + cfg.Default.Host + "/api" + } } else if config.IsDev() { clientConfig.URL = config.DevBaseURL + "/api" } else if c.App.Name == "writeas" { diff --git a/config/options.go b/config/options.go index 895f518..4232bf9 100644 --- a/config/options.go +++ b/config/options.go @@ -95,10 +95,16 @@ func HostDirectory(c *cli.Context) (string, error) { } // flag takes precedence over defaults if hostFlag := c.GlobalString("host"); hostFlag != "" { + if parts := strings.Split(hostFlag, "://"); len(parts) > 1 { + return parts[1], nil + } return hostFlag, nil } if cfg.Default.Host != "" && cfg.Default.User != "" { + if parts := strings.Split(cfg.Default.Host, "://"); len(parts) > 1 { + return parts[1], nil + } return cfg.Default.Host, nil } From 024b7090acf00daca7565decc5af8af522232352 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 09:00:48 -0700 Subject: [PATCH 58/63] clean up CurrentUser global cfg logic --- config/user.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/config/user.go b/config/user.go index f39cb73..1a90c55 100644 --- a/config/user.go +++ b/config/user.go @@ -159,11 +159,10 @@ func CurrentUser(c *cli.Context) (string, error) { if err != nil { return "", err } - // only user global defaults when both are set and hosts match + // only use global defaults when both are set and no host flag if globalCFG.Default.User != "" && globalCFG.Default.Host != "" && - c.GlobalIsSet("host") && - globalCFG.Default.Host == c.GlobalString("host") { + !c.GlobalIsSet("host") { cfg = globalCFG } } From b60ae1edc079a083b97d696a2b9dd1e389810122 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 10:33:52 -0700 Subject: [PATCH 59/63] get tokens for update and delete this changes DoUpdate and DoDelete to load the user and set the access token in the client when no edit token is provided. --- api/api.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/api/api.go b/api/api.go index ab665e5..dd550c0 100644 --- a/api/api.go +++ b/api/api.go @@ -225,6 +225,15 @@ func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code return fmt.Errorf("%v", err) } + if token == "" { + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return fmt.Errorf("You must either provide and edit token or log in to delete a post.") + } + } + params := writeas.PostParams{} params.Title, params.Content = posts.ExtractTitle(string(post)) if lang := config.Language(c, false); lang != "" { @@ -251,6 +260,15 @@ func DoDelete(c *cli.Context, friendlyID, token string) error { return fmt.Errorf("%v", err) } + if token == "" { + u, _ := config.LoadUser(c) + if u != nil { + cl.SetToken(u.AccessToken) + } else { + return fmt.Errorf("You must either provide and edit token or log in to delete a post.") + } + } + err = cl.DeletePost(friendlyID, token) if err != nil { if config.Debug() { From 05380e618fd0d46690b70d5f84eaa280c0f9507a Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 10:59:36 -0700 Subject: [PATCH 60/63] fix post url for non standard hostnames --- commands/commands.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/commands/commands.go b/commands/commands.go index 9419cc8..751e984 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "strings" "text/tabwriter" "github.com/howeyc/gopass" @@ -262,9 +263,28 @@ func CmdListPosts(c *cli.Context) error { } func getPostURL(c *cli.Context, slug string) string { - base := config.WriteasBaseURL - if config.IsDev() { - base = config.DevBaseURL + var base string + if c.App.Name == "writeas" { + if config.IsDev() { + base = config.DevBaseURL + } else { + base = config.WriteasBaseURL + } + } else { + if host := api.HostURL(c); host != "" { + base = host + } else { + // TODO handle error, or load config globally, see T601 + // https://phabricator.write.as/T601 + cfg, _ := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"])) + if cfg.Default.Host != "" && cfg.Default.User != "" { + if parts := strings.Split(cfg.Default.Host, "://"); len(parts) > 1 { + base = cfg.Default.Host + } else { + base = "https://" + cfg.Default.Host + } + } + } } ext := "" // Output URL in requested format From 47d8bb3e6d6e1ff7bc3424986316348cf583ab3d Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Tue, 10 Sep 2019 11:06:43 -0700 Subject: [PATCH 61/63] wf/post list: draft posts not anonymous --- commands/commands.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commands/commands.go b/commands/commands.go index 751e984..29ddc21 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -207,7 +207,11 @@ func CmdListPosts(c *cli.Context) error { } if len(remotePosts) > 0 { - fmt.Println("Anonymous Posts") + if c.App.Name == "wf" { + fmt.Println("Draft Posts") + } else { + fmt.Println("Anonymous Posts") + } if details { identifier := "URL" if ids || !urls { From 1123705eb7ede3260d13ee4ba7c093f574202ffa Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 18 Sep 2019 16:34:30 -0400 Subject: [PATCH 62/63] Fix un-auth'd messages on various commands --- cmd/wf/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/wf/main.go b/cmd/wf/main.go index 7cce4c9..ef9d8f7 100644 --- a/cmd/wf/main.go +++ b/cmd/wf/main.go @@ -79,7 +79,7 @@ func main() { { Name: "delete", Usage: "Delete a post", - Action: requireAuth(commands.CmdDelete, "delete"), + Action: requireAuth(commands.CmdDelete, "delete a post"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -99,7 +99,7 @@ func main() { { Name: "update", Usage: "Update (overwrite) a post", - Action: requireAuth(commands.CmdUpdate, "update"), + Action: requireAuth(commands.CmdUpdate, "update a post"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -148,7 +148,7 @@ func main() { Name: "posts", Usage: "List all of your posts", Description: "This will list only local posts.", - Action: requireAuth(commands.CmdListPosts, "posts"), + Action: requireAuth(commands.CmdListPosts, "view posts"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "id", @@ -170,7 +170,7 @@ func main() { }, { Name: "blogs", Usage: "List blogs", - Action: requireAuth(commands.CmdCollections, "blogs"), + Action: requireAuth(commands.CmdCollections, "view blogs"), Flags: []cli.Flag{ cli.BoolFlag{ Name: "tor, t", @@ -189,7 +189,7 @@ func main() { }, { Name: "claim", Usage: "Claim local unsynced posts", - Action: requireAuth(commands.CmdClaim, "claim"), + Action: requireAuth(commands.CmdClaim, "claim unsynced posts"), Description: "This will claim any unsynced posts local to this machine. To see which posts these are run: wf posts.", Flags: []cli.Flag{ cli.BoolFlag{ From ff67ed2bb8c63a5da2eacf8b97788ca50614615c Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 18 Sep 2019 14:10:49 -0700 Subject: [PATCH 63/63] remove default collection of username --- config/options.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/options.go b/config/options.go index 4232bf9..9a163a6 100644 --- a/config/options.go +++ b/config/options.go @@ -79,10 +79,6 @@ func Collection(c *cli.Context) string { if coll := c.String("b"); coll != "" { return coll } - u, _ := LoadUser(c) - if u != nil { - return u.User.Username - } return "" }