|
|
|
@ -12,6 +12,7 @@ import (
|
|
|
|
|
"io"
|
|
|
|
|
"net"
|
|
|
|
|
"os"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
@ -36,7 +37,7 @@ type Client struct {
|
|
|
|
|
prev *terminal.State
|
|
|
|
|
keyStore map[string]rsa.PublicKey
|
|
|
|
|
requestCount int
|
|
|
|
|
outstanding map[int]chan Envelope
|
|
|
|
|
outstanding map[int]chan request
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// establishes a connection to the server
|
|
|
|
@ -77,24 +78,23 @@ func (c *Client) handleMessages() {
|
|
|
|
|
// handle a message received from the server
|
|
|
|
|
func (c *Client) handleMessage(m Envelope) error {
|
|
|
|
|
c.info("received response for message %d", m.Id)
|
|
|
|
|
res, ok := c.outstanding[m.Id]
|
|
|
|
|
p, ok := c.outstanding[m.Id]
|
|
|
|
|
if !ok {
|
|
|
|
|
c.info("%v", m)
|
|
|
|
|
c.err("received message corresponding to no known request id: %d", m.Id)
|
|
|
|
|
return fmt.Errorf("no such id: %d", m.Id)
|
|
|
|
|
}
|
|
|
|
|
res <- m
|
|
|
|
|
close(res)
|
|
|
|
|
r, err := m.Open()
|
|
|
|
|
if err != nil {
|
|
|
|
|
p <- ErrorDoc(err.Error())
|
|
|
|
|
} else {
|
|
|
|
|
p <- r
|
|
|
|
|
}
|
|
|
|
|
close(p)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) handleNote(raw json.RawMessage) error {
|
|
|
|
|
c.info("unmarshaling note...")
|
|
|
|
|
var enote EncryptedNote
|
|
|
|
|
if err := json.Unmarshal(raw, &enote); err != nil {
|
|
|
|
|
return fmt.Errorf("unable to unmarshal encrypted note: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) handleNote(enote *EncryptedNote) error {
|
|
|
|
|
c.info("aes key ciphertext: %x", enote.Key)
|
|
|
|
|
key, err := rsa.DecryptPKCS1v15(rand.Reader, c.key, enote.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
@ -119,12 +119,7 @@ func (c *Client) handleNote(raw json.RawMessage) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) handleListNotes(raw json.RawMessage) error {
|
|
|
|
|
var notes ListNotesResponse
|
|
|
|
|
if err := json.Unmarshal(raw, ¬es); err != nil {
|
|
|
|
|
return fmt.Errorf("unable to unmarshal listnotes response: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) handleListNotes(notes ListNotesResponse) error {
|
|
|
|
|
writeNoteTitle := func(id int, title string) {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
@ -159,25 +154,20 @@ func (c *Client) handshake() error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
res := <-promise
|
|
|
|
|
switch res.Kind {
|
|
|
|
|
case "error":
|
|
|
|
|
var e ErrorDoc
|
|
|
|
|
if err := json.Unmarshal(res.Body, &e); err != nil {
|
|
|
|
|
return fmt.Errorf("cannot read server error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
c.err("server error: %v", e.Error())
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
close(c.done)
|
|
|
|
|
case "bool":
|
|
|
|
|
c.info(string(res.Body))
|
|
|
|
|
return v
|
|
|
|
|
case *Bool:
|
|
|
|
|
c.renderLine()
|
|
|
|
|
return nil
|
|
|
|
|
default:
|
|
|
|
|
c.err("i dunno what to do with this")
|
|
|
|
|
close(c.done)
|
|
|
|
|
return fmt.Errorf("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) sendRequest(r request) (chan Envelope, error) {
|
|
|
|
|
func (c *Client) sendRequest(r request) (chan request, error) {
|
|
|
|
|
e, err := wrapRequest(c.requestCount, r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@ -187,7 +177,7 @@ func (c *Client) sendRequest(r request) (chan Envelope, error) {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res := make(chan Envelope, 1)
|
|
|
|
|
res := make(chan request, 1)
|
|
|
|
|
c.outstanding[c.requestCount] = res
|
|
|
|
|
c.requestCount++
|
|
|
|
|
c.info("sending json request: %s", b)
|
|
|
|
@ -344,23 +334,41 @@ func (c *Client) getNote(args []string) {
|
|
|
|
|
c.err("that doesn't look like an int: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res, err := c.sendRequest(GetNoteRequest{Id: id})
|
|
|
|
|
p, err := c.sendRequest(GetNoteRequest{Id: id})
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("couldn't request note: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
e := <-res
|
|
|
|
|
c.handleNote(e.Body)
|
|
|
|
|
res := <-p
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *EncryptedNote:
|
|
|
|
|
c.handleNote(v)
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
c.err("error getting note: %v", v.Error())
|
|
|
|
|
c.renderLine()
|
|
|
|
|
default:
|
|
|
|
|
c.err("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) listNotes(args []string) {
|
|
|
|
|
r := &ListNotes{N: 10}
|
|
|
|
|
res, err := c.sendRequest(r)
|
|
|
|
|
p, err := c.sendRequest(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
}
|
|
|
|
|
e := <-res
|
|
|
|
|
c.handleListNotes(e.Body)
|
|
|
|
|
res := <-p
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *ListNotesResponse:
|
|
|
|
|
c.handleListNotes(*v)
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
c.err("error retrieving list of notes: %v", v.Error())
|
|
|
|
|
c.renderLine()
|
|
|
|
|
default:
|
|
|
|
|
c.err("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) encryptNote(title string, message []rune) (*EncryptedNote, error) {
|
|
|
|
@ -412,13 +420,21 @@ func (c *Client) fetchKey(args []string) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
req := KeyRequest(args[0])
|
|
|
|
|
res, err := c.sendRequest(req)
|
|
|
|
|
p, err := c.sendRequest(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("couldn't send key request: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
e := <-res
|
|
|
|
|
c.handleKeyResponse(e.Body)
|
|
|
|
|
res := <-p
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *KeyResponse:
|
|
|
|
|
c.saveKey(v.Nick, v.Key)
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
c.err("error fetching key: %v", v.Error())
|
|
|
|
|
default:
|
|
|
|
|
c.err("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
}
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) saveKey(nick string, key rsa.PublicKey) {
|
|
|
|
@ -518,11 +534,10 @@ func (c *Client) sendMessage(args []string) {
|
|
|
|
|
|
|
|
|
|
func (c *Client) listMessages(args []string) {
|
|
|
|
|
r := &ListMessages{N: 10}
|
|
|
|
|
promise, err := c.sendRequest(r)
|
|
|
|
|
p, err := c.sendRequest(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
}
|
|
|
|
|
env := <-promise
|
|
|
|
|
|
|
|
|
|
writeMessageId := func(id int, from string) {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
@ -532,23 +547,29 @@ func (c *Client) listMessages(args []string) {
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var res ListMessagesResponse
|
|
|
|
|
if err := json.Unmarshal(env.Body, &res); err != nil {
|
|
|
|
|
c.err("couldn't read list messages response: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, item := range res {
|
|
|
|
|
key, err := c.rsaDecrypt(item.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("unable to read aes key: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
from, err := c.aesDecrypt(key, item.From)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("unable to read message sender: %v", err)
|
|
|
|
|
return
|
|
|
|
|
res := <-p
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
c.err("error getting message list: %v", v.Error())
|
|
|
|
|
c.renderLine()
|
|
|
|
|
case *ListMessagesResponse:
|
|
|
|
|
for _, item := range *v {
|
|
|
|
|
key, err := c.rsaDecrypt(item.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("unable to read aes key: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
from, err := c.aesDecrypt(key, item.From)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("unable to read message sender: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeMessageId(item.Id, string(from))
|
|
|
|
|
}
|
|
|
|
|
writeMessageId(item.Id, string(from))
|
|
|
|
|
default:
|
|
|
|
|
c.err("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -564,51 +585,53 @@ func (c *Client) getMessage(args []string) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
promise, err := c.sendRequest(GetMessage{Id: id})
|
|
|
|
|
p, err := c.sendRequest(GetMessage{Id: id})
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
raw := <-promise
|
|
|
|
|
res := <-p
|
|
|
|
|
switch v := res.(type) {
|
|
|
|
|
case *Message:
|
|
|
|
|
key, err := c.rsaDecrypt(v.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var msg Message
|
|
|
|
|
if err := json.Unmarshal(raw.Body, &msg); err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
from, err := c.aesDecrypt(key, v.From)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key, err := c.rsaDecrypt(msg.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
text, err := c.aesDecrypt(key, v.Text)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
from, err := c.aesDecrypt(key, msg.From)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
text, err := c.aesDecrypt(key, msg.Text)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.err("%v", err)
|
|
|
|
|
return
|
|
|
|
|
c.trunc()
|
|
|
|
|
fmt.Print("\033[37m")
|
|
|
|
|
fmt.Print("\rFrom: ")
|
|
|
|
|
fmt.Print("\033[0m") // unset color choice
|
|
|
|
|
fmt.Println(string(from))
|
|
|
|
|
fmt.Print("\033[90m")
|
|
|
|
|
fmt.Println("--------------------------------------------------------------------------------")
|
|
|
|
|
fmt.Printf("\033[0m")
|
|
|
|
|
fmt.Println(string(text))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
case *ErrorDoc:
|
|
|
|
|
c.err("error getting message: %v", v.Error())
|
|
|
|
|
c.renderLine()
|
|
|
|
|
default:
|
|
|
|
|
c.err("received response of unexpected type: %v", reflect.TypeOf(v))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
c.trunc()
|
|
|
|
|
fmt.Print("\033[37m")
|
|
|
|
|
fmt.Print("\rFrom: ")
|
|
|
|
|
fmt.Print("\033[0m") // unset color choice
|
|
|
|
|
fmt.Println(string(from))
|
|
|
|
|
fmt.Print("\033[90m")
|
|
|
|
|
fmt.Println("--------------------------------------------------------------------------------")
|
|
|
|
|
fmt.Printf("\033[0m")
|
|
|
|
|
fmt.Println(string(text))
|
|
|
|
|
c.renderLine()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Client) readTextBlock() ([]rune, error) {
|
|
|
|
@ -778,7 +801,7 @@ func connect() {
|
|
|
|
|
done: make(chan interface{}),
|
|
|
|
|
line: make([]rune, 0, 32),
|
|
|
|
|
keyStore: make(map[string]rsa.PublicKey, 8),
|
|
|
|
|
outstanding: make(map[int]chan Envelope),
|
|
|
|
|
outstanding: make(map[int]chan request),
|
|
|
|
|
}
|
|
|
|
|
client.run()
|
|
|
|
|
}
|
|
|
|
|