Page Menu
Home
Musing Studio
Search
Configure Global Search
Log In
Files
F10670020
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
View Options
diff --git a/cli.go b/cli.go
index 8e98fee..937afaf 100644
--- a/cli.go
+++ b/cli.go
@@ -1,269 +1,289 @@
package main
import (
"bufio"
"bytes"
"fmt"
"github.com/codegangsta/cli"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strconv"
+ "strings"
)
const (
apiUrl = "http://i.write.as"
hiddenApiUrl = "http://writeas7pm7rcdqg.onion"
readApiUrl = "http://i.write.as"
VERSION = "0.1.0"
)
func main() {
+ initialize()
+
+ // Run the app
app := cli.NewApp()
app.Name = "writeas"
app.Version = VERSION
app.Usage = "Simple text pasting and publishing"
app.Authors = []cli.Author{
{
Name: "Write.as",
Email: "hello@write.as",
},
}
app.Action = cmdPost
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "tor, t",
Usage: "Perform action on Tor hidden service",
},
}
app.Commands = []cli.Command{
{
Name: "post",
Usage: "Alias for default action: create post from stdin",
Action: cmdPost,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "tor, t",
Usage: "Post to Tor hidden service",
},
},
},
{
Name: "delete",
Usage: "Delete a post",
Action: cmdDelete,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "tor, t",
Usage: "Delete from Tor hidden service",
},
},
},
{
Name: "get",
Usage: "Read a raw post",
Action: cmdGet,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "tor, t",
Usage: "Get from Tor hidden service",
},
},
},
}
app.Run(os.Args)
}
+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)
}
}
return fullPost
}
func check(err error) {
if err != nil {
fmt.Printf("%s\n", err)
os.Exit(1)
}
}
func getPass() []byte {
// TODO: don't show passphrase in the terminal
var p string
_, err := fmt.Scanln(&p)
check(err)
return []byte(p)
}
func cmdPost(c *cli.Context) {
fullPost := readStdIn()
tor := c.Bool("tor") || c.Bool("t")
if tor {
fmt.Println("Posting to hidden service...")
} else {
fmt.Println("Posting...")
}
DoPost(fullPost, false, tor)
}
func cmdDelete(c *cli.Context) {
friendlyId := c.Args().Get(0)
token := c.Args().Get(1)
if friendlyId == "" || token == "" {
fmt.Println("usage: writeas delete <postId> <token>")
os.Exit(1)
}
tor := c.Bool("tor") || c.Bool("t")
DoDelete(friendlyId, token, tor)
}
func cmdGet(c *cli.Context) {
friendlyId := c.Args().Get(0)
if friendlyId == "" {
fmt.Println("usage: writeas get <postId>")
os.Exit(1)
}
tor := c.Bool("tor") || c.Bool("t")
DoFetch(friendlyId, tor)
}
func client(read, tor bool, path, query string) (string, *http.Client) {
var u *url.URL
var client *http.Client
if tor {
u, _ = url.ParseRequestURI(hiddenApiUrl)
if read {
u.Path = "/" + path
} else {
u.Path = "/api"
}
client = torClient()
} else {
if read {
u, _ = url.ParseRequestURI(readApiUrl)
} else {
u, _ = url.ParseRequestURI(apiUrl)
}
u.Path = "/" + path
client = &http.Client{}
}
if query != "" {
u.RawQuery = query
}
urlStr := fmt.Sprintf("%v", u)
return urlStr, client
}
func DoFetch(friendlyId string, tor bool) {
path := friendlyId
if len(friendlyId) == 12 {
// Original (pre-alpha) plain text URLs
path = friendlyId
} else if len(friendlyId) == 13 {
// Alpha phase HTML-based URLs
path = friendlyId + ".txt"
} else {
// Fallback path. Plan is to always support .txt file for raw files
path = friendlyId + ".txt"
}
urlStr, client := client(true, tor, path, "")
r, _ := http.NewRequest("GET", urlStr, nil)
r.Header.Add("User-Agent", "writeas-cli v"+VERSION)
resp, err := client.Do(r)
check(err)
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
content, err := ioutil.ReadAll(resp.Body)
check(err)
fmt.Printf("%s\n", string(content))
} else if resp.StatusCode == http.StatusNotFound {
fmt.Printf("Post not found.\n")
} else {
fmt.Printf("Problem getting post: %s\n", resp.Status)
}
}
func DoPost(post []byte, encrypt, tor bool) {
data := url.Values{}
data.Set("w", string(post))
if encrypt {
data.Add("e", "")
}
urlStr, client := client(false, tor, "", "")
r, _ := http.NewRequest("POST", urlStr, bytes.NewBufferString(data.Encode()))
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
resp, err := client.Do(r)
check(err)
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
content, err := ioutil.ReadAll(resp.Body)
check(err)
- fmt.Printf("%s\n", string(content))
+
+ nlPos := strings.Index(string(content), "\n")
+ url := content[:nlPos]
+ idPos := strings.LastIndex(string(url), "/") + 1
+ id := string(url[idPos:])
+ token := string(content[nlPos+1:])
+
+ addPost(id, token)
+
+ fmt.Printf("%s\n", url)
} else {
fmt.Printf("Unable to post: %s\n", resp.Status)
}
}
func DoDelete(friendlyId, token string, tor bool) {
urlStr, client := client(false, tor, "", fmt.Sprintf("id=%s&t=%s", friendlyId, token))
r, _ := http.NewRequest("DELETE", urlStr, nil)
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(r)
check(err)
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
if tor {
fmt.Println("Post deleted from hidden service.")
} else {
fmt.Println("Post deleted.")
}
} else {
fmt.Printf("Problem deleting: %s\n", resp.Status)
}
}
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..51e3a19
--- /dev/null
+++ b/config.go
@@ -0,0 +1,7 @@
+// +build !debug
+
+package main
+
+const (
+ DEBUG = false
+)
diff --git a/config_debug.go b/config_debug.go
new file mode 100644
index 0000000..81353ef
--- /dev/null
+++ b/config_debug.go
@@ -0,0 +1,7 @@
+// +build debug
+
+package main
+
+const (
+ DEBUG = true
+)
diff --git a/posts.go b/posts.go
new file mode 100644
index 0000000..75a08ed
--- /dev/null
+++ b/posts.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "fmt"
+ "github.com/writeas/writeas-cli/utils"
+ "os"
+ "path/filepath"
+)
+
+const (
+ POSTS_FILE = "posts.psv"
+ SEPARATOR = `|`
+)
+
+func userDataDir() string {
+ return filepath.Join(parentDataDir(), DATA_DIR_NAME)
+}
+
+func dataDirExists() bool {
+ return fileutils.Exists(userDataDir())
+}
+
+func createDataDir() {
+ err := os.Mkdir(userDataDir(), 0700)
+ if err != nil && DEBUG {
+ panic(err)
+ }
+}
+
+func addPost(id, token string) {
+ f, err := os.OpenFile(filepath.Join(userDataDir(), POSTS_FILE), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
+ if err != nil {
+ if DEBUG {
+ panic(err)
+ } else {
+ return
+ }
+ }
+ defer f.Close()
+
+ l := fmt.Sprintf("%s%s%s\n", id, SEPARATOR, token)
+
+ if _, err = f.WriteString(l); err != nil && DEBUG {
+ panic(err)
+ }
+}
diff --git a/posts_nix.go b/posts_nix.go
new file mode 100644
index 0000000..fb1fabc
--- /dev/null
+++ b/posts_nix.go
@@ -0,0 +1,18 @@
+// +build !windows
+
+package main
+
+import (
+ "github.com/mitchellh/go-homedir"
+)
+
+const DATA_DIR_NAME = ".writeas"
+
+func parentDataDir() string {
+ dir, err := homedir.Dir()
+ if err != nil {
+ panic(err)
+ }
+
+ return dir
+}
diff --git a/posts_win.go b/posts_win.go
new file mode 100644
index 0000000..3166372
--- /dev/null
+++ b/posts_win.go
@@ -0,0 +1,13 @@
+// +build windows
+
+package main
+
+import (
+ "os"
+)
+
+const DATA_DIR_NAME = "Write.as"
+
+func parentDataDir() string {
+ return os.Getenv("APPDATA")
+}
diff --git a/utils/fileutils.go b/utils/fileutils.go
new file mode 100644
index 0000000..c0b4d08
--- /dev/null
+++ b/utils/fileutils.go
@@ -0,0 +1,13 @@
+package fileutils
+
+import (
+ "os"
+)
+
+// Exists returns whether or not the given file exists
+func Exists(p string) bool {
+ if _, err := os.Stat(p); err == nil {
+ return true
+ }
+ return false
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 16, 8:05 PM (8 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3240424
Attached To
rWCLI writeas-cli
Event Timeline
Log In to Comment