Page MenuHomeMusing Studio

No OneTemporary

diff --git a/api/api.go b/api/api.go
index d3fa3fc..b59debe 100644
--- a/api/api.go
+++ b/api/api.go
@@ -1,267 +1,255 @@
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"
)
-func client(userAgent string, tor bool) *writeas.Client {
- var client *writeas.Client
- if tor {
- client = writeas.NewTorClient(TorPort)
- } else {
- if config.IsDev() {
- client = writeas.NewDevClient()
- } else {
- client = writeas.NewClient()
- }
- }
- client.UserAgent = userAgent
-
- return client
-}
-
-func NewClient(c *cli.Context, authRequired bool) (*writeas.Client, error) {
+func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) {
var client *writeas.Client
if config.IsTor(c) {
- client = writeas.NewTorClient(TorPort)
+ client = writeas.NewTorClient(config.TorPort(c))
} else {
if config.IsDev() {
client = writeas.NewDevClient()
} else {
client = writeas.NewClient()
}
}
client.UserAgent = config.UserAgent(c)
// TODO: load user into var shared across the app
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if u != nil {
client.SetToken(u.AccessToken)
} else if authRequired {
return nil, fmt.Errorf("Not currently logged in. Authenticate with: writeas auth <username>")
}
return client, nil
}
// DoFetch retrieves the Write.as post with the given friendlyID,
// optionally via the Tor hidden service.
-func DoFetch(friendlyID, ua string, tor bool) error {
- cl := client(ua, tor)
+func DoFetch(c *cli.Context, friendlyID string) error {
+ cl, err := newClient(c, false)
+ if err != nil {
+ return err
+ }
p, err := cl.GetPost(friendlyID)
if err != nil {
return err
}
if p.Title != "" {
fmt.Printf("# %s\n\n", string(p.Title))
}
fmt.Printf("%s\n", string(p.Content))
return nil
}
// DoFetchPosts retrieves all remote posts for the
// authenticated user
func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) {
- cl, err := NewClient(c, true)
+ cl, err := newClient(c, true)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("Unable to create client: %v", err)
}
posts, err := cl.GetUserPosts()
if err != nil {
return nil, err
}
return *posts, nil
}
// DoPost creates a Write.as post, returning an error if it was
// unsuccessful.
-func DoPost(c *cli.Context, post []byte, font string, encrypt, tor, code bool) (*writeas.Post, error) {
- cl, _ := NewClient(c, false)
+func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writeas.Post, error) {
+ cl, err := newClient(c, false)
+ if err != nil {
+ return nil, fmt.Errorf("Unable to create client: %v", err)
+ }
pp := &writeas.PostParams{
Font: config.GetFont(code, font),
Collection: config.Collection(c),
}
pp.Title, pp.Content = posts.ExtractTitle(string(post))
if lang := config.Language(c, true); lang != "" {
pp.Language = &lang
}
p, err := cl.CreatePost(pp)
if err != nil {
return nil, fmt.Errorf("Unable to post: %v", err)
}
var url string
if p.Collection != nil {
url = p.Collection.URL + p.Slug
} else {
- if tor {
+ if config.IsTor(c) {
url = config.TorBaseURL
} else if config.IsDev() {
url = config.DevBaseURL
} else {
url = config.WriteasBaseURL
}
url += "/" + p.ID
// Output URL in requested format
if c.Bool("md") {
url += ".md"
}
}
if cl.Token() == "" {
// Store post locally, since we're not authenticated
AddPost(c, p.ID, p.Token)
}
// Copy URL to clipboard
err = clipboard.WriteAll(string(url))
if err != nil {
log.Errorln("writeas: Didn't copy to clipboard: %s", err)
} else {
log.Info(c, "Copied to clipboard.")
}
// Output URL
fmt.Printf("%s\n", url)
return p, nil
}
// DoFetchCollections retrieves a list of the currently logged in users
// collections.
func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) {
- cl, err := NewClient(c, true)
+ cl, err := newClient(c, true)
if err != nil {
if config.Debug() {
- log.ErrorlnQuit("could not create new client: %v", err)
+ log.ErrorlnQuit("could not create client: %v", err)
}
return nil, fmt.Errorf("Couldn't create new client")
}
colls, err := cl.GetUserCollections()
if err != nil {
if config.Debug() {
log.ErrorlnQuit("failed fetching user collections: %v", err)
}
- return nil, fmt.Errorf("Couldn't get user collections")
+ return nil, fmt.Errorf("Couldn't get user blogs")
}
out := make([]RemoteColl, len(*colls))
for i, c := range *colls {
coll := RemoteColl{
Alias: c.Alias,
Title: c.Title,
URL: c.URL,
}
out[i] = coll
}
return out, nil
}
// DoUpdate updates the given post on Write.as.
-func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, tor, code bool) error {
- cl, _ := NewClient(c, false)
+func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code bool) error {
+ cl, err := newClient(c, false)
+ if err != nil {
+ return fmt.Errorf("Unable to create client: %v", err)
+ }
params := writeas.PostParams{}
params.Title, params.Content = posts.ExtractTitle(string(post))
if lang := config.Language(c, false); lang != "" {
params.Language = &lang
}
if code || font != "" {
params.Font = config.GetFont(code, font)
}
- _, err := cl.UpdatePost(friendlyID, token, &params)
+ _, err = cl.UpdatePost(friendlyID, token, &params)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem updating: %v", err)
}
return fmt.Errorf("Post doesn't exist, or bad edit token given.")
}
-
- if tor {
- log.Info(c, "Post updated via hidden service.")
- } else {
- log.Info(c, "Post updated.")
- }
return nil
}
// DoDelete deletes the given post on Write.as, and removes any local references
-func DoDelete(c *cli.Context, friendlyID, token string, tor bool) error {
- cl, _ := NewClient(c, false)
+func DoDelete(c *cli.Context, friendlyID, token string) error {
+ cl, err := newClient(c, false)
+ if err != nil {
+ return fmt.Errorf("Unable to create client: %v", err)
+ }
- err := cl.DeletePost(friendlyID, token)
+ err = cl.DeletePost(friendlyID, token)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem deleting: %v", err)
}
return fmt.Errorf("Post doesn't exist, or bad edit token given.")
}
- if tor {
- log.Info(c, "Post deleted from hidden service.")
- } else {
- log.Info(c, "Post deleted.")
- }
RemovePost(c.App.ExtraInfo()["configDir"], friendlyID)
return nil
}
func DoLogIn(c *cli.Context, username, password string) error {
- cl := client(config.UserAgent(c), config.IsTor(c))
+ cl, err := newClient(c, false)
+ if err != nil {
+ return fmt.Errorf("Unable to create client: %v", err)
+ }
u, err := cl.LogIn(username, password)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem logging in: %v", err)
}
return err
}
err = config.SaveUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]), u)
if err != nil {
return err
}
log.Info(c, "Logged in as %s.\n", u.User.Username)
return nil
}
func DoLogOut(c *cli.Context) error {
- cl, err := NewClient(c, true)
+ cl, err := newClient(c, true)
if err != nil {
- return err
+ return fmt.Errorf("Unable to create client: %v", err)
}
err = cl.LogOut()
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem logging out: %v", err)
}
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
}
diff --git a/api/posts.go b/api/posts.go
index 379a2c6..6ab1647 100644
--- a/api/posts.go
+++ b/api/posts.go
@@ -1,304 +1,290 @@
package api
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
writeas "github.com/writeas/go-writeas/v2"
"github.com/writeas/writeas-cli/config"
"github.com/writeas/writeas-cli/fileutils"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
const (
postsFile = "posts.psv"
separator = `|`
)
// Post holds the basic authentication information for a Write.as post.
type Post struct {
ID string
EditToken string
}
// RemotePost holds addition information about published
// posts
type RemotePost struct {
Post
Title,
Excerpt,
Slug,
Collection,
EditToken string
Synced bool
Updated time.Time
}
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)
if err != nil {
return fmt.Errorf("Error creating local posts list: %s", err)
}
defer f.Close()
l := fmt.Sprintf("%s%s%s\n", id, separator, token)
if _, err = f.WriteString(l); err != nil {
return fmt.Errorf("Error writing to local posts list: %s", err)
}
return nil
}
// 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, true)
if err != nil {
return nil, err
}
postsToClaim := make([]writeas.OwnedPostParams, len(*localPosts))
for i, post := range *localPosts {
postsToClaim[i] = writeas.OwnedPostParams{
ID: post.ID,
Token: post.EditToken,
}
}
return cl.ClaimPosts(&postsToClaim)
}
func TokenFromID(c *cli.Context, id string) string {
post := fileutils.FindLine(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile), id)
if post == "" {
return ""
}
parts := strings.Split(post, separator)
if len(parts) < 2 {
return ""
}
return parts[1]
}
func RemovePost(path, id string) {
fileutils.RemoveLine(filepath.Join(config.UserDataDir(path), postsFile), id)
}
func GetPosts(c *cli.Context) *[]Post {
lines := fileutils.ReadData(filepath.Join(config.UserDataDir(c.App.ExtraInfo()["configDir"]), postsFile))
posts := []Post{}
if lines != nil && len(*lines) > 0 {
parts := make([]string, 2)
for _, l := range *lines {
parts = strings.Split(l, separator)
if len(parts) < 2 {
continue
}
posts = append(posts, Post{ID: parts[0], EditToken: parts[1]})
}
}
return &posts
}
func GetUserPosts(c *cli.Context) ([]RemotePost, error) {
waposts, err := DoFetchPosts(c)
if err != nil {
return nil, err
}
if len(waposts) == 0 {
return nil, nil
}
posts := []RemotePost{}
for _, p := range waposts {
post := RemotePost{
Post: Post{
ID: p.ID,
EditToken: p.Token,
},
Title: p.Title,
Excerpt: getExcerpt(p.Content),
Slug: p.Slug,
Synced: p.Slug != "",
Updated: p.Updated,
}
if p.Collection != nil {
post.Collection = p.Collection.Alias
}
posts = append(posts, post)
}
return posts, nil
}
// getExcerpt takes in a content string and returns
// a concatenated version. limited to no more than
// two lines of 80 chars each. delimited by '...'
func getExcerpt(input string) string {
length := len(input)
if length <= 80 {
return input
} else if length < 160 {
ln1, idx := trimToLength(input, 80)
if idx == -1 {
idx = 80
}
ln2, _ := trimToLength(input[idx:], 80)
return ln1 + "\n" + ln2
} else {
excerpt := input[:158]
ln1, idx := trimToLength(excerpt, 80)
if idx == -1 {
idx = 80
}
ln2, _ := trimToLength(excerpt[idx:], 80)
return ln1 + "\n" + ln2 + "..."
}
}
func trimToLength(in string, l int) (string, int) {
c := []rune(in)
spaceIdx := -1
length := len(c)
if length <= l {
return in, spaceIdx
}
for i := l; i > 0; i-- {
if c[i] == ' ' {
spaceIdx = i
break
}
}
if spaceIdx > -1 {
c = c[:spaceIdx]
}
return string(c), spaceIdx
}
func ComposeNewPost() (string, *[]byte) {
f, err := fileutils.TempFile(os.TempDir(), "WApost", "txt")
if err != nil {
if config.Debug() {
panic(err)
} else {
log.Errorln("Error creating temp file: %s", err)
return "", nil
}
}
f.Close()
cmd := config.EditPostCmd(f.Name())
if cmd == nil {
os.Remove(f.Name())
fmt.Println(config.NoEditorErr)
return "", nil
}
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
if err := cmd.Start(); err != nil {
os.Remove(f.Name())
if config.Debug() {
panic(err)
} else {
log.Errorln("Error starting editor: %s", err)
return "", nil
}
}
// If something fails past this point, the temporary post file won't be
// removed automatically. Calling function should handle this.
if err := cmd.Wait(); err != nil {
if config.Debug() {
panic(err)
} else {
log.Errorln("Editor finished with error: %s", err)
return "", nil
}
}
post, err := ioutil.ReadFile(f.Name())
if err != nil {
if config.Debug() {
panic(err)
} else {
log.Errorln("Error reading post: %s", err)
return "", nil
}
}
return f.Name(), &post
}
func WritePost(postsDir string, p *writeas.Post) error {
postFilename := p.ID
collDir := ""
if p.Collection != nil {
postFilename = p.Slug
collDir = p.Collection.Alias
}
postFilename += PostFileExt
txtFile := p.Content
if p.Title != "" {
txtFile = "# " + p.Title + "\n\n" + txtFile
}
return ioutil.WriteFile(filepath.Join(postsDir, collDir, postFilename), []byte(txtFile), 0644)
}
-func HandlePost(fullPost []byte, c *cli.Context) (*writeas.Post, error) {
- tor := config.IsTor(c)
- if c.Int("tor-port") != 0 {
- TorPort = c.Int("tor-port")
- }
- if tor {
- log.Info(c, "Posting to hidden service...")
- } else {
- log.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.ErrorlnQuit("Error reading from stdin: %v", err)
}
numChunks++
numBytes += int64(len(buf))
fullPost = append(fullPost, buf...)
if err != nil && err != io.EOF {
log.ErrorlnQuit("Error appending to end of post: %v", err)
}
}
return fullPost
}
diff --git a/api/sync.go b/api/sync.go
index d687f24..b093be9 100644
--- a/api/sync.go
+++ b/api/sync.go
@@ -1,131 +1,131 @@
package api
import (
//"github.com/writeas/writeas-cli/sync"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/writeas/writeas-cli/config"
"github.com/writeas/writeas-cli/fileutils"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
const (
PostFileExt = ".txt"
userFilename = "writeas_user"
)
func CmdPull(c *cli.Context) error {
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return err
}
// Create posts directory if needed
if cfg.Posts.Directory == "" {
syncSetUp(c.App.ExtraInfo()["configDir"], cfg)
}
- // Fetch posts
- cl, err := NewClient(c, true)
+ cl, err := newClient(c, true)
if err != nil {
return err
}
+ // Fetch posts
posts, err := cl.GetUserPosts()
if err != nil {
return err
}
for _, p := range *posts {
postFilename := p.ID
collDir := ""
if p.Collection != nil {
postFilename = p.Slug
// Create directory for collection
collDir = p.Collection.Alias
if !fileutils.Exists(filepath.Join(cfg.Posts.Directory, collDir)) {
log.Info(c, "Creating folder "+collDir)
err = os.Mkdir(filepath.Join(cfg.Posts.Directory, collDir), 0755)
if err != nil {
log.Errorln("Error creating blog directory %s: %s. Skipping post %s.", collDir, err, postFilename)
continue
}
}
}
postFilename += PostFileExt
// Write file
txtFile := p.Content
if p.Title != "" {
txtFile = "# " + p.Title + "\n\n" + txtFile
}
err = ioutil.WriteFile(filepath.Join(cfg.Posts.Directory, collDir, postFilename), []byte(txtFile), 0644)
if err != nil {
log.Errorln("Error creating file %s: %s", postFilename, err)
}
log.Info(c, "Saved post "+postFilename)
// Update mtime and atime on files
modTime := p.Updated.Local()
err = os.Chtimes(filepath.Join(cfg.Posts.Directory, collDir, postFilename), modTime, modTime)
if err != nil {
log.Errorln("Error setting time on %s: %s", postFilename, err)
}
}
return nil
}
func syncSetUp(path string, 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))
if err != nil {
return err
}
// Prompt for posts directory
defaultDir, err := os.Getwd()
if err != nil {
return err
}
var dir string
fmt.Printf("Posts directory? [%s]: ", defaultDir)
fmt.Scanln(&dir)
if dir == "" {
dir = defaultDir
}
// FIXME: This only works on non-Windows OSes (fix: https://www.reddit.com/r/golang/comments/5t3ezd/hidden_files_directories/)
userFilepath := filepath.Join(dir, "."+userFilename)
// Create directory if needed
if !fileutils.Exists(dir) {
err = os.MkdirAll(dir, 0700)
if err != nil {
if config.Debug() {
log.Errorln("Error creating data directory: %s", err)
}
return err
}
// Create username file in directory
err = ioutil.WriteFile(userFilepath, []byte(u.User.Username), 0644)
fmt.Println("Created posts directory.")
}
// Save preference
cfg.Posts.Directory = dir
err = config.SaveConfig(config.UserDataDir(path), cfg)
if err != nil {
if config.Debug() {
log.Errorln("Unable to save config: %s", err)
}
return err
}
fmt.Println("Saved config.")
return nil
}
diff --git a/api/tor.go b/api/tor.go
deleted file mode 100644
index dae5be5..0000000
--- a/api/tor.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package api
-
-import (
- "fmt"
- "net/http"
-
- "code.as/core/socks"
-)
-
-var (
- TorPort = 9150
-)
-
-func torClient() *http.Client {
- dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, fmt.Sprintf("127.0.0.1:%d", TorPort))
- transport := &http.Transport{Dial: dialSocksProxy}
- return &http.Client{Transport: transport}
-}
diff --git a/commands/commands.go b/commands/commands.go
index 8627411..3f06507 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -1,326 +1,352 @@
package commands
import (
"fmt"
"io/ioutil"
"os"
"text/tabwriter"
"github.com/howeyc/gopass"
"github.com/writeas/writeas-cli/api"
"github.com/writeas/writeas-cli/config"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
func CmdPost(c *cli.Context) error {
- _, err := api.HandlePost(api.ReadStdIn(), c)
+ _, err := api.DoPost(c, api.ReadStdIn(), c.String("font"), false, c.Bool("code"))
+ if config.IsTor(c) {
+ log.Info(c, "Posted to hidden service...")
+ } else {
+ log.Info(c, "Posted...")
+ }
+
return err
}
func CmdNew(c *cli.Context) error {
fname, p := api.ComposeNewPost()
if p == nil {
// Assume composeNewPost already told us what the error was. Abort now.
os.Exit(1)
}
// Ensure we have something to post
if len(*p) == 0 {
// Clean up temporary post
if fname != "" {
os.Remove(fname)
}
log.InfolnQuit("Empty post. Bye!")
}
- _, err := api.HandlePost(*p, c)
+ if config.IsTor(c) {
+ log.Info(c, "Posting to hidden service...")
+ } else {
+ log.Info(c, "Posting...")
+ }
+
+ _, err := api.DoPost(c, *p, c.String("font"), false, c.Bool("code"))
if err != nil {
log.Errorln("Error posting: %s\n%s", err, config.MessageRetryCompose(fname))
return cli.NewExitError("", 1)
}
// Clean up temporary post
if fname != "" {
os.Remove(fname)
}
return nil
}
func CmdPublish(c *cli.Context) error {
filename := c.Args().Get(0)
if filename == "" {
return cli.NewExitError("usage: writeas publish <filename>", 1)
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
- _, err = api.HandlePost(content, c)
+
+ if config.IsTor(c) {
+ log.Info(c, "Publishing to hidden service...")
+ } else {
+ log.Info(c, "Publishing...")
+ }
+ _, err = api.DoPost(c, content, c.String("font"), false, c.Bool("code"))
// TODO: write local file if directory is set
return err
}
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, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
if token == "" && u == nil {
log.Errorln("Couldn't find an edit token locally. Did you create this post here?")
log.ErrorlnQuit("If you have an edit token, use: writeas delete %s <token>", friendlyID)
}
}
- tor := config.IsTor(c)
- if c.Int("tor-port") != 0 {
- api.TorPort = c.Int("tor-port")
- }
- if tor {
+ if config.IsTor(c) {
log.Info(c, "Deleting via hidden service...")
} else {
log.Info(c, "Deleting...")
}
- err := api.DoDelete(c, friendlyID, token, tor)
+ err := api.DoDelete(c, friendlyID, token)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Couldn't delete remote copy: %v", err), 1)
}
// TODO: Delete local file, if necessary
return nil
}
func CmdUpdate(c *cli.Context) error {
friendlyID := c.Args().Get(0)
token := c.Args().Get(1)
if friendlyID == "" {
return cli.NewExitError("usage: writeas update <postId> [<token>]", 1)
}
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
if token == "" && u == nil {
log.Errorln("Couldn't find an edit token locally. Did you create this post here?")
log.ErrorlnQuit("If you have an edit token, use: writeas update %s <token>", friendlyID)
}
}
// Read post body
fullPost := api.ReadStdIn()
- tor := config.IsTor(c)
- if c.Int("tor-port") != 0 {
- api.TorPort = c.Int("tor-port")
- }
- if tor {
+ if config.IsTor(c) {
log.Info(c, "Updating via hidden service...")
} else {
log.Info(c, "Updating...")
}
- return api.DoUpdate(c, fullPost, friendlyID, token, c.String("font"), tor, c.Bool("code"))
+ return api.DoUpdate(c, fullPost, friendlyID, token, c.String("font"), c.Bool("code"))
}
func CmdGet(c *cli.Context) error {
friendlyID := c.Args().Get(0)
if friendlyID == "" {
return cli.NewExitError("usage: writeas get <postId>", 1)
}
- tor := config.IsTor(c)
- if c.Int("tor-port") != 0 {
- api.TorPort = c.Int("tor-port")
- }
- if tor {
+ if config.IsTor(c) {
log.Info(c, "Getting via hidden service...")
} else {
log.Info(c, "Getting...")
}
- return api.DoFetch(friendlyID, config.UserAgent(c), tor)
+ return api.DoFetch(c, friendlyID)
}
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 <postId> <token>", 1)
}
err := api.AddPost(c, friendlyID, token)
return err
}
func CmdListPosts(c *cli.Context) error {
urls := c.Bool("url")
ids := c.Bool("id")
details := c.Bool("v")
posts := api.GetPosts(c)
if details {
var p api.Post
tw := tabwriter.NewWriter(os.Stdout, 10, 0, 2, ' ', tabwriter.TabIndent)
numPosts := len(*posts)
if ids || !urls && numPosts != 0 {
fmt.Fprintf(tw, "%s\t%s\t\n", "ID", "Token")
} else if numPosts != 0 {
fmt.Fprintf(tw, "%s\t%s\t\n", "URL", "Token")
} else {
fmt.Fprintf(tw, "No local posts found\n")
}
for i := range *posts {
p = (*posts)[numPosts-1-i]
if ids || !urls {
fmt.Fprintf(tw, "%s\t%s\t\n", p.ID, p.EditToken)
} else {
fmt.Fprintf(tw, "%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken)
}
}
return tw.Flush()
}
for _, p := range *posts {
if ids || !urls {
fmt.Printf("%s\n", p.ID)
} else {
fmt.Printf("%s\n", getPostURL(c, p.ID))
}
}
return nil
}
func getPostURL(c *cli.Context, slug string) string {
base := config.WriteasBaseURL
if config.IsDev() {
base = config.DevBaseURL
}
ext := ""
// Output URL in requested format
if c.Bool("md") {
ext = ".md"
}
return fmt.Sprintf("%s/%s%s", base, slug, ext)
}
func CmdCollections(c *cli.Context) error {
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u == nil {
return cli.NewExitError("You must be authenticated to view collections.\nLog in first with: writeas auth <username>", 1)
}
+ if config.IsTor(c) {
+ log.Info(c, "Getting blogs via hidden service...")
+ } else {
+ log.Info(c, "Getting blogs...")
+ }
colls, err := api.DoFetchCollections(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Couldn't get collections for user %s: %v", u.User.Username, err), 1)
}
urls := c.Bool("url")
tw := tabwriter.NewWriter(os.Stdout, 8, 0, 2, ' ', tabwriter.TabIndent)
detail := "Title"
if urls {
detail = "URL"
}
fmt.Fprintf(tw, "%s\t%s\t\n", "Alias", detail)
for _, c := range colls {
dData := c.Title
if urls {
dData = c.URL
}
fmt.Fprintf(tw, "%s\t%s\t\n", c.Alias, dData)
}
tw.Flush()
return nil
}
func CmdClaim(c *cli.Context) error {
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u == nil {
return cli.NewExitError("You must be authenticated to claim local posts.\nLog in first with: writeas auth <username>", 1)
}
localPosts := api.GetPosts(c)
if len(*localPosts) == 0 {
return nil
}
log.Info(c, "Claiming %d post(s) for %s...", len(*localPosts), u.User.Username)
+ if config.IsTor(c) {
+ log.Info(c, "...via hidden service...")
+ }
+
results, err := api.ClaimPosts(c, localPosts)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Failed to claim posts: %v", err), 1)
}
var okCount, errCount int
for _, r := range *results {
id := r.ID
if id == "" {
// No top-level ID, so the claim was successful
id = r.Post.ID
}
status := fmt.Sprintf("Post %s...", id)
if r.ErrorMessage != "" {
log.Errorln("%serror: %v", status, r.ErrorMessage)
errCount++
} else {
log.Info(c, "%sOK", status)
okCount++
// only delete local if successful
api.RemovePost(c.App.ExtraInfo()["configDir"], id)
}
}
log.Info(c, "%d claimed, %d failed", okCount, errCount)
return nil
}
func CmdAuth(c *cli.Context) error {
// Check configuration
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u != nil && u.AccessToken != "" {
return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: writeas logout", 1)
}
// Validate arguments and get password
username := c.Args().Get(0)
if username == "" {
return cli.NewExitError("usage: writeas auth <username>", 1)
}
fmt.Print("Password: ")
pass, err := gopass.GetPasswdMasked()
if err != nil {
return cli.NewExitError(fmt.Sprintf("error reading password: %v", err), 1)
}
// Validate password
if len(pass) == 0 {
return cli.NewExitError("Please enter your password.", 1)
}
+
+ if config.IsTor(c) {
+ log.Info(c, "Logging in to hidden service...")
+ } else {
+ log.Info(c, "Logging in...")
+ }
err = api.DoLogIn(c, username, string(pass))
if err != nil {
return cli.NewExitError(fmt.Sprintf("error logging in: %v", err), 1)
}
return nil
}
func CmdLogOut(c *cli.Context) error {
+ if config.IsTor(c) {
+ log.Info(c, "Logging out of hidden service...")
+ } else {
+ log.Info(c, "Logging out...")
+ }
return api.DoLogOut(c)
}
diff --git a/config/options.go b/config/options.go
index 1abf9ee..c3bffb5 100644
--- a/config/options.go
+++ b/config/options.go
@@ -1,56 +1,64 @@
package config
import (
"github.com/cloudfoundry/jibber_jabber"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
// Application constants.
const (
Version = "2.0"
defaultUserAgent = "writeas-cli v" + Version
// Defaults for posts on Write.as.
DefaultFont = PostFontMono
WriteasBaseURL = "https://write.as"
DevBaseURL = "https://development.write.as"
TorBaseURL = "http://writeas7pm7rcdqg.onion"
+ torPort = 9150
)
func UserAgent(c *cli.Context) string {
ua := c.String("user-agent")
if ua == "" {
return defaultUserAgent
}
return ua + " (" + defaultUserAgent + ")"
}
func IsTor(c *cli.Context) bool {
return c.Bool("tor") || c.Bool("t")
}
+func TorPort(c *cli.Context) int {
+ if c.IsSet("tor-port") && c.Int("tor-port") != 0 {
+ return c.Int("tor-port")
+ }
+ return torPort
+}
+
func Language(c *cli.Context, auto bool) string {
if l := c.String("lang"); l != "" {
return l
}
if !auto {
return ""
}
// Automatically detect language
l, err := jibber_jabber.DetectLanguage()
if err != nil {
log.Info(c, "Language detection failed: %s", err)
return ""
}
return l
}
func Collection(c *cli.Context) string {
if coll := c.String("c"); coll != "" {
return coll
}
if coll := c.String("b"); coll != "" {
return coll
}
return ""
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 31, 9:50 AM (4 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3145708

Event Timeline