first moving everything out into the base package should be followed by some refactoring and reorganizing before creating two different binaries for write.as and writefreelypull/27/head
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" | |||
@@ -11,16 +11,12 @@ import ( | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
const ( | |||
defaultUserAgent = "writeas-cli v" + version | |||
) | |||
func client(userAgent string, tor bool) *writeas.Client { | |||
var client *writeas.Client | |||
if tor { | |||
client = writeas.NewTorClient(torPort) | |||
} else { | |||
if isDev() { | |||
if IsDev() { | |||
client = writeas.NewDevClient() | |||
} else { | |||
client = writeas.NewClient() | |||
@@ -36,7 +32,7 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { | |||
if isTor(c) { | |||
client = writeas.NewTorClient(torPort) | |||
} else { | |||
if isDev() { | |||
if IsDev() { | |||
client = writeas.NewDevClient() | |||
} else { | |||
client = writeas.NewClient() | |||
@@ -44,7 +40,7 @@ func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) { | |||
} | |||
client.UserAgent = userAgent(c) | |||
// TODO: load user into var shared across the app | |||
u, _ := loadUser() | |||
u, _ := LoadUser(userDataDir()) | |||
if u != nil { | |||
client.SetToken(u.AccessToken) | |||
} else if authRequired { | |||
@@ -94,11 +90,11 @@ func DoPost(c *cli.Context, post []byte, font string, encrypt, tor, code bool) ( | |||
url = p.Collection.URL + p.Slug | |||
} else { | |||
if tor { | |||
url = torBaseURL | |||
} else if isDev() { | |||
url = devBaseURL | |||
url = TorBaseURL | |||
} else if IsDev() { | |||
url = DevBaseURL | |||
} else { | |||
url = writeasBaseURL | |||
url = WriteasBaseURL | |||
} | |||
url += "/" + p.ID | |||
// Output URL in requested format | |||
@@ -188,7 +184,7 @@ func DoLogIn(c *cli.Context, username, password string) error { | |||
return err | |||
} | |||
err = saveUser(u) | |||
err = SaveUser(userDataDir(), u) | |||
if err != nil { | |||
return err | |||
} | |||
@@ -211,7 +207,7 @@ func DoLogOut(c *cli.Context) error { | |||
} | |||
// Delete local user data | |||
err = fileutils.DeleteFile(filepath.Join(userDataDir(), userFile)) | |||
err = fileutils.DeleteFile(filepath.Join(userDataDir(), UserFile)) | |||
if err != nil { | |||
return err | |||
} |
@@ -0,0 +1,51 @@ | |||
package cmd | |||
import ( | |||
writeascli "github.com/writeas/writeas-cli" | |||
"gopkg.in/urfave/cli.v1" | |||
) | |||
// Available flags for creating posts | |||
var PostFlags = []cli.Flag{ | |||
cli.StringFlag{ | |||
Name: "c, b", | |||
Usage: "Optional blog to post to", | |||
Value: "", | |||
}, | |||
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: "code", | |||
Usage: "Specifies this post is code", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "md", | |||
Usage: "Returns post URL with Markdown enabled", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "verbose, v", | |||
Usage: "Make the operation more talkative", | |||
}, | |||
cli.StringFlag{ | |||
Name: "font", | |||
Usage: "Sets post font to given value", | |||
Value: writeascli.DefaultFont, | |||
}, | |||
cli.StringFlag{ | |||
Name: "lang", | |||
Usage: "Sets post language to given ISO 639-1 language code", | |||
Value: "", | |||
}, | |||
cli.StringFlag{ | |||
Name: "user-agent", | |||
Usage: "Sets the User-Agent for API requests", | |||
Value: "", | |||
}, | |||
} |
@@ -1,77 +1,13 @@ | |||
package main | |||
import ( | |||
"bufio" | |||
"io" | |||
"log" | |||
"os" | |||
writeas "go.code.as/writeas.v2" | |||
writeascli "github.com/writeas/writeas-cli" | |||
cmd "github.com/writeas/writeas-cli/cmd" | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
// API constants for communicating with Write.as. | |||
const ( | |||
writeasBaseURL = "https://write.as" | |||
devBaseURL = "https://development.write.as" | |||
torBaseURL = "http://writeas7pm7rcdqg.onion" | |||
) | |||
// Application constants. | |||
const ( | |||
version = "1.99-dev" | |||
) | |||
// Defaults for posts on Write.as. | |||
const ( | |||
defaultFont = PostFontMono | |||
) | |||
// Available flags for creating posts | |||
var postFlags = []cli.Flag{ | |||
cli.StringFlag{ | |||
Name: "c, b", | |||
Usage: "Optional blog to post to", | |||
Value: "", | |||
}, | |||
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: "code", | |||
Usage: "Specifies this post is code", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "md", | |||
Usage: "Returns post URL with Markdown enabled", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "verbose, v", | |||
Usage: "Make the operation more talkative", | |||
}, | |||
cli.StringFlag{ | |||
Name: "font", | |||
Usage: "Sets post font to given value", | |||
Value: defaultFont, | |||
}, | |||
cli.StringFlag{ | |||
Name: "lang", | |||
Usage: "Sets post language to given ISO 639-1 language code", | |||
Value: "", | |||
}, | |||
cli.StringFlag{ | |||
Name: "user-agent", | |||
Usage: "Sets the User-Agent for API requests", | |||
Value: "", | |||
}, | |||
} | |||
func main() { | |||
initialize() | |||
@@ -83,7 +19,7 @@ func main() { | |||
// Run the app | |||
app := cli.NewApp() | |||
app.Name = "writeas" | |||
app.Version = version | |||
app.Version = writeascli.Version | |||
app.Usage = "Publish text quickly" | |||
app.Authors = []cli.Author{ | |||
{ | |||
@@ -91,14 +27,14 @@ func main() { | |||
Email: "hello@write.as", | |||
}, | |||
} | |||
app.Action = cmdPost | |||
app.Flags = postFlags | |||
app.Action = writeascli.CmdPost | |||
app.Flags = cmd.PostFlags | |||
app.Commands = []cli.Command{ | |||
{ | |||
Name: "post", | |||
Usage: "Alias for default action: create post from stdin", | |||
Action: cmdPost, | |||
Flags: postFlags, | |||
Action: writeascli.CmdPost, | |||
Flags: cmd.PostFlags, | |||
Description: `Create a new post on Write.as from stdin. | |||
Use the --code flag to indicate that the post should use syntax | |||
@@ -124,19 +60,19 @@ func main() { | |||
If posting fails for any reason, 'writeas' will show you the temporary file | |||
location and how to pipe it to 'writeas' to retry.`, | |||
Action: cmdNew, | |||
Flags: postFlags, | |||
Action: writeascli.CmdNew, | |||
Flags: cmd.PostFlags, | |||
}, | |||
{ | |||
Name: "publish", | |||
Usage: "Publish a file to Write.as", | |||
Action: cmdPublish, | |||
Flags: postFlags, | |||
Action: writeascli.CmdPublish, | |||
Flags: cmd.PostFlags, | |||
}, | |||
{ | |||
Name: "delete", | |||
Usage: "Delete a post", | |||
Action: cmdDelete, | |||
Action: writeascli.CmdDelete, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -156,7 +92,7 @@ func main() { | |||
{ | |||
Name: "update", | |||
Usage: "Update (overwrite) a post", | |||
Action: cmdUpdate, | |||
Action: writeascli.CmdUpdate, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -184,7 +120,7 @@ func main() { | |||
{ | |||
Name: "get", | |||
Usage: "Read a raw post", | |||
Action: cmdGet, | |||
Action: writeascli.CmdGet, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -209,12 +145,12 @@ func main() { | |||
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: cmdAdd, | |||
Action: writeascli.CmdAdd, | |||
}, | |||
{ | |||
Name: "list", | |||
Usage: "List local posts", | |||
Action: cmdList, | |||
Action: writeascli.CmdList, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "id", | |||
@@ -233,7 +169,7 @@ func main() { | |||
{ | |||
Name: "fetch", | |||
Usage: "Fetch authenticated user's Write.as posts", | |||
Action: cmdPull, | |||
Action: writeascli.CmdPull, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -253,7 +189,7 @@ func main() { | |||
{ | |||
Name: "auth", | |||
Usage: "Authenticate with Write.as", | |||
Action: cmdAuth, | |||
Action: writeascli.CmdAuth, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -273,7 +209,7 @@ func main() { | |||
{ | |||
Name: "logout", | |||
Usage: "Log out of Write.as", | |||
Action: cmdLogOut, | |||
Action: writeascli.CmdLogOut, | |||
Flags: []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "tor, t", | |||
@@ -311,50 +247,7 @@ OPTIONS: | |||
func initialize() { | |||
// Ensure we have a data directory to use | |||
if !dataDirExists() { | |||
createDataDir() | |||
} | |||
} | |||
func readStdIn() []byte { | |||
numBytes, numChunks := int64(0), int64(0) | |||
r := bufio.NewReader(os.Stdin) | |||
fullPost := []byte{} | |||
buf := make([]byte, 0, 1024) | |||
for { | |||
n, err := r.Read(buf[:cap(buf)]) | |||
buf = buf[:n] | |||
if n == 0 { | |||
if err == nil { | |||
continue | |||
} | |||
if err == io.EOF { | |||
break | |||
} | |||
log.Fatal(err) | |||
} | |||
numChunks++ | |||
numBytes += int64(len(buf)) | |||
fullPost = append(fullPost, buf...) | |||
if err != nil && err != io.EOF { | |||
log.Fatal(err) | |||
} | |||
if !writeascli.DataDirExists() { | |||
writeascli.CreateDataDir() | |||
} | |||
return fullPost | |||
} | |||
func handlePost(fullPost []byte, c *cli.Context) (*writeas.Post, error) { | |||
tor := isTor(c) | |||
if c.Int("tor-port") != 0 { | |||
torPort = c.Int("tor-port") | |||
} | |||
if tor { | |||
Info(c, "Posting to hidden service...") | |||
} else { | |||
Info(c, "Posting...") | |||
} | |||
return DoPost(c, fullPost, c.String("font"), false, tor, c.Bool("code")) | |||
} |
@@ -1,91 +0,0 @@ | |||
package main | |||
import ( | |||
"encoding/json" | |||
"io/ioutil" | |||
"path/filepath" | |||
"github.com/writeas/writeas-cli/fileutils" | |||
writeas "go.code.as/writeas.v2" | |||
ini "gopkg.in/ini.v1" | |||
) | |||
const ( | |||
userConfigFile = "config.ini" | |||
userFile = "user.json" | |||
) | |||
type ( | |||
APIConfig struct { | |||
} | |||
PostsConfig struct { | |||
Directory string `ini:"directory"` | |||
} | |||
UserConfig struct { | |||
API APIConfig `ini:"api"` | |||
Posts PostsConfig `ini:"posts"` | |||
} | |||
) | |||
func loadConfig() (*UserConfig, error) { | |||
// TODO: load config to var shared across app | |||
cfg, err := ini.LooseLoad(filepath.Join(userDataDir(), userConfigFile)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// Parse INI file | |||
uc := &UserConfig{} | |||
err = cfg.MapTo(uc) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return uc, nil | |||
} | |||
func saveConfig(uc *UserConfig) error { | |||
cfg := ini.Empty() | |||
err := ini.ReflectFrom(cfg, uc) | |||
if err != nil { | |||
return err | |||
} | |||
return cfg.SaveTo(filepath.Join(userDataDir(), userConfigFile)) | |||
} | |||
func loadUser() (*writeas.AuthUser, error) { | |||
fname := filepath.Join(userDataDir(), userFile) | |||
userJSON, err := ioutil.ReadFile(fname) | |||
if err != nil { | |||
if !fileutils.Exists(fname) { | |||
// Don't return a file-not-found error | |||
return nil, nil | |||
} | |||
return nil, err | |||
} | |||
// Parse JSON file | |||
u := &writeas.AuthUser{} | |||
err = json.Unmarshal(userJSON, u) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return u, nil | |||
} | |||
func saveUser(u *writeas.AuthUser) error { | |||
// Marshal struct into pretty-printed JSON | |||
userJSON, err := json.MarshalIndent(u, "", " ") | |||
if err != nil { | |||
return err | |||
} | |||
// Save file | |||
err = ioutil.WriteFile(filepath.Join(userDataDir(), userFile), userJSON, 0600) | |||
if err != nil { | |||
return err | |||
} | |||
return nil | |||
} |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" | |||
@@ -7,16 +7,17 @@ import ( | |||
"path/filepath" | |||
"github.com/howeyc/gopass" | |||
"github.com/writeas/writeas-cli/config" | |||
"github.com/writeas/writeas-cli/fileutils" | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
func cmdPost(c *cli.Context) error { | |||
func CmdPost(c *cli.Context) error { | |||
_, err := handlePost(readStdIn(), c) | |||
return err | |||
} | |||
func cmdNew(c *cli.Context) error { | |||
func CmdNew(c *cli.Context) error { | |||
fname, p := composeNewPost() | |||
if p == nil { | |||
// Assume composeNewPost already told us what the error was. Abort now. | |||
@@ -48,7 +49,7 @@ func cmdNew(c *cli.Context) error { | |||
return nil | |||
} | |||
func cmdPublish(c *cli.Context) error { | |||
func CmdPublish(c *cli.Context) error { | |||
filename := c.Args().Get(0) | |||
if filename == "" { | |||
return cli.NewExitError("usage: writeas publish <filename>", 1) | |||
@@ -63,7 +64,7 @@ func cmdPublish(c *cli.Context) error { | |||
} | |||
// Save post to posts folder | |||
cfg, err := loadConfig() | |||
cfg, err := config.LoadConfig(userDataDir()) | |||
if cfg.Posts.Directory != "" { | |||
err = WritePost(cfg.Posts.Directory, p) | |||
if err != nil { | |||
@@ -73,14 +74,14 @@ func cmdPublish(c *cli.Context) error { | |||
return nil | |||
} | |||
func cmdDelete(c *cli.Context) error { | |||
func CmdDelete(c *cli.Context) error { | |||
friendlyID := c.Args().Get(0) | |||
token := c.Args().Get(1) | |||
if friendlyID == "" { | |||
return cli.NewExitError("usage: writeas delete <postId> [<token>]", 1) | |||
} | |||
u, _ := loadUser() | |||
u, _ := LoadUser(userDataDir()) | |||
if token == "" { | |||
// Search for the token locally | |||
token = tokenFromID(friendlyID) | |||
@@ -108,7 +109,7 @@ func cmdDelete(c *cli.Context) error { | |||
} | |||
// Delete local file, if necessary | |||
cfg, err := loadConfig() | |||
cfg, err := config.LoadConfig(userDataDir()) | |||
if cfg.Posts.Directory != "" { | |||
// TODO: handle deleting blog posts | |||
err = fileutils.DeleteFile(filepath.Join(cfg.Posts.Directory, friendlyID+postFileExt)) | |||
@@ -120,14 +121,14 @@ func cmdDelete(c *cli.Context) error { | |||
return nil | |||
} | |||
func cmdUpdate(c *cli.Context) error { | |||
func CmdUpdate(c *cli.Context) error { | |||
friendlyID := c.Args().Get(0) | |||
token := c.Args().Get(1) | |||
if friendlyID == "" { | |||
return cli.NewExitError("usage: writeas update <postId> [<token>]", 1) | |||
} | |||
u, _ := loadUser() | |||
u, _ := LoadUser(userDataDir()) | |||
if token == "" { | |||
// Search for the token locally | |||
token = tokenFromID(friendlyID) | |||
@@ -155,7 +156,7 @@ func cmdUpdate(c *cli.Context) error { | |||
return DoUpdate(c, fullPost, friendlyID, token, c.String("font"), tor, c.Bool("code")) | |||
} | |||
func cmdGet(c *cli.Context) error { | |||
func CmdGet(c *cli.Context) error { | |||
friendlyID := c.Args().Get(0) | |||
if friendlyID == "" { | |||
return cli.NewExitError("usage: writeas get <postId>", 1) | |||
@@ -176,7 +177,7 @@ func cmdGet(c *cli.Context) error { | |||
return DoFetch(friendlyID, userAgent(c), tor) | |||
} | |||
func cmdAdd(c *cli.Context) error { | |||
func CmdAdd(c *cli.Context) error { | |||
friendlyID := c.Args().Get(0) | |||
token := c.Args().Get(1) | |||
if friendlyID == "" || token == "" { | |||
@@ -187,7 +188,7 @@ func cmdAdd(c *cli.Context) error { | |||
return err | |||
} | |||
func cmdList(c *cli.Context) error { | |||
func CmdList(c *cli.Context) error { | |||
urls := c.Bool("url") | |||
ids := c.Bool("id") | |||
@@ -199,9 +200,9 @@ func cmdList(c *cli.Context) error { | |||
fmt.Printf("%s ", p.ID) | |||
} | |||
if urls { | |||
base := writeasBaseURL | |||
if isDev() { | |||
base = devBaseURL | |||
base := WriteasBaseURL | |||
if IsDev() { | |||
base = DevBaseURL | |||
} | |||
ext := "" | |||
// Output URL in requested format | |||
@@ -215,9 +216,9 @@ func cmdList(c *cli.Context) error { | |||
return nil | |||
} | |||
func cmdAuth(c *cli.Context) error { | |||
func CmdAuth(c *cli.Context) error { | |||
// Check configuration | |||
u, err := loadUser() | |||
u, err := LoadUser(userDataDir()) | |||
if err != nil { | |||
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1) | |||
} | |||
@@ -249,6 +250,6 @@ func cmdAuth(c *cli.Context) error { | |||
return nil | |||
} | |||
func cmdLogOut(c *cli.Context) error { | |||
func CmdLogOut(c *cli.Context) error { | |||
return DoLogOut(c) | |||
} |
@@ -1,6 +1,6 @@ | |||
// +build !debug | |||
package main | |||
package writeascli | |||
const ( | |||
debug = false |
@@ -0,0 +1,51 @@ | |||
package config | |||
import ( | |||
"path/filepath" | |||
ini "gopkg.in/ini.v1" | |||
) | |||
const ( | |||
UserConfigFile = "config.ini" | |||
) | |||
type ( | |||
APIConfig struct { | |||
} | |||
PostsConfig struct { | |||
Directory string `ini:"directory"` | |||
} | |||
UserConfig struct { | |||
API APIConfig `ini:"api"` | |||
Posts PostsConfig `ini:"posts"` | |||
} | |||
) | |||
func LoadConfig(dataDir string) (*UserConfig, error) { | |||
// TODO: load config to var shared across app | |||
cfg, err := ini.LooseLoad(filepath.Join(dataDir, UserConfigFile)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// Parse INI file | |||
uc := &UserConfig{} | |||
err = cfg.MapTo(uc) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return uc, nil | |||
} | |||
func SaveConfig(dataDir string, uc *UserConfig) error { | |||
cfg := ini.Empty() | |||
err := ini.ReflectFrom(cfg, uc) | |||
if err != nil { | |||
return err | |||
} | |||
return cfg.SaveTo(filepath.Join(dataDir, UserConfigFile)) | |||
} |
@@ -1,6 +1,6 @@ | |||
// +build debug | |||
package main | |||
package writeascli | |||
const ( | |||
debug = true |
@@ -1,9 +1,9 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"os" | |||
) | |||
func isDev() bool { | |||
func IsDev() bool { | |||
return os.Getenv("WRITEAS_DEV") == "1" | |||
} |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"os" | |||
@@ -6,7 +6,7 @@ import ( | |||
var editors = []string{"WRITEAS_EDITOR", "EDITOR"} | |||
func getConfiguredEditor() string { | |||
func GetConfiguredEditor() string { | |||
for _, v := range editors { | |||
if e := os.Getenv(v); e != "" { | |||
return e |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"errors" |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" | |||
@@ -30,7 +30,7 @@ var postFontMap = map[string]postFont{ | |||
func getFont(code bool, font string) string { | |||
if code { | |||
if font != "" && font != defaultFont { | |||
if font != "" && font != DefaultFont { | |||
fmt.Printf("A non-default font '%s' and --code flag given. 'code' type takes precedence.\n", font) | |||
} | |||
return "code" | |||
@@ -41,6 +41,6 @@ func getFont(code bool, font string) string { | |||
return string(f) | |||
} | |||
fmt.Printf("Font '%s' invalid. Using default '%s'\n", font, defaultFont) | |||
return string(defaultFont) | |||
fmt.Printf("Font '%s' invalid. Using default '%s'\n", font, DefaultFont) | |||
return string(DefaultFont) | |||
} |
@@ -9,6 +9,8 @@ require ( | |||
github.com/jtolds/gls v4.2.1+incompatible // indirect | |||
github.com/microcosm-cc/bluemonday v1.0.1 // indirect | |||
github.com/mitchellh/go-homedir v1.0.0 | |||
github.com/onsi/ginkgo v1.8.0 // indirect | |||
github.com/onsi/gomega v1.5.0 // indirect | |||
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 | |||
@@ -4,16 +4,25 @@ github.com/atotto/clipboard v0.1.1 h1:WSoEbAS70E5gw8FbiqFlp69MGsB6dUb4l+0AGGLiVG | |||
github.com/atotto/clipboard v0.1.1/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= | |||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do= | |||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= | |||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | |||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0= | |||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= | |||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= | |||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | |||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | |||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44= | |||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= | |||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= | |||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | |||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | |||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= | |||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | |||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= | |||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | |||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY= | |||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | |||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | |||
@@ -30,11 +39,23 @@ go.code.as/writeas.v2 v0.0.0-20181216235156-68cbee8f4a5e h1:emU11ZqEW7s+6/Ty52t0 | |||
go.code.as/writeas.v2 v0.0.0-20181216235156-68cbee8f4a5e/go.mod h1:wH0YOXh4B2fcSJ/ihy+qru0XfCdGb4CPKaO0qS2g47k= | |||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= | |||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4= | |||
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= | |||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | |||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | |||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | |||
gopkg.in/ini.v1 v1.39.3 h1:+LGDwGPQXrK1zLmDY5GMdgX7uNvs4iS+9fIRAGaDBbg= | |||
gopkg.in/ini.v1 v1.39.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | |||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | |||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= | |||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= | |||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= | |||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" |
@@ -1,10 +1,21 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"github.com/cloudfoundry/jibber_jabber" | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
// Application constants. | |||
const ( | |||
Version = "1.99-dev" | |||
defaultUserAgent = "writeas-cli v" + Version | |||
// Defaults for posts on Write.as. | |||
DefaultFont = PostFontMono | |||
WriteasBaseURL = "https://write.as" | |||
DevBaseURL = "https://development.write.as" | |||
TorBaseURL = "http://writeas7pm7rcdqg.onion" | |||
) | |||
func userAgent(c *cli.Context) string { | |||
ua := c.String("user-agent") | |||
if ua == "" { |
@@ -1,14 +1,18 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"bufio" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"os" | |||
"path/filepath" | |||
"strings" | |||
"github.com/writeas/writeas-cli/fileutils" | |||
writeas "go.code.as/writeas.v2" | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
const ( | |||
@@ -26,11 +30,11 @@ func userDataDir() string { | |||
return filepath.Join(parentDataDir(), dataDirName) | |||
} | |||
func dataDirExists() bool { | |||
func DataDirExists() bool { | |||
return fileutils.Exists(userDataDir()) | |||
} | |||
func createDataDir() { | |||
func CreateDataDir() { | |||
err := os.Mkdir(userDataDir(), 0700) | |||
if err != nil { | |||
if debug { | |||
@@ -164,3 +168,46 @@ func WritePost(postsDir string, p *writeas.Post) error { | |||
} | |||
return ioutil.WriteFile(filepath.Join(postsDir, collDir, postFilename), []byte(txtFile), 0644) | |||
} | |||
func handlePost(fullPost []byte, c *cli.Context) (*writeas.Post, error) { | |||
tor := isTor(c) | |||
if c.Int("tor-port") != 0 { | |||
torPort = c.Int("tor-port") | |||
} | |||
if tor { | |||
Info(c, "Posting to hidden service...") | |||
} else { | |||
Info(c, "Posting...") | |||
} | |||
return DoPost(c, fullPost, c.String("font"), false, tor, c.Bool("code")) | |||
} | |||
func readStdIn() []byte { | |||
numBytes, numChunks := int64(0), int64(0) | |||
r := bufio.NewReader(os.Stdin) | |||
fullPost := []byte{} | |||
buf := make([]byte, 0, 1024) | |||
for { | |||
n, err := r.Read(buf[:cap(buf)]) | |||
buf = buf[:n] | |||
if n == 0 { | |||
if err == nil { | |||
continue | |||
} | |||
if err == io.EOF { | |||
break | |||
} | |||
log.Fatal(err) | |||
} | |||
numChunks++ | |||
numBytes += int64(len(buf)) | |||
fullPost = append(fullPost, buf...) | |||
if err != nil && err != io.EOF { | |||
log.Fatal(err) | |||
} | |||
} | |||
return fullPost | |||
} |
@@ -1,6 +1,6 @@ | |||
// +build !windows | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" | |||
@@ -24,7 +24,7 @@ func parentDataDir() string { | |||
} | |||
func editPostCmd(fname string) *exec.Cmd { | |||
editor := getConfiguredEditor() | |||
editor := GetConfiguredEditor() | |||
if editor == "" { | |||
// Fall back to default editor | |||
path, err := exec.LookPath("vim") |
@@ -1,6 +1,6 @@ | |||
// +build windows | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
//"github.com/writeas/writeas-cli/sync" | |||
@@ -7,6 +7,7 @@ import ( | |||
"os" | |||
"path/filepath" | |||
"github.com/writeas/writeas-cli/config" | |||
"github.com/writeas/writeas-cli/fileutils" | |||
cli "gopkg.in/urfave/cli.v1" | |||
) | |||
@@ -16,8 +17,8 @@ const ( | |||
userFilename = "writeas_user" | |||
) | |||
func cmdPull(c *cli.Context) error { | |||
cfg, err := loadConfig() | |||
func CmdPull(c *cli.Context) error { | |||
cfg, err := config.LoadConfig(userDataDir()) | |||
if err != nil { | |||
return err | |||
} | |||
@@ -78,10 +79,10 @@ func cmdPull(c *cli.Context) error { | |||
} | |||
// TODO: move UserConfig to its own package, and this to sync package | |||
func syncSetUp(cfg *UserConfig) error { | |||
func syncSetUp(cfg *config.UserConfig) error { | |||
// Get user information and fail early (before we make the user do | |||
// anything), if we're going to | |||
u, err := loadUser() | |||
u, err := LoadUser(userDataDir()) | |||
if err != nil { | |||
return err | |||
} | |||
@@ -117,7 +118,7 @@ func syncSetUp(cfg *UserConfig) error { | |||
// Save preference | |||
cfg.Posts.Directory = dir | |||
err = saveConfig(cfg) | |||
err = config.SaveConfig(userDataDir(), cfg) | |||
if err != nil { | |||
if debug { | |||
Errorln("Unable to save config: %s", err) |
@@ -1,4 +1,4 @@ | |||
package main | |||
package writeascli | |||
import ( | |||
"fmt" |
@@ -0,0 +1,47 @@ | |||
package writeascli | |||
import ( | |||
"encoding/json" | |||
"io/ioutil" | |||
"path/filepath" | |||
"github.com/writeas/writeas-cli/fileutils" | |||
"go.code.as/writeas.v2" | |||
) | |||
const UserFile = "user.json" | |||
func LoadUser(dataDir string) (*writeas.AuthUser, error) { | |||
fname := filepath.Join(dataDir, UserFile) | |||
userJSON, err := ioutil.ReadFile(fname) | |||
if err != nil { | |||
if !fileutils.Exists(fname) { | |||
// Don't return a file-not-found error | |||
return nil, nil | |||
} | |||
return nil, err | |||
} | |||
// Parse JSON file | |||
u := &writeas.AuthUser{} | |||
err = json.Unmarshal(userJSON, u) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return u, nil | |||
} | |||
func SaveUser(dataDir string, u *writeas.AuthUser) error { | |||
// Marshal struct into pretty-printed JSON | |||
userJSON, err := json.MarshalIndent(u, "", " ") | |||
if err != nil { | |||
return err | |||
} | |||
// Save file | |||
err = ioutil.WriteFile(filepath.Join(dataDir, UserFile), userJSON, 0600) | |||
if err != nil { | |||
return err | |||
} | |||
return nil | |||
} |