1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
// "THE BEER-WARE LICENSE" (Revision 42):
// <tobias.rehbein@web.de> wrote this file. As long as you retain this notice
// you can do whatever you want with this stuff. If we meet some day, and you
// think this stuff is worth it, you can buy me a beer in return.
// Tobias Rehbein
package mbox
import (
"bufio"
"bytes"
"errors"
"io"
"io/ioutil"
)
// ErrInvalidFormat is the error returned by the NextMessage method of Reader if
// its content is malformed in a way that it is not possible to extract a
// message.
var ErrInvalidFormat = errors.New("invalid mbox format")
type messageReader struct {
r *bufio.Reader
next bytes.Buffer
atEOF, atSeparator bool
atMiddleOfLine bool
}
func (mr *messageReader) Read(p []byte) (int, error) {
if mr.atEOF || mr.atSeparator {
return 0, io.EOF
}
if mr.next.Len() == 0 {
b, isPrefix, err := mr.r.ReadLine()
if err != nil {
mr.atEOF = true
return 0, err
}
if !mr.atMiddleOfLine {
if bytes.HasPrefix(b, header) {
mr.atSeparator = true
return 0, io.EOF
} else if len(b) == 0 {
// Check if the next line is separator. In such case the new
// line should not be written to not have double new line.
b, isPrefix, err = mr.r.ReadLine()
if err != nil {
mr.atEOF = true
return 0, err
}
if bytes.HasPrefix(b, header) {
mr.atSeparator = true
return 0, io.EOF
}
mr.next.Write([]byte("\r\n"))
}
if bytes.HasPrefix(b, escapedHeader) {
b = b[1:]
}
}
mr.next.Write(b)
if !isPrefix {
mr.next.Write([]byte("\r\n"))
}
mr.atMiddleOfLine = isPrefix
}
return mr.next.Read(p)
}
// Reader reads an mbox archive.
type Reader struct {
r *bufio.Reader
mr *messageReader
}
// NewReader returns a new Reader to read messages from mbox file format data
// provided by io.Reader r.
func NewReader(r io.Reader) *Reader {
return &Reader{r: bufio.NewReader(r)}
}
// NextMessage returns the next message text (containing both the header and the
// body). It will return io.EOF if there are no messages left.
func (r *Reader) NextMessage() (io.Reader, error) {
if r.mr == nil {
for {
b, isPrefix, err := r.r.ReadLine()
if err != nil {
return nil, err
}
isFromLine := bytes.HasPrefix(b, header)
// Discard the rest of the line.
for isPrefix {
_, isPrefix, err = r.r.ReadLine()
if err != nil {
return nil, err
}
}
if len(b) == 0 {
continue
}
if isFromLine {
break
} else {
return nil, ErrInvalidFormat
}
}
} else {
if _, err := io.Copy(ioutil.Discard, r.mr); err != nil {
return nil, err
}
if r.mr.atEOF {
return nil, io.EOF
}
}
r.mr = &messageReader{r: r.r}
return r.mr, nil
}
|