parent
1a658fc22c
commit
1c5e17472d
5 changed files with 74 additions and 9 deletions
|
@ -23,6 +23,7 @@ type MailboxRenderData struct {
|
||||||
Mailboxes []*imap.MailboxInfo
|
Mailboxes []*imap.MailboxInfo
|
||||||
Messages []imapMessage
|
Messages []imapMessage
|
||||||
PrevPage, NextPage int
|
PrevPage, NextPage int
|
||||||
|
Query string
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGetMailbox(ectx echo.Context) error {
|
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 mailboxes []*imap.MailboxInfo
|
||||||
var msgs []imapMessage
|
var msgs []imapMessage
|
||||||
var mbox *imap.MailboxStatus
|
var mbox *imap.MailboxStatus
|
||||||
|
@ -49,7 +52,12 @@ func handleGetMailbox(ectx echo.Context) error {
|
||||||
if mailboxes, err = listMailboxes(c); err != nil {
|
if mailboxes, err = listMailboxes(c); err != nil {
|
||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
mbox = c.Mailbox()
|
mbox = c.Mailbox()
|
||||||
|
@ -60,12 +68,15 @@ func handleGetMailbox(ectx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPage, nextPage := -1, -1
|
prevPage, nextPage := -1, -1
|
||||||
|
if query == "" {
|
||||||
|
// TODO: paging for search
|
||||||
if page > 0 {
|
if page > 0 {
|
||||||
prevPage = page - 1
|
prevPage = page - 1
|
||||||
}
|
}
|
||||||
if (page+1)*messagesPerPage < int(mbox.Messages) {
|
if (page+1)*messagesPerPage < int(mbox.Messages) {
|
||||||
nextPage = page + 1
|
nextPage = page + 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{
|
return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{
|
||||||
RenderData: *koushin.NewRenderData(ctx),
|
RenderData: *koushin.NewRenderData(ctx),
|
||||||
|
@ -74,6 +85,7 @@ func handleGetMailbox(ectx echo.Context) error {
|
||||||
Messages: msgs,
|
Messages: msgs,
|
||||||
PrevPage: prevPage,
|
PrevPage: prevPage,
|
||||||
NextPage: nextPage,
|
NextPage: nextPage,
|
||||||
|
Query: query,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
seqSet := new(imap.SeqSet)
|
var seqSet imap.SeqSet
|
||||||
seqSet.AddRange(uint32(from), uint32(to))
|
seqSet.AddRange(uint32(from), uint32(to))
|
||||||
|
|
||||||
fetch := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchBodyStructure}
|
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)
|
ch := make(chan *imap.Message, 10)
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
done <- conn.Fetch(seqSet, fetch, ch)
|
done <- conn.Fetch(&seqSet, fetch, ch)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
msgs := make([]imapMessage, 0, to-from)
|
msgs := make([]imapMessage, 0, to-from)
|
||||||
|
@ -218,6 +218,53 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes
|
||||||
return msgs, nil
|
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) {
|
func getMessagePart(conn *imapclient.Client, mboxName string, uid uint32, partPath []int) (*imapMessage, *message.Entity, error) {
|
||||||
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -23,6 +23,7 @@ func init() {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.GET("/mailbox/:mbox", handleGetMailbox)
|
p.GET("/mailbox/:mbox", handleGetMailbox)
|
||||||
|
p.POST("/mailbox/:mbox", handleGetMailbox)
|
||||||
|
|
||||||
p.GET("/message/:mbox/:uid", func(ectx echo.Context) error {
|
p.GET("/message/:mbox/:uid", func(ectx echo.Context) error {
|
||||||
ctx := ectx.(*koushin.Context)
|
ctx := ectx.(*koushin.Context)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<h1>koushin</h1>
|
<h1>koushin</h1>
|
||||||
|
|
||||||
<form method="post" action="/login">
|
<form method="post" action="">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" name="username" id="username"/>
|
<input type="text" name="username" id="username"/>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
|
|
||||||
<h2>{{.Mailbox.Name}}</h2>
|
<h2>{{.Mailbox.Name}}</h2>
|
||||||
|
|
||||||
|
<form method="post" action="">
|
||||||
|
<input type="search" name="query" value="{{.Query}}">
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</form>
|
||||||
|
|
||||||
<p>Mailboxes:</p>
|
<p>Mailboxes:</p>
|
||||||
<ul>
|
<ul>
|
||||||
{{range .Mailboxes}}
|
{{range .Mailboxes}}
|
||||||
|
|
Loading…
Reference in a new issue