From 1c5e17472d01be241ff80b062f77a88dafc51f17 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 16 Dec 2019 14:36:43 +0100 Subject: Add basic search Closes: https://todo.sr.ht/~sircmpwn/koushin/23 --- plugins/base/handlers.go | 24 ++++++++++++++----- plugins/base/imap.go | 51 ++++++++++++++++++++++++++++++++++++++-- plugins/base/plugin.go | 1 + plugins/base/public/login.html | 2 +- plugins/base/public/mailbox.html | 5 ++++ 5 files changed, 74 insertions(+), 9 deletions(-) (limited to 'plugins/base') diff --git a/plugins/base/handlers.go b/plugins/base/handlers.go index d0ba9ba..133052c 100644 --- a/plugins/base/handlers.go +++ b/plugins/base/handlers.go @@ -23,6 +23,7 @@ type MailboxRenderData struct { Mailboxes []*imap.MailboxInfo Messages []imapMessage PrevPage, NextPage int + Query string } func handleGetMailbox(ectx echo.Context) error { @@ -41,6 +42,8 @@ func handleGetMailbox(ectx echo.Context) error { } } + query := ctx.FormValue("query") + var mailboxes []*imap.MailboxInfo var msgs []imapMessage var mbox *imap.MailboxStatus @@ -49,7 +52,12 @@ func handleGetMailbox(ectx echo.Context) error { if mailboxes, err = listMailboxes(c); err != nil { return err } - if msgs, err = listMessages(c, mboxName, page); err != nil { + if query != "" { + msgs, err = searchMessages(c, mboxName, query) + } else { + msgs, err = listMessages(c, mboxName, page) + } + if err != nil { return err } mbox = c.Mailbox() @@ -60,11 +68,14 @@ func handleGetMailbox(ectx echo.Context) error { } prevPage, nextPage := -1, -1 - if page > 0 { - prevPage = page - 1 - } - if (page+1)*messagesPerPage < int(mbox.Messages) { - nextPage = page + 1 + if query == "" { + // TODO: paging for search + if page > 0 { + prevPage = page - 1 + } + if (page+1)*messagesPerPage < int(mbox.Messages) { + nextPage = page + 1 + } } return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{ @@ -74,6 +85,7 @@ func handleGetMailbox(ectx echo.Context) error { Messages: msgs, PrevPage: prevPage, NextPage: nextPage, + Query: query, }) } diff --git a/plugins/base/imap.go b/plugins/base/imap.go index 93f3c4e..b1e36a6 100644 --- a/plugins/base/imap.go +++ b/plugins/base/imap.go @@ -189,7 +189,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes return nil, nil } - seqSet := new(imap.SeqSet) + var seqSet imap.SeqSet seqSet.AddRange(uint32(from), uint32(to)) fetch := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchBodyStructure} @@ -197,7 +197,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes ch := make(chan *imap.Message, 10) done := make(chan error, 1) go func() { - done <- conn.Fetch(seqSet, fetch, ch) + done <- conn.Fetch(&seqSet, fetch, ch) }() msgs := make([]imapMessage, 0, to-from) @@ -218,6 +218,53 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes return msgs, nil } +func searchMessages(conn *imapclient.Client, mboxName, query string) ([]imapMessage, error) { + if err := ensureMailboxSelected(conn, mboxName); err != nil { + return nil, err + } + + criteria := imap.SearchCriteria{Text: []string{query}} + nums, err := conn.Search(&criteria) + if err != nil { + return nil, fmt.Errorf("UID SEARCH failed: %v", err) + } + if len(nums) == 0 { + return nil, nil + } + + indexes := make(map[uint32]int) + for i, num := range nums { + indexes[num] = i + } + + // TODO: paging + var seqSet imap.SeqSet + seqSet.AddNum(nums...) + + fetch := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchBodyStructure} + + ch := make(chan *imap.Message, 10) + done := make(chan error, 1) + go func() { + done <- conn.Fetch(&seqSet, fetch, ch) + }() + + msgs := make([]imapMessage, len(nums)) + for msg := range ch { + i, ok := indexes[msg.SeqNum] + if !ok { + continue + } + msgs[i] = imapMessage{msg} + } + + if err := <-done; err != nil { + return nil, fmt.Errorf("failed to fetch message list: %v", err) + } + + return msgs, nil +} + func getMessagePart(conn *imapclient.Client, mboxName string, uid uint32, partPath []int) (*imapMessage, *message.Entity, error) { if err := ensureMailboxSelected(conn, mboxName); err != nil { return nil, nil, err diff --git a/plugins/base/plugin.go b/plugins/base/plugin.go index 906730d..7c2aeaf 100644 --- a/plugins/base/plugin.go +++ b/plugins/base/plugin.go @@ -23,6 +23,7 @@ func init() { }) p.GET("/mailbox/:mbox", handleGetMailbox) + p.POST("/mailbox/:mbox", handleGetMailbox) p.GET("/message/:mbox/:uid", func(ectx echo.Context) error { ctx := ectx.(*koushin.Context) diff --git a/plugins/base/public/login.html b/plugins/base/public/login.html index 8ce5f25..7378935 100644 --- a/plugins/base/public/login.html +++ b/plugins/base/public/login.html @@ -2,7 +2,7 @@

koushin

-
+

diff --git a/plugins/base/public/mailbox.html b/plugins/base/public/mailbox.html index ddd1260..53cf697 100644 --- a/plugins/base/public/mailbox.html +++ b/plugins/base/public/mailbox.html @@ -8,6 +8,11 @@

{{.Mailbox.Name}}

+ + + +
+

Mailboxes: