"errors"
"fmt"
"io"
+ "io/ioutil"
"net"
"os"
+ "path"
"src.bluestatic.org/mailpopbox/pop3"
)
}
func (server *pop3Server) openMailbox(maildrop string) (*mailbox, error) {
+ files, err := ioutil.ReadDir(maildrop)
+ if err != nil {
+ // TODO: hide error, log instead
+ return nil, err
+ }
+
mb := &mailbox{
- messages: make([]message, 0),
+ messages: make([]message, 0, len(files)),
+ }
+
+ i := 0
+ for _, file := range files {
+ if file.IsDir() {
+ continue
+ }
+
+ msg := message{
+ filename: path.Join(maildrop, file.Name()),
+ index: i,
+ size: file.Size(),
+ }
+ mb.messages = append(mb.messages, msg)
+ i++
}
+
return mb, nil
}
type message struct {
filename string
index int
- size int
+ size int64
deleted bool
}
}
func (m message) Size() int {
- return m.size
+ return int(m.size)
+}
+
+func (m message) Deleted() bool {
+ return m.deleted
}
func (mb *mailbox) ListMessages() ([]pop3.Message, error) {
return msgs, nil
}
-func (mb *mailbox) Retrieve(msg pop3.Message) (io.ReadCloser, error) {
- filename := msg.(*message).filename
+func (mb *mailbox) Retrieve(idx int) (io.ReadCloser, error) {
+ if idx > len(mb.messages) {
+ return nil, errors.New("no such message")
+ }
+ filename := mb.messages[idx-1].filename
return os.Open(filename)
}
-func (mb *mailbox) Delete(msg pop3.Message) error {
- message := msg.(*message)
+func (mb *mailbox) Delete(idx int) error {
+ message := &mb.messages[idx-1]
if message.deleted {
return errors.New("already deleted")
}
import (
"fmt"
+ "io"
"net"
"net/textproto"
"strings"
conn.err(errStateTxn)
return
}
+
+ msgs, err := conn.mb.ListMessages()
+ if err != nil {
+ conn.err(err.Error())
+ return
+ }
+
+ size := 0
+ num := 0
+ for _, msg := range msgs {
+ if msg.Deleted() {
+ continue
+ }
+ size += msg.Size()
+ num++
+ }
+
+ conn.ok(fmt.Sprintf("%d %d", num, size))
}
func (conn *connection) doLIST() {
conn.err(errStateTxn)
return
}
+
+ msgs, err := conn.mb.ListMessages()
+ if err != nil {
+ conn.err(err.Error())
+ return
+ }
+
+ conn.ok("scan listing")
+ for _, msg := range msgs {
+ conn.tp.PrintfLine("%d %d", msg.ID(), msg.Size())
+ }
+ conn.tp.PrintfLine(".")
}
func (conn *connection) doRETR() {
conn.err(errStateTxn)
return
}
+
+ idx, ok := conn.intParam()
+ if !ok {
+ return
+ }
+
+ rc, err := conn.mb.Retrieve(idx)
+ if err != nil {
+ conn.err(err.Error())
+ return
+ }
+
+ w := conn.tp.DotWriter()
+ io.Copy(w, rc)
+ w.Close()
}
func (conn *connection) doDELE() {
conn.err(errStateTxn)
return
}
+
+ idx, ok := conn.intParam()
+ if !ok {
+ return
+ }
+
+ if err := conn.mb.Delete(idx); err != nil {
+ conn.err(err.Error())
+ } else {
+ conn.ok("")
+ }
}
func (conn *connection) doRSET() {
conn.mb.Reset()
conn.ok("")
}
+
+func (conn *connection) intParam() (int, bool) {
+ var cmd string
+ var param int
+ if _, err := fmt.Sscanf(conn.line, "%s %d", &cmd, ¶m); err != nil {
+ conn.err(errSyntax)
+ return 0, false
+ }
+ return param, true
+}