1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.james.mime4j.parser;
21
22 import org.apache.james.mime4j.descriptor.BodyDescriptor;
23 import org.apache.james.mime4j.parser.AbstractContentHandler;
24 import org.apache.james.mime4j.parser.MimeStreamParser;
25 import org.apache.james.mime4j.util.ByteSequence;
26 import org.apache.james.mime4j.util.ContentUtil;
27 import org.apache.log4j.BasicConfigurator;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.LinkedList;
33
34 import junit.framework.TestCase;
35
36 public class MimeStreamParserTest extends TestCase {
37
38 @Override
39 public void setUp() {
40 BasicConfigurator.resetConfiguration();
41 BasicConfigurator.configure();
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 public void testBoundaryInEpilogue() throws Exception {
88 StringBuilder sb = new StringBuilder();
89 sb.append("From: foo@bar.com\r\n");
90 sb.append("To: someone@else.com\r\n");
91 sb.append("Content-type: multipart/something; boundary=myboundary\r\n");
92 sb.append("\r\n");
93 sb.append("This is the preamble.\r\n");
94 sb.append("--myboundary\r\n");
95 sb.append("Content-type: text/plain\r\n");
96 sb.append("\r\n");
97 sb.append("This is the first body.\r\n");
98 sb.append("It's completely meaningless.\r\n");
99 sb.append("After this line the body ends.\r\n");
100 sb.append("\r\n");
101 sb.append("--myboundary--\r\n");
102
103 StringBuilder epilogue = new StringBuilder();
104 epilogue.append("Content-type: text/plain\r\n");
105 epilogue.append("\r\n");
106 epilogue.append("This is actually the epilogue but it looks like a second body.\r\n");
107 epilogue.append("Yada yada yada.\r\n");
108 epilogue.append("\r\n");
109 epilogue.append("--myboundary--\r\n");
110 epilogue.append("This is still the epilogue.\r\n");
111
112 sb.append(epilogue.toString());
113
114 ByteArrayInputStream bais = new ByteArrayInputStream(sb.toString().getBytes("US-ASCII"));
115
116 final StringBuilder actual = new StringBuilder();
117
118 MimeStreamParser parser = new MimeStreamParser();
119 parser.setContentHandler(new AbstractContentHandler() {
120 @Override
121 public void epilogue(InputStream is) throws IOException {
122 int b;
123 while ((b = is.read()) != -1) {
124 actual.append((char) b);
125 }
126 }
127 });
128 parser.parse(bais);
129
130 assertEquals(epilogue.toString(), actual.toString());
131 }
132
133 public void testParseOneLineFields() throws Exception {
134 StringBuilder sb = new StringBuilder();
135 final LinkedList<String> expected = new LinkedList<String>();
136 expected.add("From: foo@abr.com");
137 sb.append(expected.getLast() + "\r\n");
138 expected.add("Subject: A subject");
139 sb.append(expected.getLast() + "\r\n");
140
141 MimeStreamParser parser = new MimeStreamParser();
142 parser.setContentHandler(new AbstractContentHandler() {
143 @Override
144 public void field(Field field) {
145 assertEquals(expected.removeFirst(), decode(field.getRaw()));
146 }
147 });
148
149 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
150
151 assertEquals(0, expected.size());
152 }
153
154 public void testCRWithoutLFInHeader() throws Exception {
155
156
157
158
159 StringBuilder sb = new StringBuilder();
160 final LinkedList<String> expected = new LinkedList<String>();
161 expected.add("The-field: This field\r\rcontains CR:s\r\r"
162 + "not\r\n\tfollowed by LF");
163 sb.append(expected.getLast() + "\r\n");
164
165 MimeStreamParser parser = new MimeStreamParser();
166 parser.setContentHandler(new AbstractContentHandler() {
167 @Override
168 public void field(Field field) {
169 assertEquals(expected.removeFirst(), decode(field.getRaw()));
170 }
171 });
172
173 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
174
175 assertEquals(0, expected.size());
176 }
177
178 public void testParseMultiLineFields() throws Exception {
179 StringBuilder sb = new StringBuilder();
180 final LinkedList<String> expected = new LinkedList<String>();
181 expected.add("Received: by netmbx.netmbx.de (/\\==/\\ Smail3.1.28.1)\r\n"
182 + "\tfrom mail.cs.tu-berlin.de with smtp\r\n"
183 + "\tid <m0uWPrO-0004wpC>;"
184 + " Wed, 19 Jun 96 18:12 MES");
185 sb.append(expected.getLast() + "\r\n");
186 expected.add("Subject: A folded subject\r\n Line 2\r\n\tLine 3");
187 sb.append(expected.getLast() + "\r\n");
188
189 MimeStreamParser parser = new MimeStreamParser();
190 parser.setContentHandler(new AbstractContentHandler() {
191 @Override
192 public void field(Field field) {
193 assertEquals(expected.removeFirst(), decode(field.getRaw()));
194 }
195 });
196
197 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
198
199 assertEquals(0, expected.size());
200 }
201
202 public void testStop() throws Exception {
203 final MimeStreamParser parser = new MimeStreamParser();
204 TestHandler handler = new TestHandler() {
205 @Override
206 public void endHeader() {
207 super.endHeader();
208 parser.stop();
209 }
210 };
211 parser.setContentHandler(handler);
212
213 String msg = "Subject: Yada yada\r\n"
214 + "From: foo@bar.com\r\n"
215 + "\r\n"
216 + "Line 1\r\n"
217 + "Line 2\r\n";
218 String expected = "<message>\r\n"
219 + "<header>\r\n"
220 + "<field>\r\n"
221 + "Subject: Yada yada"
222 + "</field>\r\n"
223 + "<field>\r\n"
224 + "From: foo@bar.com"
225 + "</field>\r\n"
226 + "</header>\r\n"
227 + "<body>\r\n"
228 + "</body>\r\n"
229 + "</message>\r\n";
230
231 parser.parse(new ByteArrayInputStream(msg.getBytes()));
232 String result = handler.sb.toString();
233
234 assertEquals(expected, result);
235 }
236
237
238
239
240 public void testInvalidFields() throws Exception {
241 StringBuilder sb = new StringBuilder();
242 final LinkedList<String> expected = new LinkedList<String>();
243 sb.append("From - foo@abr.com\r\n");
244 expected.add("From: some@one.com");
245 sb.append(expected.getLast() + "\r\n");
246 expected.add("Subject: A subject");
247 sb.append(expected.getLast() + "\r\n");
248 sb.append("A line which should be ignored\r\n");
249
250 MimeStreamParser parser = new MimeStreamParser();
251 parser.setContentHandler(new AbstractContentHandler() {
252 @Override
253 public void field(Field field) {
254 assertEquals(expected.removeFirst(), decode(field.getRaw()));
255 }
256 });
257
258 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
259
260 assertEquals(0, expected.size());
261 }
262
263
264
265
266 public void testEmptyStream() throws Exception {
267 final LinkedList<String> expected = new LinkedList<String>();
268 expected.add("startMessage");
269 expected.add("startHeader");
270 expected.add("endHeader");
271 expected.add("body");
272 expected.add("endMessage");
273
274 MimeStreamParser parser = new MimeStreamParser();
275 parser.setContentHandler(new AbstractContentHandler() {
276 @Override
277 public void body(BodyDescriptor bd, InputStream is) {
278 assertEquals(expected.removeFirst(), "body");
279 }
280
281 @Override
282 public void endMultipart() {
283 fail("endMultipart shouldn't be called for empty stream");
284 }
285
286 @Override
287 public void endBodyPart() {
288 fail("endBodyPart shouldn't be called for empty stream");
289 }
290
291 @Override
292 public void endHeader() {
293 assertEquals(expected.removeFirst(), "endHeader");
294 }
295
296 @Override
297 public void endMessage() {
298 assertEquals(expected.removeFirst(), "endMessage");
299 }
300
301 @Override
302 public void field(Field field) {
303 fail("field shouldn't be called for empty stream");
304 }
305
306 @Override
307 public void startMultipart(BodyDescriptor bd) {
308 fail("startMultipart shouldn't be called for empty stream");
309 }
310
311 @Override
312 public void startBodyPart() {
313 fail("startBodyPart shouldn't be called for empty stream");
314 }
315
316 @Override
317 public void startHeader() {
318 assertEquals(expected.removeFirst(), "startHeader");
319 }
320
321 @Override
322 public void startMessage() {
323 assertEquals(expected.removeFirst(), "startMessage");
324 }
325 });
326
327 parser.parse(new ByteArrayInputStream(new byte[0]));
328
329 assertEquals(0, expected.size());
330 }
331
332
333
334
335 public void testEmpyHeader() throws Exception {
336 StringBuilder sb = new StringBuilder();
337 sb.append("\r\n");
338 sb.append("The body is right here\r\n");
339
340 final StringBuilder body = new StringBuilder();
341
342 MimeStreamParser parser = new MimeStreamParser();
343 parser.setContentHandler(new AbstractContentHandler() {
344 @Override
345 public void field(Field field) {
346 fail("No fields should be reported");
347 }
348 @Override
349 public void body(BodyDescriptor bd, InputStream is) throws IOException {
350 int b;
351 while ((b = is.read()) != -1) {
352 body.append((char) b);
353 }
354 }
355 });
356
357 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
358
359 assertEquals("The body is right here\r\n", body.toString());
360 }
361
362
363
364
365 public void testEmptyBody() throws Exception {
366 StringBuilder sb = new StringBuilder();
367 final LinkedList<String> expected = new LinkedList<String>();
368 expected.add("From: some@one.com");
369 sb.append(expected.getLast() + "\r\n");
370 expected.add("Subject: A subject");
371 sb.append(expected.getLast() + "\r\n\r\n");
372
373 MimeStreamParser parser = new MimeStreamParser();
374 parser.setContentHandler(new AbstractContentHandler() {
375 @Override
376 public void field(Field field) {
377 assertEquals(expected.removeFirst(), decode(field.getRaw()));
378 }
379 @Override
380 public void body(BodyDescriptor bd, InputStream is) throws IOException {
381 assertEquals(-1, is.read());
382 }
383 });
384
385 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
386
387 assertEquals(0, expected.size());
388 }
389
390
391
392
393 public void testPrematureEOFAfterFields() throws Exception {
394 StringBuilder sb = new StringBuilder();
395 final LinkedList<String> expected = new LinkedList<String>();
396 expected.add("From: some@one.com");
397 sb.append(expected.getLast() + "\r\n");
398 expected.add("Subject: A subject");
399 sb.append(expected.getLast());
400
401 MimeStreamParser parser = new MimeStreamParser();
402 parser.setContentHandler(new AbstractContentHandler() {
403 @Override
404 public void field(Field field) {
405 assertEquals(expected.removeFirst(), decode(field.getRaw()));
406 }
407 });
408
409 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
410
411 assertEquals(0, expected.size());
412
413 sb = new StringBuilder();
414 expected.clear();
415 expected.add("From: some@one.com");
416 sb.append(expected.getLast() + "\r\n");
417 expected.add("Subject: A subject");
418 sb.append(expected.getLast() + "\r\n");
419
420 parser = new MimeStreamParser();
421 parser.setContentHandler(new AbstractContentHandler() {
422 @Override
423 public void field(Field field) {
424 assertEquals(expected.removeFirst(), decode(field.getRaw()));
425 }
426 });
427
428 parser.parse(new ByteArrayInputStream(sb.toString().getBytes()));
429
430 assertEquals(0, expected.size());
431 }
432
433 public void testAutomaticContentDecoding() throws Exception {
434 MimeStreamParser parser = new MimeStreamParser();
435 parser.setContentDecoding(true);
436 TestHandler handler = new TestHandler();
437 parser.setContentHandler(handler);
438
439 String msg = "Subject: Yada yada\r\n"
440 + "From: foo@bar.com\r\n"
441 + "Content-Type: application/octet-stream\r\n"
442 + "Content-Transfer-Encoding: base64\r\n"
443 + "\r\n"
444 + "V2hvIGF0ZSBteSBjYWtlPwo=";
445 String expected = "<message>\r\n"
446 + "<header>\r\n"
447 + "<field>\r\n"
448 + "Subject: Yada yada"
449 + "</field>\r\n"
450 + "<field>\r\n"
451 + "From: foo@bar.com"
452 + "</field>\r\n"
453 + "<field>\r\n"
454 + "Content-Type: application/octet-stream"
455 + "</field>\r\n"
456 + "<field>\r\n"
457 + "Content-Transfer-Encoding: base64"
458 + "</field>\r\n"
459 + "</header>\r\n"
460 + "<body>\r\n"
461 + "Who ate my cake?\n"
462 + "</body>\r\n"
463 + "</message>\r\n";
464
465 parser.parse(new ByteArrayInputStream(msg.getBytes()));
466 String result = handler.sb.toString();
467
468 assertEquals(expected, result);
469 }
470
471 protected String decode(ByteSequence byteSequence) {
472 return ContentUtil.decode(byteSequence);
473 }
474
475 }