View Javadoc

1   /*****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  package org.apache.james.core;
20  
21  import org.apache.avalon.framework.container.ContainerUtil;
22  import org.apache.mailet.RFC2822Headers;
23  
24  import javax.mail.BodyPart;
25  import javax.mail.Session;
26  import javax.mail.internet.InternetHeaders;
27  import javax.mail.internet.MimeBodyPart;
28  import javax.mail.internet.MimeMessage;
29  import javax.mail.internet.MimeMultipart;
30  
31  import java.io.ByteArrayInputStream;
32  import java.io.ByteArrayOutputStream;
33  import java.util.Arrays;
34  import java.util.Enumeration;
35  import java.util.Properties;
36  
37  import junit.framework.TestCase;
38  
39  /***
40   * Test the subject folding issue.
41   */
42  public class MimeMessageTest extends TestCase {
43  
44      protected MimeMessage getSimpleMessage() throws Exception {
45          MimeMessage mmCreated = new MimeMessage(Session.getDefaultInstance(new Properties()));
46          mmCreated.setSubject("test");
47          mmCreated.setText("test body");
48          mmCreated.saveChanges();
49          return mmCreated;
50      }
51      
52      protected String getSimpleMessageCleanedSource() throws Exception {
53          return "Subject: test\r\n"
54              +"MIME-Version: 1.0\r\n"
55              +"Content-Type: text/plain; charset=us-ascii\r\n"
56              +"Content-Transfer-Encoding: 7bit\r\n"
57              +"\r\n"
58              +"test body";
59      }
60      
61  
62      protected MimeMessage getMessageWithBadReturnPath() throws Exception {
63          MimeMessage mmCreated = new MimeMessage(Session.getDefaultInstance(new Properties()));
64          mmCreated.setSubject("test");
65          mmCreated.setHeader(RFC2822Headers.RETURN_PATH, "<mybadreturn@example.com>");
66          mmCreated.setText("test body");
67          mmCreated.saveChanges();
68          return mmCreated;
69      }
70      
71      protected String getMessageWithBadReturnPathSource() throws Exception {
72          return "Subject: test\r\n"
73              +"Return-Path: <mybadreturn@example.com>\r\n"
74              +"MIME-Version: 1.0\r\n"
75              +"Content-Type: text/plain; charset=us-ascii\r\n"
76              +"Content-Transfer-Encoding: 7bit\r\n"
77              +"\r\n"
78              +"test body";
79      }
80      
81      protected String getSimpleMessageCleanedSourceHeaderExpected() throws Exception {
82          return "X-Test: foo\r\n"+getSimpleMessageCleanedSource();
83      }
84      
85      /*
86       * Class under test for String getSubject()
87       */
88      public void testSimpleMessage() throws Exception {
89          assertEquals(getSimpleMessageCleanedSource(), getCleanedMessageSource(getSimpleMessage()));
90      }
91      
92      
93      protected MimeMessage getMultipartMessage() throws Exception {
94          MimeMessage mmCreated = new MimeMessage(Session.getDefaultInstance(new Properties()));
95          mmCreated.setSubject("test");
96          MimeMultipart mm = new MimeMultipart("alternative");
97          mm.addBodyPart(new MimeBodyPart(new InternetHeaders(new ByteArrayInputStream("X-header: test1\r\nContent-Type: text/plain; charset=Cp1252\r\n".getBytes())),"first part ???".getBytes()));
98          mm.addBodyPart(new MimeBodyPart(new InternetHeaders(new ByteArrayInputStream("X-header: test2\r\nContent-Type: text/plain; charset=Cp1252\r\nContent-Transfer-Encoding: quoted-printable\r\n".getBytes())),"second part =E8=E8".getBytes()));
99          mmCreated.setContent(mm);
100         mmCreated.saveChanges();
101         return mmCreated;
102     }
103     
104     protected String getMultipartMessageSource() {
105         return "Subject: test\r\n"
106             +"MIME-Version: 1.0\r\n"
107             +"Content-Type: multipart/alternative; \r\n" 
108             +"\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
109             +"\r\n"
110             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
111             +"X-header: test1\r\n"
112             +"Content-Type: text/plain; charset=Cp1252\r\n"
113             +"Content-Transfer-Encoding: quoted-printable\r\n"
114             +"\r\n"
115             +"first part =E8\r\n"
116             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
117             +"X-header: test2\r\n"
118             +"Content-Type: text/plain; charset=Cp1252\r\n"
119             +"Content-Transfer-Encoding: quoted-printable\r\n"
120             +"\r\n"
121             +"second part =E8=E8\r\n"
122             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
123     }
124     
125     protected String getMultipartMessageExpected1() {
126         return "Subject: test\r\n"
127             +"MIME-Version: 1.0\r\n"
128             +"Content-Type: multipart/alternative; \r\n" 
129             +"\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
130             +"\r\n"
131             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
132             +"X-header: test1\r\n"
133             +"Content-Type: text/plain; charset=Cp1252\r\n"
134             +"Content-Transfer-Encoding: quoted-printable\r\n"
135             +"\r\n"
136             +"test=80\r\n"
137             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
138             +"X-header: test2\r\n"
139             +"Content-Type: text/plain; charset=Cp1252\r\n"
140             +"Content-Transfer-Encoding: quoted-printable\r\n"
141             +"\r\n"
142             +"second part =E8=E8\r\n"
143             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
144     }
145     
146     protected String getMultipartMessageExpected2() {
147         return "Subject: test\r\n"
148             +"MIME-Version: 1.0\r\n"
149             +"Content-Type: multipart/alternative; \r\n" 
150             +"\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
151             +"\r\n"
152             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
153             +"X-header: test1\r\n"
154             +"Content-Type: text/plain; charset=Cp1252\r\n"
155             +"Content-Transfer-Encoding: quoted-printable\r\n"
156             +"\r\n"
157             +"test=80\r\n"
158             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
159             +"X-header: test2\r\n"
160             +"Content-Type: text/plain; charset=Cp1252\r\n"
161             +"Content-Transfer-Encoding: quoted-printable\r\n"
162             +"\r\n"
163             +"second part =E8=E8\r\n"
164             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
165             +"Subject: test3\r\n"
166             +"Content-Transfer-Encoding: 7bit\r\n"
167             +"Content-Type: text/plain; charset=us-ascii\r\n"
168             +"\r\n"
169             +"second part\r\n"
170             +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
171     }
172     
173     protected String getMultipartMessageExpected3() {
174         return "Subject: test\r\n"
175             +"MIME-Version: 1.0\r\n"
176             +"Content-Type: binary/octet-stream\r\n"
177             +"Content-Transfer-Encoding: quoted-printable\r\n"
178             +"\r\n"
179             +"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         
195         MimeMultipart content1 = (MimeMultipart) mm.getContent();
196         BodyPart b1 = content1.getBodyPart(0);
197         b1.setContent("test\u20AC","text/plain; charset=Cp1252");
198         mm.setContent(content1,mm.getContentType());
199         //.setHeader(RFC2822Headers.CONTENT_TYPE,contentType);
200         mm.saveChanges();
201 
202         assertEquals(getMultipartMessageExpected1(),getCleanedMessageSource(mm));
203 
204         MimeMultipart content2 = (MimeMultipart) mm.getContent();
205         content2.addBodyPart(new MimeBodyPart(new InternetHeaders(new ByteArrayInputStream("Subject: test3\r\n".getBytes())),"second part".getBytes()));
206         mm.setContent(content2,mm.getContentType());
207         mm.saveChanges();
208 
209         assertEquals(getMultipartMessageExpected2(),getCleanedMessageSource(mm));
210 
211         mm.setContent("mynewco\u00F2\u00E0\u00F9ntent\u20AC\u00E0!","text/plain; charset=cp1252");
212         mm.setHeader(RFC2822Headers.CONTENT_TYPE,"binary/octet-stream");
213         //mm.setHeader("Content-Transfer-Encoding","8bit");
214         mm.saveChanges();
215         
216         assertEquals(getMultipartMessageExpected3(),getCleanedMessageSource(mm));
217         
218         ContainerUtil.dispose(mm);
219         
220     }
221 
222     protected MimeMessage getMissingEncodingAddHeaderMessage() throws Exception {
223         MimeMessage m = new MimeMessage(Session.getDefaultInstance(new Properties()));
224         m.setText("Test\u00E0\r\n");
225         m.setSubject("test");
226         m.saveChanges();
227         return m;
228     }
229     
230 
231     protected String getMissingEncodingAddHeaderSource() {
232         return "Subject: test\r\n"+
233                 "\r\n"+
234                 "Test\u00E0\r\n";
235     }
236     
237     protected String getMissingEncodingAddHeaderExpected() {
238         return "Subject: test\r\n"
239             +"MIME-Version: 1.0\r\n"
240             +"Content-Type: text/plain; charset=Cp1252\r\n"
241             +"Content-Transfer-Encoding: quoted-printable\r\n"
242             +"\r\n"
243             +"Test=E0\r\n";
244     }
245     
246 
247     /***
248      * This test is not usable in different locale environment.
249      */
250     /*
251     public void testMissingEncodingAddHeader() throws Exception {
252         
253         
254         MimeMessage mm = getMissingEncodingAddHeaderMessage();
255         mm.setHeader("Content-Transfer-Encoding", "quoted-printable");
256         mm.saveChanges();
257 
258         assertEquals(getMissingEncodingAddHeaderExpected(),getCleanedMessageSource(mm));
259     }
260     */
261     
262 
263     protected String getCleanedMessageSource(MimeMessage mm) throws Exception {
264         ByteArrayOutputStream out2;
265         out2 = new ByteArrayOutputStream();
266         mm.writeTo(out2,new String[] {"Message-ID"});
267 
268         String res = out2.toString();
269 
270         int p = res.indexOf("\r\n\r\n");
271         if (p > 0) {
272             String head = res.substring(0,p);
273             String[] str = head.split("\r\n");
274             Arrays.sort(str);
275             StringBuffer outputHead = new StringBuffer();
276             for (int i = str.length-1; i >= 0; i--) {
277                 outputHead.append(str[i]);
278                 outputHead.append("\r\n");
279             }
280             outputHead.append(res.substring(p+2));
281             res = outputHead.toString();
282         }
283         
284         res = res.replaceAll("----=_Part_//d+_//d+//.//d+","----=_Part_//0_XXXXXXXXXXX.XXXXXXXXXXX");
285         return res;
286     }
287     
288     protected void debugMessage(MimeMessage mm) throws Exception {
289         System.out.println("-------------------");
290         System.out.println(getCleanedMessageSource(mm));
291         System.out.println("-------------------");
292     }
293     
294 
295     protected MimeMessage getMissingEncodingMessage() throws Exception {
296         MimeMessage mmCreated = new MimeMessage(Session.getDefaultInstance(new Properties()));
297         mmCreated.setSubject("test");
298         MimeMultipart mm = new MimeMultipart("alternative");
299         mm.addBodyPart(new MimeBodyPart(new InternetHeaders(new ByteArrayInputStream("X-header: test2\r\nContent-Type: text/plain; charset=Cp1252\r\nContent-Transfer-Encoding: quoted-printable\r\n".getBytes())),"second part =E8=E8".getBytes()));
300         mmCreated.setContent(mm);
301         mmCreated.saveChanges();
302         return mmCreated;
303     }
304     
305 
306     protected String getMissingEncodingMessageSource() {
307         return "Subject: test\r\n"
308         +"MIME-Version: 1.0\r\n"
309         +"Content-Type: multipart/alternative; \r\n" 
310         +"\tboundary=\"----=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\"\r\n"
311         +"\r\n"
312         +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX\r\n"
313         +"X-header: test2\r\n"
314         +"Content-Type: text/plain; charset=Cp1252\r\n"
315         +"Content-Transfer-Encoding: quoted-printable\r\n"
316         +"\r\n"
317         +"second part =E8=E8\r\n"
318         +"------=_Part_0_XXXXXXXXXXX.XXXXXXXXXXX--\r\n";
319     }
320     
321 
322     public void testGetLineCount() throws Exception {
323         MimeMessage mm = getMissingEncodingMessage();
324         try {
325             int count = mm.getLineCount();
326             assertTrue(count == -1 || count == 7);
327         } catch (Exception e) {
328             fail("Unexpected exception in getLineCount");
329         }
330         ContainerUtil.dispose(mm);
331     }
332     
333     /***
334      * This test throw a NullPointerException when the original message was created by
335      * a MimeMessageInputStreamSource.
336      */
337     public void testMessageCloningViaCoW() throws Exception {
338         MimeMessage mmorig = getSimpleMessage();
339         
340         MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
341 
342         MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
343 
344         mm2.setHeader("Subject", "Modified");
345         
346         ContainerUtil.dispose(mm2);
347         System.gc();
348         Thread.sleep(200);
349         //((Disposable)mail_dup.getMessage()).dispose();
350         
351         mm.setHeader("Subject", "Modified");
352         
353         ContainerUtil.dispose(mm);
354         ContainerUtil.dispose(mmorig);
355     }
356     
357     /***
358      * This test throw a NullPointerException when the original message was created by
359      * a MimeMessageInputStreamSource.
360      */
361     public void testMessageCloningViaCoW2() throws Exception {
362         MimeMessage mmorig = getSimpleMessage();
363         
364         MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
365         
366         MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
367         
368         ContainerUtil.dispose(mm);
369         mm = null;
370         System.gc();
371         Thread.sleep(200);
372 
373         try {
374             mm2.writeTo(System.out);
375         } catch (Exception e) {
376             e.printStackTrace();
377             fail("Exception while writing the message to output");
378         }
379         
380         ContainerUtil.dispose(mm2);
381         ContainerUtil.dispose(mmorig);
382     }
383 
384     
385     /***
386      * This test throw a NullPointerException when the original message was created by
387      * a MimeMessageInputStreamSource.
388      */
389     public void testMessageCloningViaCoWSubjectLost() throws Exception {
390         MimeMessage mmorig = getSimpleMessage();
391         
392         MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
393 
394         mm.setHeader("X-Test", "foo");
395         mm.saveChanges();
396         
397         assertEquals(getSimpleMessageCleanedSourceHeaderExpected(),getCleanedMessageSource(mm));
398 
399         ContainerUtil.dispose(mm);
400         ContainerUtil.dispose(mmorig);
401     }
402     
403     public void testReturnPath() throws Exception {
404         MimeMessage message = getSimpleMessage();
405         assertNull(message.getHeader(RFC2822Headers.RETURN_PATH));
406     }
407     
408     public void testHeaderOrder() throws Exception {
409         MimeMessage message = getSimpleMessage();
410         message.setHeader(RFC2822Headers.RETURN_PATH, "<test@test.de>");
411         Enumeration h =  message.getAllHeaderLines();
412         
413         assertEquals(h.nextElement(),"Return-Path: <test@test.de>");
414     }
415 
416 }