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  
20  package org.apache.james.transport.mailets;
21  
22  import org.apache.mailet.base.test.FakeMail;
23  import org.apache.mailet.base.test.FakeMailContext;
24  import org.apache.mailet.base.test.FakeMailetConfig;
25  import org.apache.mailet.Mail;
26  import org.apache.mailet.Mailet;
27  
28  import javax.mail.MessagingException;
29  import javax.mail.Session;
30  import javax.mail.internet.MimeMessage;
31  import javax.mail.internet.MimeUtility;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.IOException;
36  import java.io.UnsupportedEncodingException;
37  import java.util.Properties;
38  
39  import junit.framework.TestCase;
40  
41  /**
42   * Test encoding issues
43   * 
44   * This test should also be run with the following JVM options to be sure it tests:
45   * "-Dfile.encoding=ASCII -Dmail.mime.charset=ANSI_X3.4-1968"
46   */
47  public class AddFooterTest extends TestCase {
48  
49      public AddFooterTest(String arg0) throws UnsupportedEncodingException {
50          super(arg0);
51          
52          /*
53          
54          String encoding = (new InputStreamReader(System.in)).getEncoding();
55          System.out.println("System Encoding: "+encoding);
56          System.out.println("Default Java Charset:"+MimeUtility.getDefaultJavaCharset());
57          System.out.println("---------");
58          String a = "\u20AC\u00E0"; // euro char followed by an italian a with an accent System.out.println(debugString(a,"UTF-8"));
59          System.out.println(debugString(a,"UTF8"));
60          System.out.println(debugString(a,"UTF-16"));
61          System.out.println(debugString(a,"UNICODE"));
62          System.out.println(debugString(a,"ISO-8859-15"));
63          System.out.println(debugString(a,"ISO-8859-1"));
64          System.out.println(debugString(a,"CP1252"));
65          System.out.println(debugString(a,"ANSI_X3.4-1968"));
66           
67           */
68      }
69  
70      private final static char[] hexchars = { '0', '1', '2', '3', '4', '5', '6',
71              '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
72  
73      public String debugString(String a, String charset)
74              throws UnsupportedEncodingException {
75          byte[] bytes = a.getBytes(charset);
76          StringBuffer res = new StringBuffer();
77          for (int i = 0; i < bytes.length; i++) {
78              if (i > 0)
79                  res.append("-");
80              res.append(hexchars[((bytes[i] + 256) % 256) / 16]);
81              res.append(hexchars[((bytes[i] + 256) % 256) % 16]);
82          }
83          res.append(" (");
84          res.append(MimeUtility.mimeCharset(charset));
85          res.append(" / ");
86          res.append(MimeUtility.javaCharset(charset));
87          res.append(")");
88          return res.toString();
89      }
90  
91      /*
92       * Class under test for String getSubject()
93       */
94      public void testAddFooterTextPlain() throws MessagingException, IOException {
95  
96          // quoted printable mimemessage text/plain
97          String asciisource = "Subject: test\r\nContent-Type: text/plain; charset=ISO-8859-15\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nTest=E0 and one\r\n";
98  
99          String iso885915qpheader = "------ my footer =E0/=A4 ------";
100         String footer = "------ my footer \u00E0/\u20AC ------";
101 
102         String res = processAddFooter(asciisource, footer);
103 
104         assertEquals(asciisource + iso885915qpheader, res);
105 
106     }
107 
108     public void testUnsupportedEncoding() throws MessagingException, IOException {
109 
110         // quoted printable mimemessage text/plain
111         String asciisource = "Subject: test\r\nContent-Type: text/plain; charset=UNSUPPORTED_ENCODING\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nTest=E0 and one\r\n";
112 
113         String footer = "------ my footer \u00E0/\u20AC ------";
114 
115         try {
116             String res = processAddFooter(asciisource, footer);
117             assertEquals(asciisource, res);
118         } catch (Exception e) {
119             fail("should not throw an exception: "+e.getMessage());
120         }
121 
122 
123     }
124 
125     /*
126      * Test for      JAMES-443
127      * This should not add the header and should leave the multipart/mixed Content-Type intact
128      */
129     public void testAddFooterMimeNestedUnsupportedMultipart() throws MessagingException, IOException {
130 
131         // quoted printable mimemessage text/plain
132         String asciisource = "MIME-Version: 1.0\r\n"
133             +"Content-Type: multipart/mixed; boundary=\"===============0204599088==\"\r\n"
134                 +"\r\n"
135                 +"This is a cryptographically signed message in MIME format.\r\n"
136                 +"\r\n"
137                 +"--===============0204599088==\r\n"
138                 +"Content-Type: multipart/unsupported; boundary=\"------------ms050404020900070803030808\"\r\n"
139                 +"\r\n"
140                 +"--------------ms050404020900070803030808\r\n"
141                 +"Content-Type: text/plain; charset=ISO-8859-1\r\n"
142                 +"\r\n"
143                 +"test\r\n"
144                 +"\r\n"
145                 +"--------------ms050404020900070803030808--\r\n"
146                 +"\r\n"
147                 +"--===============0204599088==--\r\n";
148         // String asciisource = "Subject: test\r\nContent-Type: multipart/mixed; boundary=\"===============0204599088==\"\r\nMIME-Version: 1.0\r\n\r\nThis is a cryptographically signed message in MIME format.\r\n\r\n--===============0204599088==\r\nContent-Type: text/plain\r\n\r\ntest\r\n--===============0204599088==\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: inline\r\n\r\ntest\r\n--===============0204599088==--\r\n";
149 
150         String footer = "------ my footer \u00E0/\u20AC ------";
151 
152         String res = processAddFooter(asciisource, footer);
153 
154         assertEquals(asciisource, res);
155 
156     }
157     
158     /*
159      * Test for      JAMES-368
160      * AddFooter couldn't process mails which MimeType is multipart/related
161      */
162     public void testAddFooterMultipartRelated() throws MessagingException, IOException {
163 
164         // quoted printable mimemessage text/plain
165         String asciisource = "MIME-Version: 1.0\r\n"
166             +"Subject: test\r\n"
167             +"Content-Type: multipart/related;\r\n"
168             +"  boundary=\"------------050206010102010306090507\"\r\n"
169             +"\r\n"
170             +"--------------050206010102010306090507\r\n"
171             +"Content-Type: text/html; charset=ISO-8859-15\r\n"
172             +"Content-Transfer-Encoding: quoted-printable\r\n"
173             +"\r\n"
174             +"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n"
175             +"<html>\r\n"
176             +"<head>\r\n"
177             +"<meta content=3D\"text/html;charset=3DISO-8859-15\" http-equiv=3D\"Content-Typ=\r\n"
178             +"e\">\r\n"
179             +"</head>\r\n"
180             +"<body bgcolor=3D\"#ffffff\" text=3D\"#000000\">\r\n"
181             +"<br>\r\n"
182             +"<div class=3D\"moz-signature\">-- <br>\r\n"
183             +"<img src=3D\"cid:part1.02060605.123@zzz.com\" border=3D\"0\"></div>\r\n";
184         String asciifoot = "</body>\r\n"
185             +"</html>\r\n"
186             +"\r\n"
187             +"--------------050206010102010306090507\r\n"
188             +"Content-Type: image/gif\r\n"
189             +"Content-Transfer-Encoding: base64\r\n"
190             +"Content-ID: <part1.02060605.123@zzz.com>\r\n"
191             +"Content-Disposition: inline;\r\n"
192             +"\r\n"
193             +"YQ==\r\n"
194             +"--------------050206010102010306090507--\r\n";
195 
196         // String asciisource = "Subject: test\r\nContent-Type: multipart/mixed; boundary=\"===============0204599088==\"\r\nMIME-Version: 1.0\r\n\r\nThis is a cryptographically signed message in MIME format.\r\n\r\n--===============0204599088==\r\nContent-Type: text/plain\r\n\r\ntest\r\n--===============0204599088==\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: inline\r\n\r\ntest\r\n--===============0204599088==--\r\n";
197 
198         String footer = "------ my footer \u00E0/\u20AC ------";
199         String expectedFooter = "<br>------ my footer =E0/=A4 ------";
200 
201         String res = processAddFooter(asciisource+asciifoot, footer);
202 
203         assertEquals(asciisource+expectedFooter+asciifoot, res);
204 
205     }
206     
207     
208 
209     /*
210      * Class under test for String getSubject()
211      */
212     public void testAddFooterTextPlainISO8859() throws MessagingException, IOException {
213 
214         // quoted printable mimemessage text/plain
215         String asciisource = "Subject: test\r\nContent-Type: text/plain; charset=iso-8859-15\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nTest=E0 and one =A4\r\n";
216 
217         String iso885915qpheader = "------ my footer =E0/=A4 ------";
218         String footer = "------ my footer \u00E0/\u20AC ------";
219 
220         String res = processAddFooter(asciisource, footer);
221 
222         assertEquals(asciisource + iso885915qpheader, res);
223 
224     }
225 
226     /*
227      * Class under test for String getSubject()
228      */
229     public void testAddFooterMultipartAlternative() throws MessagingException,
230             IOException {
231 
232         String sep = "--==--";
233         String head = "Subject: test\r\nContent-Type: multipart/alternative;\r\n    boundary=\""
234                 + sep
235                 + "\"\r\nMIME-Version: 1.0\r\n";
236         String content1 = "Content-Type: text/plain;\r\n    charset=\"ISO-8859-15\"\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nTest=E0 and @=80";
237         String c2h = "Content-Type: text/html;\r\n    charset=\"CP1252\"\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n";
238         String c2pre = "<html><body>test =80 ss";
239         String c2post = "</body></html>";
240 
241         StringBuffer asciisource = new StringBuffer();
242         asciisource.append(head);
243         asciisource.append("\r\n--");
244         asciisource.append(sep);
245         asciisource.append("\r\n");
246         asciisource.append(content1);
247         asciisource.append("\r\n--");
248         asciisource.append(sep);
249         asciisource.append("\r\n");
250         asciisource.append(c2h);
251         asciisource.append(c2pre);
252         asciisource.append(c2post);
253         asciisource.append("\r\n--");
254         asciisource.append(sep);
255         asciisource.append("--\r\n");
256 
257         String iso885915qpheader = "------ my footer =E0/=A4 ------";
258         String cp1252qpfooter = "------ my footer =E0/=80 ------";
259         String footer = "------ my footer \u00E0/\u20AC ------";
260 
261         StringBuffer expected = new StringBuffer();
262         expected.append(head);
263         expected.append("\r\n--");
264         expected.append(sep);
265         expected.append("\r\n");
266         expected.append(content1);
267         expected.append("\r\n");
268         expected.append(iso885915qpheader);
269         expected.append("\r\n--");
270         expected.append(sep);
271         expected.append("\r\n");
272         expected.append(c2h);
273         expected.append(c2pre);
274         expected.append("<br>");
275         expected.append(cp1252qpfooter);
276         expected.append(c2post);
277         expected.append("\r\n--");
278         expected.append(sep);
279         expected.append("--\r\n");
280         
281         String res = processAddFooter(asciisource.toString(), footer);
282 
283         assertEquals(expected.toString(), res);
284 
285     }
286 
287     private String processAddFooter(String asciisource, String footer)
288             throws MessagingException, IOException {
289         Mailet mailet = new AddFooter();
290 
291         FakeMailetConfig mci = new FakeMailetConfig("Test",new FakeMailContext());
292         mci.setProperty("text",footer);
293 
294         mailet.init(mci);
295 
296         Mail mail = new FakeMail();
297         mail.setMessage(new MimeMessage(Session
298                 .getDefaultInstance(new Properties()),
299                 new ByteArrayInputStream(asciisource.getBytes())));
300 
301         mailet.service(mail);
302 
303         ByteArrayOutputStream rawMessage = new ByteArrayOutputStream();
304         mail.getMessage().writeTo(
305                 rawMessage,
306                 new String[] { "Bcc", "Content-Length", "Message-ID" });
307         String res = rawMessage.toString();
308         return res;
309     }
310 
311 }