-
Notifications
You must be signed in to change notification settings - Fork 19
/
reader.go
127 lines (110 loc) · 2.75 KB
/
reader.go
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
}