From 2f5dd15c779d510662a32a187f6bbc83a1a7f4de Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 7 Dec 2014 18:08:46 -0500 Subject: [PATCH] can now store encrypted notes but you can't read them --- client.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- note.go | 14 +++++++++ server.go | 44 +++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 note.go diff --git a/client.go b/client.go index d95a417..aa5860b 100644 --- a/client.go +++ b/client.go @@ -3,6 +3,7 @@ package main import ( "bufio" "code.google.com/p/go.crypto/ssh/terminal" + "crypto/rand" "crypto/rsa" "encoding/json" "fmt" @@ -41,6 +42,7 @@ type Client struct { prev *terminal.State } +// establishes a connection to the server func (c *Client) dial() error { addr := fmt.Sprintf("%s:%d", c.host, c.port) c.info("dialing %s", addr) @@ -55,6 +57,7 @@ func (c *Client) dial() error { return nil } +// handles messages received from the current server func (c *Client) handleMessages() { messages := make(chan Envelope) errors := make(chan error) @@ -74,6 +77,7 @@ func (c *Client) handleMessages() { } } +// handle a message received from the server func (c *Client) handleMessage(m Envelope) error { switch m.Kind { case "meta": @@ -83,6 +87,7 @@ func (c *Client) handleMessage(m Envelope) error { } } +// handles a meta message; that is, a message that is shown to the user func (c *Client) handleMeta(body json.RawMessage) error { var meta Meta if err := json.Unmarshal(body, &meta); err != nil { @@ -169,6 +174,7 @@ func (c *Client) control(r rune) { c.enter() case 21: // ctrl+u c.clearLine() + case 27: // up case 127: // backspace c.backspace() default: @@ -185,16 +191,98 @@ func (c *Client) enter() { func (c *Client) exec(line string) { parts := strings.Split(line, " ") - if len(parts) == 0 { + if len(parts) == 0 || strings.TrimSpace(parts[0]) == "" { c.renderLine() return } switch parts[0] { + case "notes/create": + c.createNote(parts[1:]) default: c.err("unrecognized client command: %s", parts[0]) } } +func (c *Client) createNote(args []string) { + if len(args) < 1 { + c.err("yeah you need to specify a title.") + return + } + title := strings.Join(args, " ") + c.info("creating new note: %s", title) + msg, err := c.readTextBlock() + if err != nil { + c.err("%v", err) + return + } + note, err := c.encryptNote(title, msg) + if err != nil { + c.err("%v", err) + return + } + if err := c.sendRequest(note); err != nil { + c.err("error sending note: %v", err) + } +} + +func (c *Client) encryptNote(title string, note []rune) (NoteRequest, error) { + obj := &NoteData{ + Title: title, + Body: []byte(string(note)), // lol, nooo, stahp + } + b, err := json.Marshal(obj) + if err != nil { + return nil, fmt.Errorf("unable to marshal note: %v", err) + } + ctxt, err := rsa.EncryptPKCS1v15(rand.Reader, &c.key.PublicKey, b) + if err != nil { + return nil, fmt.Errorf("unable to encrypt note: %v", err) + } + return ctxt, nil +} + +func (c *Client) readTextBlock() ([]rune, error) { + // god dammit what have i gotten myself into + msg := make([]rune, 0, 400) + fmt.Print("\033[1K") // clear to beginning of current line + fmt.Print("\r") // move to beginning of current line + fmt.Print("\033[s") // save the cursor position + renderMsg := func() { + fmt.Print("\033[u") // restore cursor position + fmt.Print("\033[0J") // clear to screen end + fmt.Printf("%s", string(msg)) // write message out + } + in := bufio.NewReader(os.Stdin) + for { + r, _, err := in.ReadRune() + switch err { + case io.EOF: + return msg, nil + case nil: + default: + return nil, fmt.Errorf("error reading textblock: %v", err) + } + if unicode.IsGraphic(r) { + msg = append(msg, r) + renderMsg() + continue + } + switch r { + case 13: // enter + msg = append(msg, '\n') + renderMsg() + case 127: // backspace + if len(msg) == 0 { + break + } + msg = msg[:len(msg)-1] + renderMsg() + case 4: // ctrl+d + return msg, nil + } + } +} + func (c *Client) eof() { fmt.Print("\033[1K") // clear to beginning of current line fmt.Print("\r") // move to beginning of current line diff --git a/note.go b/note.go new file mode 100644 index 0000000..d656ce6 --- /dev/null +++ b/note.go @@ -0,0 +1,14 @@ +package main + +import () + +type NoteRequest []byte + +func (n NoteRequest) Kind() string { + return "note" +} + +type NoteData struct { + Title string + Body []byte +} diff --git a/server.go b/server.go index b6acffd..5650dd9 100644 --- a/server.go +++ b/server.go @@ -4,8 +4,12 @@ import ( "crypto/rsa" "encoding/json" "fmt" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" "io" "net" + "strconv" + "strings" ) func stream(r io.Reader, c chan Envelope, e chan error, done chan interface{}) { @@ -29,6 +33,7 @@ type serverConnection struct { conn net.Conn nick string key rsa.PublicKey + db *leveldb.DB } func (s *serverConnection) sendMeta(template string, args ...interface{}) { @@ -46,6 +51,8 @@ func (s *serverConnection) handleRequest(request Envelope) error { switch request.Kind { case "auth": return s.handleAuthRequest(request.Body) + case "note": + return s.handleNoteRequest(request.Body) default: return fmt.Errorf("no such request type: %v", request.Kind) } @@ -59,10 +66,47 @@ func (s *serverConnection) handleAuthRequest(body json.RawMessage) error { s.nick = auth.Nick s.key = auth.Key s.sendMeta("hello, %s", auth.Nick) + if err := s.openDB(); err != nil { + error_log.Printf("failed to open database: %v", err) + } info_log.Printf("authenticated user %s", auth.Nick) return nil } +func (s *serverConnection) handleNoteRequest(body json.RawMessage) error { + r := &util.Range{Start: []byte("notes/")} + it := s.db.NewIterator(r, nil) + defer it.Release() + + id := 0 + if it.Last() { + k := it.Key() + id_s := strings.TrimPrefix(string(k), "notes/") + lastId, err := strconv.Atoi(id_s) + if err != nil { + return fmt.Errorf("error getting note id: %v", err) + } + id = lastId + 1 + } + key := fmt.Sprintf("notes/%d", id) + if err := s.db.Put([]byte(key), body, nil); err != nil { + return fmt.Errorf("unable to write note to db: %v", err) + } + info_log.Printf("stored new note at %s", key) + return nil +} + +func (s *serverConnection) openDB() error { + path := fmt.Sprintf("./%s.db", s.nick) + db, err := leveldb.OpenFile(path, nil) + if err != nil { + return fmt.Errorf("unable to open db file at %s: %v", path, err) + } + info_log.Printf("opened database file: %s", path) + s.db = db + return nil +} + func (s *serverConnection) run() { defer func() { s.conn.Close()