diff options
-rw-r--r-- | aerc.go | 11 | ||||
-rw-r--r-- | lib/ipc/message.go | 52 | ||||
-rw-r--r-- | lib/ipc/receive.go | 57 | ||||
-rw-r--r-- | lib/ipc/send.go | 24 |
4 files changed, 108 insertions, 36 deletions
@@ -159,12 +159,8 @@ func main() { } retryExec := false args := os.Args[optind:] - if len(args) > 1 { - usage("error: invalid arguments") - return - } else if len(args) == 1 { - arg := args[0] - err := ipc.ConnectAndExec(arg) + if len(args) > 0 { + err := ipc.ConnectAndExec(args) if err == nil { return // other aerc instance takes over } @@ -232,8 +228,7 @@ func main() { if retryExec { // retry execution - arg := args[0] - err := ipc.ConnectAndExec(arg) + err := ipc.ConnectAndExec(args) if err != nil { fmt.Fprintf(os.Stderr, "Failed to communicate to aerc: %v\n", err) err = aerc.CloseBackends() diff --git a/lib/ipc/message.go b/lib/ipc/message.go new file mode 100644 index 00000000..3bd1e85c --- /dev/null +++ b/lib/ipc/message.go @@ -0,0 +1,52 @@ +package ipc + +import "encoding/json" + +// Request constains all parameters needed for the main instance to respond to +// a request. +type Request struct { + // Arguments contains the commandline arguments. The detection of what + // action to take is left to the receiver. + Arguments []string `json:"arguments"` +} + +// Response is used to report the results of a command. +type Response struct { + // Error contains the success-state of the command. Error is an empty + // string if everything ran successfully. + Error string `json:"error"` +} + +// Encode transforms the message in an easier to transfer format +func (msg *Request) Encode() ([]byte, error) { + return json.Marshal(msg) +} + +// DecodeMessage consumes a raw message and returns the message contained +// within. +func DecodeMessage(data []byte) (*Request, error) { + msg := new(Request) + err := json.Unmarshal(data, msg) + return msg, err +} + +// Encode transforms the message in an easier to transfer format +func (msg *Response) Encode() ([]byte, error) { + return json.Marshal(msg) +} + +// DecodeRequest consumes a raw message and returns the message contained +// within. +func DecodeRequest(data []byte) (*Request, error) { + msg := new(Request) + err := json.Unmarshal(data, msg) + return msg, err +} + +// DecodeResponse consumes a raw message and returns the message contained +// within. +func DecodeResponse(data []byte) (*Response, error) { + msg := new(Response) + err := json.Unmarshal(data, msg) + return msg, err +} diff --git a/lib/ipc/receive.go b/lib/ipc/receive.go index c074b116..11a96e30 100644 --- a/lib/ipc/receive.go +++ b/lib/ipc/receive.go @@ -3,7 +3,6 @@ package ipc import ( "bufio" "errors" - "fmt" "net" "net/url" "os" @@ -26,7 +25,7 @@ type AercServer struct { func StartServer() (*AercServer, error) { sockpath := path.Join(xdg.RuntimeDir(), "aerc.sock") // remove the socket if it is not connected to a session - if err := ConnectAndExec(""); err != nil { + if err := ConnectAndExec(nil); err != nil { os.Remove(sockpath) } log.Debugf("Starting Unix server: %s", sockpath) @@ -69,14 +68,25 @@ func (as *AercServer) Serve() { log.Errorf("unix:%d failed to set deadline: %v", clientId, err) } for scanner.Scan() { + // allow up to 1 minute between commands err = conn.SetDeadline(time.Now().Add(1 * time.Minute)) if err != nil { log.Errorf("unix:%d failed to update deadline: %v", clientId, err) } - msg := scanner.Text() - log.Tracef("unix:%d got message %s", clientId, msg) + msg, err := DecodeRequest(scanner.Bytes()) + log.Tracef("unix:%d got message %s", clientId, scanner.Text()) + if err != nil { + log.Errorf("unix:%d failed to parse request: %v", clientId, err) + continue + } - _, err = conn.Write([]byte(as.handleMessage(msg))) + response := as.handleMessage(msg) + result, err := response.Encode() + if err != nil { + log.Errorf("unix:%d failed to encode result: %v", clientId, err) + continue + } + _, err = conn.Write(append(result, '\n')) if err != nil { log.Errorf("unix:%d failed to send response: %v", clientId, err) break @@ -86,31 +96,30 @@ func (as *AercServer) Serve() { } } -func (as *AercServer) handleMessage(msg string) string { - if !strings.ContainsRune(msg, ':') { - return "error: invalid command\n" +func (as *AercServer) handleMessage(req *Request) *Response { + if len(req.Arguments) == 0 { + return &Response{} // send noop success message, i.e. ping } - prefix := msg[:strings.IndexRune(msg, ':')] var err error - switch prefix { - case "mailto": - mailto, err := url.Parse(msg) + switch { + case strings.HasPrefix(req.Arguments[0], "mailto:"): + mailto, err := url.Parse(req.Arguments[0]) if err != nil { - return fmt.Sprintf("error: %v\n", err) + return &Response{Error: err.Error()} } - if as.OnMailto != nil { - err = as.OnMailto(mailto) - if err != nil { - return fmt.Sprintf("mailto failed: %v\n", err) + err = as.OnMailto(mailto) + if err != nil { + return &Response{ + Error: err.Error(), } } - case "mbox": - if as.OnMbox != nil { - err = as.OnMbox(msg) - if err != nil { - return fmt.Sprintf("mbox failed: %v\n", err) - } + case strings.HasPrefix(req.Arguments[0], "mbox:"): + err = as.OnMbox(req.Arguments[0]) + if err != nil { + return &Response{Error: err.Error()} } + default: + return &Response{Error: "command not understood"} } - return "result: success\n" + return &Response{} } diff --git a/lib/ipc/send.go b/lib/ipc/send.go index 5cc97cc0..522e944a 100644 --- a/lib/ipc/send.go +++ b/lib/ipc/send.go @@ -10,14 +10,20 @@ import ( "github.com/kyoh86/xdg" ) -func ConnectAndExec(msg string) error { +func ConnectAndExec(args []string) error { sockpath := path.Join(xdg.RuntimeDir(), "aerc.sock") conn, err := net.Dial("unix", sockpath) if err != nil { return err } defer conn.Close() - _, err = conn.Write([]byte(msg + "\n")) + + req, err := (&Request{Arguments: args}).Encode() + if err != nil { + return fmt.Errorf("failed to encode request: %w", err) + } + + _, err = conn.Write(append(req, '\n')) if err != nil { return fmt.Errorf("failed to send message: %w", err) } @@ -25,7 +31,17 @@ func ConnectAndExec(msg string) error { if !scanner.Scan() { return errors.New("No response from server") } - result := scanner.Text() - fmt.Println(result) + resp, err := DecodeResponse(scanner.Bytes()) + if err != nil { + return err + } + + // TODO: handle this in a more elegant manner + if resp.Error == "" { + fmt.Println("result: success") + } else { + fmt.Println("result: ", resp.Error) + } + return nil } |