-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmessage.go
183 lines (167 loc) · 5.43 KB
/
message.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// 2019, Georg Sauthoff <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later
package main
import (
"bytes"
"io"
)
func write_messageP(in io.Reader, h io.WriteCloser, b io.WriteCloser,
m io.WriteCloser, pedantic bool) error {
sw := new_split_message_writer(h, b, m)
// use small read size for testing
block := make([]byte, read_size)
for {
block := block[:cap(block)]
n, err := in.Read(block)
if err != nil && err != io.EOF {
return err
}
block = block[:n]
if n == 0 {
break
}
if _, err := sw.Write(block); err != nil {
debugf("write_message split_message_writer write failed: %v", err)
if pedantic {
return err
} else {
// when classifying messages it makes sense to ignore write errors
// since we want to work with what we already have
// (think: a decoding error in some mime-attachment was preceded
// by many spammy words in the header/other parts)
return nil
}
}
}
if err := sw.Close(); err != nil {
return err
}
return nil
}
func write_message(in io.Reader, h io.WriteCloser, b io.WriteCloser,
m io.WriteCloser) error {
return write_messageP(in, h, b, m, false)
}
func new_content_writer(typ []byte, boundary []byte, houtP io.WriteCloser, boutP io.WriteCloser, moutP io.WriteCloser) io.WriteCloser {
if bytes.HasPrefix(typ, []byte("multipart")) {
w := new_multipart_split_writer(boundary, func() io.WriteCloser {
return new_split_message_writer(moutP, boutP, moutP)
})
return w
} else if bytes.Equal(typ, []byte("text/plain")) {
return boutP
} else if bytes.Equal(typ, []byte("text/html")) {
return new_remove_tags_writer(new_replace_entities_writer(
new_shrink_space_writer(boutP)))
//return new_remove_tags_writer((boutP))
}
return new_dev_null_writer()
}
type split_message_writer struct {
houtP io.WriteCloser
boutP io.WriteCloser
moutP io.WriteCloser
hout io.WriteCloser
bout io.WriteCloser
eh *extract_header_writer
m split_machine
}
func new_split_message_writer(houtP io.WriteCloser, boutP io.WriteCloser,
moutP io.WriteCloser) *split_message_writer {
w := new(split_message_writer)
w.houtP = new_keep_open_writer(houtP)
w.boutP = new_keep_open_writer(boutP)
w.moutP = new_keep_open_writer(moutP)
w.eh = new_extract_header_writer()
w.hout = new_unfold_writer(
newMultiWriteCloser(w.eh, new_header_decode_writer(w.houtP)))
return w
}
func (w *split_message_writer) Write(block []byte) (int, error) {
n := len(block)
for len(block) != 0 {
action, bs, bl := w.m.next(block)
block = bl
switch action {
case split_machine_WRITE_HEADER:
if _, err := w.hout.Write(bs); err != nil {
return 0, err
}
case split_machine_SETUP_BODY:
if err := w.hout.Close(); err != nil {
return 0, err
}
w.eh.parse()
debugf("parsed: content type |%s| type |%s| charset |%s|" +
" encoding |%s| boundary |%s|\n",
w.eh.content_type, w.eh.typ, w.eh.charset,
w.eh.transfer_encoding, w.eh.boundary)
w.bout = new_transfer_decode_writer(w.eh.transfer_encoding,
new_charset_writer(w.eh.charset,
new_content_writer(w.eh.typ, w.eh.boundary,
w.houtP, w.boutP, w.moutP)))
case split_machine_WRITE_BODY:
if _, err := w.bout.Write(bs); err != nil {
return 0, err
}
case split_machine_MORE:
;
}
}
return n, nil
}
func (w *split_message_writer) Close() error {
if w.bout != nil {
return w.bout.Close()
}
return nil
}
const (
split_machine_WRITE_HEADER = iota
split_machine_SETUP_BODY
split_machine_WRITE_BODY
split_machine_MORE
)
type split_machine struct {
state int
}
func (w *split_machine) next(block []byte) (int, []byte, []byte) {
const ( IN_HEADER = iota
IN_DELIM
IN_BODY_SETUP
IN_BODY
)
switch w.state {
case IN_HEADER:
i := bytes.Index(block, []byte("\n\n"))
if i == -1 {
if block[len(block)-1] == byte('\n') {
w.state = IN_DELIM
}
r := block
block = block[:0]
return split_machine_WRITE_HEADER, r, block
} else {
r := block[:i+1]
block = block[i+2:]
w.state = IN_BODY_SETUP
return split_machine_WRITE_HEADER, r, block
}
case IN_DELIM:
if block[0] == byte('\n') {
block = block[1:]
w.state = IN_BODY_SETUP
} else {
w.state = IN_HEADER
}
return split_machine_MORE, nil, block
case IN_BODY_SETUP:
w.state = IN_BODY
return split_machine_SETUP_BODY, nil, block
case IN_BODY:
r := block
block = block[:0]
return split_machine_WRITE_BODY, r, block
}
return split_machine_MORE, nil, block
}