diff --git a/internal/imapserver/mailbox.go b/internal/imapserver/mailbox.go index 7c805a4..49459cc 100644 --- a/internal/imapserver/mailbox.go +++ b/internal/imapserver/mailbox.go @@ -11,6 +11,7 @@ import ( "github.com/emersion/go-imap" "github.com/emersion/go-imap/backend/backendutil" "github.com/emersion/go-message/textproto" + "github.com/neilalexander/yggmail/internal/storage/types" ) type Mailbox struct { @@ -115,17 +116,17 @@ func (mbox *Mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.Fe } for _, id := range ids { - mseq, mid, body, seen, answered, flagged, deleted, datetime, err := mbox.backend.Storage.MailSelect(mbox.name, int(id)) + mseq, mail, err := mbox.backend.Storage.MailSelect(mbox.name, int(id)) if err != nil { continue } fetched := imap.NewMessage(uint32(id), items) fetched.SeqNum = uint32(mseq) - fetched.Uid = uint32(mid) + fetched.Uid = uint32(mail.ID) get := func() (io.Reader, textproto.Header, error) { - bodyreader := bufio.NewReader(bytes.NewReader(body)) + bodyreader := bufio.NewReader(bytes.NewReader(mail.Mail)) hdr, err := textproto.ReadHeader(bodyreader) if err != nil { return nil, textproto.Header{}, fmt.Errorf("textproto.ReadHeader: %w", err) @@ -155,24 +156,24 @@ func (mbox *Mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.Fe case imap.FetchFlags: fetched.Flags = []string{} - if seen { + if mail.Seen { fetched.Flags = append(fetched.Flags, "\\Seen") } - if answered { + if mail.Answered { fetched.Flags = append(fetched.Flags, "\\Answered") } - if flagged { + if mail.Flagged { fetched.Flags = append(fetched.Flags, "\\Flagged") } - if deleted { + if mail.Deleted { fetched.Flags = append(fetched.Flags, "\\Deleted") } case imap.FetchInternalDate: - fetched.InternalDate = datetime + fetched.InternalDate = mail.Date case imap.FetchRFC822Size: - fetched.Size = uint32(len(body)) + fetched.Size = uint32(len(mail.Mail)) case imap.FetchUid: fetched.Uid = uint32(id) @@ -241,11 +242,10 @@ func (mbox *Mailbox) UpdateMessagesFlags(uid bool, seqSet *imap.SeqSet, op imap. } for _, id := range ids { - var seen, answered, flagged, deleted bool - var mid int + var mail *types.Mail if op != imap.SetFlags { var err error - _, mid, _, seen, answered, flagged, deleted, _, err = mbox.backend.Storage.MailSelect(mbox.name, int(id)) + _, mail, err = mbox.backend.Storage.MailSelect(mbox.name, int(id)) if err != nil { return fmt.Errorf("mbox.backend.Storage.MailSelect: %w", err) } @@ -253,18 +253,19 @@ func (mbox *Mailbox) UpdateMessagesFlags(uid bool, seqSet *imap.SeqSet, op imap. for _, flag := range flags { switch flag { case "\\Seen": - seen = op != imap.RemoveFlags + mail.Seen = op != imap.RemoveFlags case "\\Answered": - answered = op != imap.RemoveFlags + mail.Answered = op != imap.RemoveFlags case "\\Flagged": - flagged = op != imap.RemoveFlags + mail.Flagged = op != imap.RemoveFlags case "\\Deleted": - deleted = op != imap.RemoveFlags + mail.Deleted = op != imap.RemoveFlags } } if err := mbox.backend.Storage.MailUpdateFlags( - mbox.name, int(mid), seen, answered, flagged, deleted, + mbox.name, int(mail.ID), mail.Seen, + mail.Answered, mail.Flagged, mail.Deleted, ); err != nil { return err } @@ -283,15 +284,17 @@ func (mbox *Mailbox) CopyMessages(uid bool, seqSet *imap.SeqSet, destName string } for _, id := range ids { - _, _, body, seen, answered, flagged, deleted, _, err := mbox.backend.Storage.MailSelect(mbox.name, int(id)) + _, mail, err := mbox.backend.Storage.MailSelect(mbox.name, int(id)) if err != nil { return fmt.Errorf("mbox.backend.Storage.MailSelect: %w", err) } - pid, err := mbox.backend.Storage.MailCreate(destName, body) + pid, err := mbox.backend.Storage.MailCreate(destName, mail.Mail) if err != nil { return fmt.Errorf("mbox.backend.Storage.MailCreate: %w", err) } - if err = mbox.backend.Storage.MailUpdateFlags(mbox.name, pid, seen, answered, flagged, deleted); err != nil { + if err = mbox.backend.Storage.MailUpdateFlags( + mbox.name, pid, mail.Seen, mail.Answered, mail.Flagged, mail.Deleted, + ); err != nil { return fmt.Errorf("mbox.backend.Storage.MailUpdateFlags: %w", err) } } diff --git a/internal/storage/sqlite3/table_mails.go b/internal/storage/sqlite3/table_mails.go index 57d9378..5549cef 100644 --- a/internal/storage/sqlite3/table_mails.go +++ b/internal/storage/sqlite3/table_mails.go @@ -4,6 +4,8 @@ import ( "database/sql" "fmt" "time" + + "github.com/neilalexander/yggmail/internal/storage/types" ) type TableMails struct { @@ -35,7 +37,6 @@ const mailsSchema = ` FOREIGN KEY (mailbox) REFERENCES mailboxes(mailbox) ON DELETE CASCADE ON UPDATE CASCADE ); - DROP VIEW IF EXISTS inboxes; CREATE VIEW IF NOT EXISTS inboxes AS SELECT * FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY mailbox) AS seq, * FROM mails ) @@ -160,13 +161,14 @@ func (t *TableMails) MailCreate(mailbox string, data []byte) (int, error) { return id, err } -func (t *TableMails) MailSelect(mailbox string, id int) (int, int, []byte, bool, bool, bool, bool, time.Time, error) { - var data []byte - var seen, answered, flagged, deleted bool - var ts int64 - var seq, pid int - err := t.selectMail.QueryRow(mailbox, id).Scan(&seq, &pid, &data, &ts, &seen, &answered, &flagged, &deleted) - return seq, pid, data, seen, answered, flagged, deleted, time.Unix(ts, 0), err +func (t *TableMails) MailSelect(mailbox string, id int) (int, *types.Mail, error) { + var seq int + mail := &types.Mail{} + err := t.selectMail.QueryRow(mailbox, id).Scan( + &seq, &mail.ID, &mail.Mail, &mail.Date, + &mail.Seen, &mail.Answered, &mail.Flagged, &mail.Deleted, + ) + return seq, mail, err } func (t *TableMails) MailSearch(mailbox string) ([]uint32, error) { diff --git a/internal/storage/storage.go b/internal/storage/storage.go index a71bccf..24c6298 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -1,6 +1,6 @@ package storage -import "time" +import "github.com/neilalexander/yggmail/internal/storage/types" type Storage interface { ConfigGet(key string) (string, error) @@ -19,7 +19,7 @@ type Storage interface { MailboxSubscribe(name string, subscribed bool) error MailCreate(mailbox string, data []byte) (int, error) - MailSelect(mailbox string, id int) (int, int, []byte, bool, bool, bool, bool, time.Time, error) + MailSelect(mailbox string, id int) (int, *types.Mail, error) MailSearch(mailbox string) ([]uint32, error) MailUpdateFlags(mailbox string, id int, seen, answered, flagged, deleted bool) error MailDelete(mailbox, id string) error diff --git a/internal/storage/types/types.go b/internal/storage/types/types.go new file mode 100644 index 0000000..97490d2 --- /dev/null +++ b/internal/storage/types/types.go @@ -0,0 +1,14 @@ +package types + +import "time" + +type Mail struct { + Mailbox string + ID int + Mail []byte + Date time.Time + Seen bool + Answered bool + Flagged bool + Deleted bool +}