@@ 1,15 1,15 @@
package mailbox
import (
- "bufio"
+ "bytes"
"errors"
+ "fmt"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/mail"
"os"
- "path"
"strconv"
"strings"
@@ 60,7 60,7 @@ func Store(mb Mailbox, msg io.Reader, fo
}
func partError(no int, msg string) error {
- return errors.New("part " + strconv.Itoa(no) + ": " + msg)
+ return errors.New("mime part " + strconv.Itoa(no) + ": " + msg)
}
func Save(files chan string, msg *mail.Message, number int) error {
@@ 76,14 76,11 @@ func Save(files chan string, msg *mail.M
if len(filename) == 0 {
return partError(no, "no filename")
}
- basename := path.Base(filename)
- if len(basename) == 0 {
- return partError(no, "invalid filename '"+filename+"'")
- }
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
return partError(no, err.Error())
}
+ defer f.Close()
in, err := mimeDecoder(p)
if err != nil {
return partError(no, err.Error())
@@ 91,12 88,13 @@ func Save(files chan string, msg *mail.M
if _, err = io.Copy(f, in); err != nil {
return partError(no, err.Error())
}
- files <- basename
- return nil
+ files <- f.Name()
+ return f.Close()
}
return parse(msg, saver)
}
+// todo: print something like "inbox/33:1 <header>"?
func Parts(parts chan string, msg *mail.Message) error {
partsLister := func(p part, no int) error {
parts <- p.Header.Get("Content-Type")
@@ 122,9 120,10 @@ func ViewPart(dst io.Writer, msg *mail.M
func newPlainTextPrinter(dst io.Writer, t transform.Transformer) func(part, int) error {
return func(p part, no int) error {
+ // todo: if the msg has no content type header, this will return "mime: no media type"
mt, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
if err != nil {
- return err
+ return errors.New("parsing media type: " + err.Error())
}
if strings.HasPrefix(mt, "text/plain") {
in, err := mimeDecoder(p)
@@ 134,8 133,9 @@ func newPlainTextPrinter(dst io.Writer,
_, err = io.Copy(dst, transform.NewReader(in, t))
return err
}
- io.WriteString(dst, "[MIME Part "+strconv.Itoa(no)+": ")
- io.WriteString(dst, p.Header.Get("Content-Type")+"]\n ")
+ if _, err = fmt.Fprintf(dst, "[MIME Part %d: %s]\n", strconv.Itoa(no), p.Header.Get("Content-Type")); err != nil {
+ return err
+ }
// process the stream (by throwing it away), as it is replaced by the string above
_, err = io.Copy(ioutil.Discard, p.Body)
return err
@@ 145,13 145,13 @@ func newPlainTextPrinter(dst io.Writer,
// if the mail has only one header line without a nl at the end, "mailbox: EOF"
// is thrown
func View(dst io.Writer, msg *mail.Message) error {
- out := bufio.NewWriter(dst)
- defer out.Flush()
dec := newDecoder()
+ var b bytes.Buffer
for _, key := range viewHeader {
- out.WriteString(key + ": " + dec.safeDecodeHeader(msg.Header.Get(key)) + "\n")
+ b.WriteString(key + ": " + dec.safeDecodeHeader(msg.Header.Get(key)) + "\n")
}
- // bug: if we dont have a mime msg, this will return "mime: no media type"
- // so we should first check if we have a mime msg
- return parse(msg, newPlainTextPrinter(out, transform.Nop))
+ if _, err := io.Copy(dst, &b); err != nil {
+ return err
+ }
+ return parse(msg, newPlainTextPrinter(dst, transform.Nop))
}
@@ 219,8 219,8 @@ func forwardMsg(dst io.Writer, from stri
if ct := msg.Header.Get("Content-Type"); len(ct) > 0 {
fmt.Fprintf(&b, "Content-Type: %s\n", ct)
}
- fmt.Fprintf(&b, "\n\n\nBegin forwarded message:\n\n\n"+
- "Date: %s\nFrom: %s\nTo: %s\nCc: %s\nSubject: %s\n\n", dec.safeDecodeHeader(msg.Header.Get("Date")),
+ b.WriteString("\n\n\nBegin forwarded message:\n\n\n")
+ fmt.Fprintf(&b, "Date: %s\nFrom: %s\nTo: %s\nCc: %s\nSubject: %s\n\n", dec.safeDecodeHeader(msg.Header.Get("Date")),
parseAddressList(msg.Header.Get("From")), parseAddressList(msg.Header.Get("To")),
parseAddressList(msg.Header.Get("Cc")), subject)
@@ 269,22 269,22 @@ func parseAddressList(list string) strin
if len(list) == 0 {
return ""
}
-
addrList, err := mail.ParseAddressList(list)
if err != nil {
return "Error: " + err.Error()
}
- var b bytes.Buffer
+ var s string
for i := range addrList {
if i > 0 {
- b.WriteString(", ")
+ s += ", "
}
- b.WriteString(addrList[i].Name)
- b.WriteString(" <")
- b.WriteString(addrList[i].Address)
- b.WriteString(">")
+ sa := addrList[i].Address
+ if sn := addrList[i].Name; len(sn) > 0 {
+ sa = sn + " <" + sa + ">"
+ }
+ s += sa
}
- return b.String()
+ return s
}
func readIntoMimeMessage(src io.Reader) (*mail.Message, error) {
@@ 306,17 306,17 @@ func readIntoMimeMessage(src io.Reader)
// h.Write never returns an error
io.WriteString(h, strconv.Itoa(time.Now().Nanosecond()))
b = fmt.Sprintf("%x", h.Sum(nil))
-
- mhdr := strings.NewReader(fmt.Sprintf("\r\n--%s\r\n"+
+ var mhdr bytes.Buffer
+ fmt.Fprintf(&mhdr, "\r\n--%s\r\n"+
"Content-Type: %s\r\n"+
"Content-Transfer-Encoding: %s\r\n"+
"Content-Disposition: inline\r\n\r\n",
- b, hdr.Get("Content-Type"), hdr.Get("Content-Transfer-Encoding")))
+ b, hdr.Get("Content-Type"), hdr.Get("Content-Transfer-Encoding"))
hdr.Del("Content-Transfer-Encoding")
hdr.Set("Content-Type", "multipart/mixed; boundary="+b)
hdr.Set("Mime-Version", "1.0")
- return &mail.Message{mail.Header(hdr), io.MultiReader(mhdr, src)}, nil
+ return &mail.Message{mail.Header(hdr), io.MultiReader(&mhdr, src)}, nil
}
func boundary(hdr textproto.MIMEHeader) (string, error) {
@@ 342,7 342,7 @@ func Attach(mbox Mailbox, dst io.Writer,
}
buf := bufio.NewWriter(dst)
defer buf.Flush()
- // todo: writeHeader uses a buffer by itself
+
if err = writeHeader(buf, msg.Header); err != nil {
return fmt.Errorf("writing msg header: %s", err)
}
@@ 410,7 410,7 @@ func (w *lineWriter) Write(b []byte) (in
}
for len(b) > w.free {
n += write(b[:w.free])
- // don't count the \r\n, these bytes do not come from b
+ // don't count \r\n, these bytes do not come from b
write([]byte("\r\n"))
b = b[w.free:]
w.free = w.maxwidth