lotus/api/api.go

154 lines
3.7 KiB
Go
Raw Normal View History

2023-08-13 03:04:16 +01:00
package api
import (
2023-09-11 17:45:37 +01:00
"bytes"
2023-08-13 03:04:16 +01:00
"encoding/json"
2023-09-11 17:23:44 +01:00
"github.com/gorilla/websocket"
2023-08-13 03:04:16 +01:00
"github.com/julienschmidt/httprouter"
2023-09-11 11:55:52 +01:00
"log"
2023-08-13 03:04:16 +01:00
"net/http"
"time"
)
2023-11-19 23:36:45 +00:00
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
2023-09-11 17:23:44 +01:00
func SetupApiServer(listen string, auth *AuthChecker, send Smtp, recv Imap) *http.Server {
2023-08-13 03:04:16 +01:00
r := httprouter.New()
2023-08-21 00:26:22 +01:00
// === ACCOUNT ===
2023-09-11 17:23:44 +01:00
r.GET("/identities", auth.Middleware(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
2023-08-21 00:26:22 +01:00
// TODO(melon): find users aliases and other account data
}))
// === SMTP ===
2023-09-11 17:23:44 +01:00
r.POST("/smtp", auth.Middleware(MessageSender(send)))
2023-09-11 17:35:29 +01:00
r.GET("/imap", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
2023-09-11 17:23:44 +01:00
// upgrade to websocket conn and defer close
c, err := upgrader.Upgrade(rw, req, nil)
if err != nil {
2023-09-11 17:23:44 +01:00
log.Println("[Imap] Failed to upgrade to websocket:", err)
2023-08-21 00:26:22 +01:00
return
}
2023-09-11 17:23:44 +01:00
defer c.Close()
// set a really short deadline to refuse unauthenticated clients
deadline := time.Now().Add(5 * time.Second)
_ = c.SetReadDeadline(deadline)
_ = c.SetWriteDeadline(deadline)
// close on all possible errors, assume we are being attacked
mt, msg, err := c.ReadMessage()
2023-08-21 00:26:22 +01:00
if err != nil {
return
}
2023-09-11 17:23:44 +01:00
if mt != websocket.TextMessage {
return
2023-09-11 11:55:52 +01:00
}
2023-09-11 17:23:44 +01:00
if len(msg) >= 2000 {
2023-08-21 00:26:22 +01:00
return
}
2023-09-11 17:23:44 +01:00
2023-09-11 17:45:37 +01:00
// parse token from message
var tokenMsg struct {
Token string `json:"token"`
}
dec := json.NewDecoder(bytes.NewReader(msg))
dec.DisallowUnknownFields()
err = dec.Decode(&tokenMsg)
if err != nil {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Authentication missing"})
2023-09-11 17:45:37 +01:00
return
}
2023-09-11 17:23:44 +01:00
2023-09-11 17:45:37 +01:00
// get a "possible" auth token value
// exit on empty token value
if tokenMsg.Token == "" {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Authentication missing"})
2023-08-21 00:26:22 +01:00
return
}
2023-09-11 17:23:44 +01:00
// check the token
2023-09-11 17:45:37 +01:00
authUser, err := auth.Check(tokenMsg.Token)
2023-08-21 00:26:22 +01:00
if err != nil {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Authentication invalid"})
2023-08-21 00:26:22 +01:00
return
}
2023-09-11 17:23:44 +01:00
2023-11-19 23:36:45 +00:00
mailInboxes := authUser.Claims.Perms.Search("mail:inbox=*")
if len(mailInboxes) != 1 {
_ = c.WriteJSON(map[string]string{"error": "Authentication should only contain one owned inbox"})
return
}
2023-09-11 17:46:53 +01:00
// open imap client
2023-11-19 23:36:45 +00:00
client, err := recv.MakeClient(mailInboxes[0][len("mail:inbox="):])
2023-08-21 00:26:22 +01:00
if err != nil {
2023-11-19 23:36:45 +00:00
log.Println("Making a client failed:", err)
_ = c.WriteJSON(map[string]string{"error": "Making a client failed"})
2023-08-21 00:26:22 +01:00
return
}
2023-09-11 17:23:44 +01:00
2023-09-11 17:46:53 +01:00
// auth was ok
err = c.WriteJSON(map[string]string{"auth": "ok"})
if err != nil {
return
}
2023-09-11 17:23:44 +01:00
for {
// authenticated users get longer to reply
// a simple ping/pong setup bypasses this
d := time.Now().Add(5 * time.Minute)
_ = c.SetReadDeadline(d)
_ = c.SetWriteDeadline(d)
// read incoming message
var m struct {
Action string `json:"action"`
Args json.RawMessage `json:"args"`
2023-09-11 17:23:44 +01:00
}
err := c.ReadJSON(&m)
if err != nil {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Invalid input"})
2023-09-11 17:23:44 +01:00
return
}
// handle action
j, err := client.HandleWS(m.Action, m.Args)
if err != nil {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Action failed"})
2023-09-11 17:23:44 +01:00
return
}
// write outgoing message
err = c.WriteJSON(j)
if err != nil {
2023-09-11 17:49:44 +01:00
_ = c.WriteJSON(map[string]string{"error": "Invalid output"})
2023-09-11 17:23:44 +01:00
return
}
}
})
2023-08-13 03:04:16 +01:00
return &http.Server{
2023-08-21 00:26:22 +01:00
Addr: listen,
2023-08-13 03:04:16 +01:00
Handler: r,
ReadTimeout: time.Minute,
ReadHeaderTimeout: time.Minute,
WriteTimeout: time.Minute,
IdleTimeout: time.Minute,
MaxHeaderBytes: 2500,
}
}
2023-08-21 00:26:22 +01:00
// apiError outputs a generic JSON error message
func apiError(rw http.ResponseWriter, code int, m string) {
rw.WriteHeader(code)
_ = json.NewEncoder(rw).Encode(map[string]string{
"error": m,
})
}