001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.core;
019:
020: import org.apache.avalon.framework.container.ContainerUtil;
021: import org.apache.mailet.RFC2822Headers;
022:
023: import javax.mail.BodyPart;
024: import javax.mail.Session;
025: import javax.mail.internet.InternetHeaders;
026: import javax.mail.internet.MimeBodyPart;
027: import javax.mail.internet.MimeMessage;
028: import javax.mail.internet.MimeMultipart;
029:
030: import java.io.ByteArrayInputStream;
031: import java.io.ByteArrayOutputStream;
032: import java.util.Arrays;
033: import java.util.Enumeration;
034: import java.util.Properties;
035:
036: import junit.framework.TestCase;
037:
038: /**
039: * Test the subject folding issue.
040: */
041: public class MimeMessageTest extends TestCase {
042:
043: protected MimeMessage getSimpleMessage() throws Exception {
044: MimeMessage mmCreated = new MimeMessage(Session
045: .getDefaultInstance(new Properties()));
046: mmCreated.setSubject("test");
047: mmCreated.setText("test body");
048: mmCreated.saveChanges();
049: return mmCreated;
050: }
051:
052: protected String getSimpleMessageCleanedSource() throws Exception {
053: return "Subject: test\r\n" + "MIME-Version: 1.0\r\n"
054: + "Content-Type: text/plain; charset=us-ascii\r\n"
055: + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n"
056: + "test body";
057: }
058:
059: protected MimeMessage getMessageWithBadReturnPath()
060: throws Exception {
061: MimeMessage mmCreated = new MimeMessage(Session
062: .getDefaultInstance(new Properties()));
063: mmCreated.setSubject("test");
064: mmCreated.setHeader(RFC2822Headers.RETURN_PATH,
065: "<mybadreturn@example.com>");
066: mmCreated.setText("test body");
067: mmCreated.saveChanges();
068: return mmCreated;
069: }
070:
071: protected String getMessageWithBadReturnPathSource()
072: throws Exception {
073: return "Subject: test\r\n"
074: + "Return-Path: <mybadreturn@example.com>\r\n"
075: + "MIME-Version: 1.0\r\n"
076: + "Content-Type: text/plain; charset=us-ascii\r\n"
077: + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n"
078: + "test body";
079: }
080:
081: protected String getSimpleMessageCleanedSourceHeaderExpected()
082: throws Exception {
083: return "X-Test: foo\r\n" + getSimpleMessageCleanedSource();
084: }
085:
086: /*
087: * Class under test for String getSubject()
088: */
089: public void testSimpleMessage() throws Exception {
090: assertEquals(getSimpleMessageCleanedSource(),
091: getCleanedMessageSource(getSimpleMessage()));
092: }
093:
094: protected MimeMessage getMultipartMessage() throws Exception {
095: MimeMessage mmCreated = new MimeMessage(Session
096: .getDefaultInstance(new Properties()));
097: mmCreated.setSubject("test");
098: MimeMultipart mm = new MimeMultipart("alternative");
099: mm.addBodyPart(new MimeBodyPart(new InternetHeaders(
100: new ByteArrayInputStream(
101: "X-header: test1\r\nContent-Type: text/plain; charset=Cp1252\r\n"
102: .getBytes())), "first part тащ"
103: .getBytes()));
104: mm
105: .addBodyPart(new MimeBodyPart(
106: new InternetHeaders(
107: new ByteArrayInputStream(
108: "X-header: test2\r\nContent-Type: text/plain; charset=Cp1252\r\nContent-Transfer-Encoding: quoted-printable\r\n"
109: .getBytes())),
110: "second part =E8=E8".getBytes()));
111: mmCreated.setContent(mm);
112: mmCreated.saveChanges();
113: return mmCreated;
114: }
115:
116: protected String getMultipartMessageSource() {
117: return "Subject: test\r\n"
118: + "MIME-Version: 1.0\r\n"
119: + "Content-Type: multipart/alternative; \r\n"
120: + "\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
121: + "\r\n" + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
122: + "X-header: test1\r\n"
123: + "Content-Type: text/plain; charset=Cp1252\r\n"
124: + "Content-Transfer-Encoding: quoted-printable\r\n"
125: + "\r\n" + "first part =E8\r\n"
126: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
127: + "X-header: test2\r\n"
128: + "Content-Type: text/plain; charset=Cp1252\r\n"
129: + "Content-Transfer-Encoding: quoted-printable\r\n"
130: + "\r\n" + "second part =E8=E8\r\n"
131: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
132: }
133:
134: protected String getMultipartMessageExpected1() {
135: return "Subject: test\r\n"
136: + "MIME-Version: 1.0\r\n"
137: + "Content-Type: multipart/alternative; \r\n"
138: + "\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
139: + "\r\n" + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
140: + "X-header: test1\r\n"
141: + "Content-Type: text/plain; charset=Cp1252\r\n"
142: + "Content-Transfer-Encoding: quoted-printable\r\n"
143: + "\r\n" + "test=80\r\n"
144: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
145: + "X-header: test2\r\n"
146: + "Content-Type: text/plain; charset=Cp1252\r\n"
147: + "Content-Transfer-Encoding: quoted-printable\r\n"
148: + "\r\n" + "second part =E8=E8\r\n"
149: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
150: }
151:
152: protected String getMultipartMessageExpected2() {
153: return "Subject: test\r\n"
154: + "MIME-Version: 1.0\r\n"
155: + "Content-Type: multipart/alternative; \r\n"
156: + "\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
157: + "\r\n" + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
158: + "X-header: test1\r\n"
159: + "Content-Type: text/plain; charset=Cp1252\r\n"
160: + "Content-Transfer-Encoding: quoted-printable\r\n"
161: + "\r\n" + "test=80\r\n"
162: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
163: + "X-header: test2\r\n"
164: + "Content-Type: text/plain; charset=Cp1252\r\n"
165: + "Content-Transfer-Encoding: quoted-printable\r\n"
166: + "\r\n" + "second part =E8=E8\r\n"
167: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
168: + "Subject: test3\r\n"
169: + "Content-Transfer-Encoding: 7bit\r\n"
170: + "Content-Type: text/plain; charset=us-ascii\r\n"
171: + "\r\n" + "second part\r\n"
172: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
173: }
174:
175: protected String getMultipartMessageExpected3() {
176: return "Subject: test\r\n" + "MIME-Version: 1.0\r\n"
177: + "Content-Type: binary/octet-stream\r\n"
178: + "Content-Transfer-Encoding: quoted-printable\r\n"
179: + "\r\n" + "mynewco=F2=E0=F9ntent=80=E0!";
180: }
181:
182: /*
183: * Class under test for String getSubject()
184: */
185: public void testMultipartMessageChanges() throws Exception {
186:
187: MimeMessage mm = getMultipartMessage();
188:
189: // ByteArrayOutputStream out = new ByteArrayOutputStream();
190: // mmCreated.writeTo(out,new String[] {"Message-ID"});
191: // String messageSource = out.toString();
192: // System.out.println(messageSource);
193:
194: MimeMultipart content1 = (MimeMultipart) mm.getContent();
195: BodyPart b1 = content1.getBodyPart(0);
196: b1.setContent("test\u20AC", "text/plain; charset=Cp1252");
197: mm.setContent(content1, mm.getContentType());
198: //.setHeader(RFC2822Headers.CONTENT_TYPE,contentType);
199: mm.saveChanges();
200:
201: assertEquals(getMultipartMessageExpected1(),
202: getCleanedMessageSource(mm));
203:
204: MimeMultipart content2 = (MimeMultipart) mm.getContent();
205: content2.addBodyPart(new MimeBodyPart(new InternetHeaders(
206: new ByteArrayInputStream("Subject: test3\r\n"
207: .getBytes())), "second part".getBytes()));
208: mm.setContent(content2, mm.getContentType());
209: mm.saveChanges();
210:
211: assertEquals(getMultipartMessageExpected2(),
212: getCleanedMessageSource(mm));
213:
214: mm.setContent("mynewco\u00F2\u00E0\u00F9ntent\u20AC\u00E0!",
215: "text/plain; charset=cp1252");
216: mm
217: .setHeader(RFC2822Headers.CONTENT_TYPE,
218: "binary/octet-stream");
219: //mm.setHeader("Content-Transfer-Encoding","8bit");
220: mm.saveChanges();
221:
222: assertEquals(getMultipartMessageExpected3(),
223: getCleanedMessageSource(mm));
224:
225: ContainerUtil.dispose(mm);
226:
227: }
228:
229: protected MimeMessage getMissingEncodingAddHeaderMessage()
230: throws Exception {
231: MimeMessage m = new MimeMessage(Session
232: .getDefaultInstance(new Properties()));
233: m.setText("Test\u00E0\r\n");
234: m.setSubject("test");
235: m.saveChanges();
236: return m;
237: }
238:
239: protected String getMissingEncodingAddHeaderSource() {
240: return "Subject: test\r\n" + "\r\n" + "Test\u00E0\r\n";
241: }
242:
243: protected String getMissingEncodingAddHeaderExpected() {
244: return "Subject: test\r\n" + "MIME-Version: 1.0\r\n"
245: + "Content-Type: text/plain; charset=Cp1252\r\n"
246: + "Content-Transfer-Encoding: quoted-printable\r\n"
247: + "\r\n" + "Test=E0\r\n";
248: }
249:
250: /**
251: * This test is not usable in different locale environment.
252: */
253: /*
254: public void testMissingEncodingAddHeader() throws Exception {
255:
256:
257: MimeMessage mm = getMissingEncodingAddHeaderMessage();
258: mm.setHeader("Content-Transfer-Encoding", "quoted-printable");
259: mm.saveChanges();
260:
261: assertEquals(getMissingEncodingAddHeaderExpected(),getCleanedMessageSource(mm));
262: }
263: */
264:
265: protected String getCleanedMessageSource(MimeMessage mm)
266: throws Exception {
267: ByteArrayOutputStream out2;
268: out2 = new ByteArrayOutputStream();
269: mm.writeTo(out2, new String[] { "Message-ID" });
270:
271: String res = out2.toString();
272:
273: int p = res.indexOf("\r\n\r\n");
274: if (p > 0) {
275: String head = res.substring(0, p);
276: String[] str = head.split("\r\n");
277: Arrays.sort(str);
278: StringBuffer outputHead = new StringBuffer();
279: for (int i = str.length - 1; i >= 0; i--) {
280: outputHead.append(str[i]);
281: outputHead.append("\r\n");
282: }
283: outputHead.append(res.substring(p + 2));
284: res = outputHead.toString();
285: }
286:
287: res = res.replaceAll("----=_Part_\\d_\\d+\\.\\d+",
288: "----=_Part_\\0_XXXXXXXXXXX.XXXXXXXXXXX");
289: return res;
290: }
291:
292: protected void debugMessage(MimeMessage mm) throws Exception {
293: System.out.println("-------------------");
294: System.out.println(getCleanedMessageSource(mm));
295: System.out.println("-------------------");
296: }
297:
298: protected MimeMessage getMissingEncodingMessage() throws Exception {
299: MimeMessage mmCreated = new MimeMessage(Session
300: .getDefaultInstance(new Properties()));
301: mmCreated.setSubject("test");
302: MimeMultipart mm = new MimeMultipart("alternative");
303: mm
304: .addBodyPart(new MimeBodyPart(
305: new InternetHeaders(
306: new ByteArrayInputStream(
307: "X-header: test2\r\nContent-Type: text/plain; charset=Cp1252\r\nContent-Transfer-Encoding: quoted-printable\r\n"
308: .getBytes())),
309: "second part =E8=E8".getBytes()));
310: mmCreated.setContent(mm);
311: mmCreated.saveChanges();
312: return mmCreated;
313: }
314:
315: protected String getMissingEncodingMessageSource() {
316: return "Subject: test\r\n"
317: + "MIME-Version: 1.0\r\n"
318: + "Content-Type: multipart/alternative; \r\n"
319: + "\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
320: + "\r\n" + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
321: + "X-header: test2\r\n"
322: + "Content-Type: text/plain; charset=Cp1252\r\n"
323: + "Content-Transfer-Encoding: quoted-printable\r\n"
324: + "\r\n" + "second part =E8=E8\r\n"
325: + "------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
326: }
327:
328: public void testGetLineCount() throws Exception {
329: MimeMessage mm = getMissingEncodingMessage();
330: try {
331: int count = mm.getLineCount();
332: assertTrue(count == -1 || count == 7);
333: } catch (Exception e) {
334: fail("Unexpected exception in getLineCount");
335: }
336: ContainerUtil.dispose(mm);
337: }
338:
339: /**
340: * This test throw a NullPointerException when the original message was created by
341: * a MimeMessageInputStreamSource.
342: */
343: public void testMessageCloningViaCoW() throws Exception {
344: MimeMessage mmorig = getSimpleMessage();
345:
346: MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
347:
348: MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
349:
350: mm2.setHeader("Subject", "Modified");
351:
352: ContainerUtil.dispose(mm2);
353: System.gc();
354: Thread.sleep(200);
355: //((Disposable)mail_dup.getMessage()).dispose();
356:
357: mm.setHeader("Subject", "Modified");
358:
359: ContainerUtil.dispose(mm);
360: ContainerUtil.dispose(mmorig);
361: }
362:
363: /**
364: * This test throw a NullPointerException when the original message was created by
365: * a MimeMessageInputStreamSource.
366: */
367: public void testMessageCloningViaCoW2() throws Exception {
368: MimeMessage mmorig = getSimpleMessage();
369:
370: MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
371:
372: MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
373:
374: ContainerUtil.dispose(mm);
375: mm = null;
376: System.gc();
377: Thread.sleep(200);
378:
379: try {
380: mm2.writeTo(System.out);
381: } catch (Exception e) {
382: e.printStackTrace();
383: fail("Exception while writing the message to output");
384: }
385:
386: ContainerUtil.dispose(mm2);
387: ContainerUtil.dispose(mmorig);
388: }
389:
390: /**
391: * This test throw a NullPointerException when the original message was created by
392: * a MimeMessageInputStreamSource.
393: */
394: public void testMessageCloningViaCoWSubjectLost() throws Exception {
395: MimeMessage mmorig = getSimpleMessage();
396:
397: MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
398:
399: mm.setHeader("X-Test", "foo");
400: mm.saveChanges();
401:
402: assertEquals(getSimpleMessageCleanedSourceHeaderExpected(),
403: getCleanedMessageSource(mm));
404:
405: ContainerUtil.dispose(mm);
406: ContainerUtil.dispose(mmorig);
407: }
408:
409: public void testReturnPath() throws Exception {
410: MimeMessage message = getSimpleMessage();
411: assertNull(message.getHeader(RFC2822Headers.RETURN_PATH));
412: }
413:
414: public void testHeaderOrder() throws Exception {
415: MimeMessage message = getSimpleMessage();
416: message.setHeader(RFC2822Headers.RETURN_PATH, "<test@test.de>");
417: Enumeration h = message.getAllHeaderLines();
418:
419: assertEquals(h.nextElement(), "Return-Path: <test@test.de>");
420: }
421:
422: }
|