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 128 129 130 131 132 133 134 135 136 137 138 139
|
// "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
lastDelimiter []byte
}
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
mr.lastDelimiter = b
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
mr.lastDelimiter = b
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
lastDelimiter []byte
}
// 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 {
r.lastDelimiter = append([]byte{}, b...)
break
} else {
return nil, ErrInvalidFormat
}
}
} else {
if _, err := io.Copy(ioutil.Discard, r.mr); err != nil {
return nil, err
}
r.lastDelimiter = append([]byte{}, r.mr.lastDelimiter...)
if r.mr.atEOF {
return nil, io.EOF
}
}
r.mr = &messageReader{r: r.r}
return r.mr, nil
}
// GetMessageDelimiter returns delimiter in mbox file after message was loaded.
// Otherwise empty.
func (r *Reader) GetMessageDelimiter() []byte {
return r.lastDelimiter
}
|