You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/02/10 12:28:30 UTC
svn commit: r620259 - in /james/mime4j/trunk/src:
main/java/org/apache/james/mime4j/message/
main/java/org/apache/james/mime4j/util/
test/java/org/apache/james/mime4j/message/
test/java/org/apache/james/mime4j/util/
Author: rdonkin
Date: Sun Feb 10 03:28:21 2008
New Revision: 620259
URL: http://svn.apache.org/viewvc?rev=620259&view=rev
Log:
MIM4J-34 Fix header charset handling. Applied revised and more flexible version of the patch. Contributed by Oleg Kalnichevski (https://issues.apache.org/jira/browse/MIME4J-34).
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/MessageUtilsTest.java
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Header.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/HeaderTest.java
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Header.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Header.java?rev=620259&r1=620258&r2=620259&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Header.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Header.java Sun Feb 10 03:28:21 2008
@@ -24,6 +24,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -36,6 +37,7 @@
import org.apache.james.mime4j.field.ContentTypeField;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.MessageUtils;
/**
@@ -154,17 +156,53 @@
/**
+ * Write the Header to the given OutputStream.
+ *
+ * @param out the OutputStream to write to
+ * @param mode compatibility mode
+ *
+ * @throws IOException if case of an I/O error
+ * @throws MimeException if case of a MIME protocol violation
+ */
+ public void writeTo(final OutputStream out, int mode) throws IOException, MimeException {
+ Charset charset = null;
+ if (mode == MessageUtils.LENIENT) {
+ ContentTypeField cf = ((ContentTypeField) getField(Field.CONTENT_TYPE));
+ if (cf != null && cf.getCharset() != null) {
+ charset = CharsetUtil.getCharset(cf.getCharset());
+ } else {
+ charset = MessageUtils.ISO_8859_1;
+ }
+ } else {
+ charset = MessageUtils.DEFAULT_CHARSET;
+ }
+ BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(out, charset), 8192);
+ for (Iterator it = fields.iterator(); it.hasNext();) {
+ Field field = (Field) it.next();
+ String fs = field.toString();
+ if (mode == MessageUtils.STRICT_ERROR && !MessageUtils.isASCII(fs)) {
+ throw new MimeException("Header '" + fs + "' violates RFC 822");
+ }
+ writer.write(fs);
+ writer.write(MessageUtils.CRLF);
+ }
+ writer.write(MessageUtils.CRLF);
+ writer.flush();
+ }
+
+ /**
* Write the Header to the given OutputStream
*
* @param out the OutputStream to write to
* @throws IOException
*/
- public void writeTo(OutputStream out) throws IOException {
- String charString = ((ContentTypeField) getField(Field.CONTENT_TYPE)).getCharset();
-
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(charString)),8192);
- writer.write(toString()+ "\r\n");
- writer.flush();
+ public void writeTo(final OutputStream out) throws IOException {
+ try {
+ writeTo(out, MessageUtils.LENIENT);
+ } catch (MimeException ex) {
+ throw new IOException(ex.getMessage());
+ }
}
}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java?rev=620259&r1=620258&r2=620259&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java Sun Feb 10 03:28:21 2008
@@ -23,14 +23,17 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.field.ContentTypeField;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.MessageUtils;
/**
* Represents a MIME multipart body (see RFC 2045).A multipart body has a
@@ -162,52 +165,73 @@
}
/**
+ * Write the Multipart to the given OutputStream.
*
- * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
+ * @param out the OutputStream to write to
+ * @param mode compatibility mode
+ *
+ * @throws IOException if case of an I/O error
+ * @throws MimeException if case of a MIME protocol violation
*/
- public void writeTo(OutputStream out) throws IOException {
- String boundary = getBoundary();
- List bodyParts = getBodyParts();
+ public void writeTo(final OutputStream out, int mode) throws IOException, MimeException {
+ Entity e = getParent();
+
+ ContentTypeField cField = (ContentTypeField) e.getHeader().getField(
+ Field.CONTENT_TYPE);
+ if (cField == null || cField.getBoundary() == null) {
+ throw new MimeException("Multipart boundary not specified");
+ }
+ String boundary = cField.getBoundary();
+ Charset charset = null;
+ if (mode == MessageUtils.LENIENT) {
+ if (cField != null && cField.getCharset() != null) {
+ charset = CharsetUtil.getCharset(cField.getCharset());
+ } else {
+ charset = MessageUtils.ISO_8859_1;
+ }
+ } else {
+ charset = MessageUtils.DEFAULT_CHARSET;
+ }
+
BufferedWriter writer = new BufferedWriter(
- new OutputStreamWriter(out, CharsetUtil.getCharset(getCharset())),
- 8192);
+ new OutputStreamWriter(out, charset), 8192);
+ List bodyParts = getBodyParts();
+
writer.write(getPreamble());
- writer.write("\r\n");
+ writer.write(MessageUtils.CRLF);
for (int i = 0; i < bodyParts.size(); i++) {
writer.write("--");
writer.write(boundary);
- writer.write("\r\n");
+ writer.write(MessageUtils.CRLF);
writer.flush();
((BodyPart) bodyParts.get(i)).writeTo(out);
- writer.write("\r\n");
+ writer.write(MessageUtils.CRLF);
}
writer.write("--");
writer.write(boundary);
- writer.write("--\r\n");
+ writer.write("--");
+ writer.write(MessageUtils.CRLF);
writer.write(getEpilogue());
- writer.write("\r\n");
+ writer.write(MessageUtils.CRLF);
writer.flush();
}
/**
- * Return the boundory of the parent Entity
+ * Write the Header to the given OutputStream
*
- * @return boundery
+ * @param out the OutputStream to write to
+ * @throws IOException
*/
- private String getBoundary() {
- Entity e = getParent();
- ContentTypeField cField = (ContentTypeField) e.getHeader().getField(
- Field.CONTENT_TYPE);
- return cField.getBoundary();
- }
-
- private String getCharset() {
- Entity e = getParent();
- String charString = ((ContentTypeField) e.getHeader().getField(Field.CONTENT_TYPE)).getCharset();
- return charString;
+ public void writeTo(final OutputStream out) throws IOException {
+ try {
+ writeTo(out, MessageUtils.LENIENT);
+ } catch (MimeException ex) {
+ throw new IOException(ex.getMessage());
+ }
}
+
}
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java?rev=620259&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java Sun Feb 10 03:28:21 2008
@@ -0,0 +1,61 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mime4j.util;
+
+import java.nio.charset.Charset;
+
+
+/**
+ * Frequently used RFC 882 constants and utility methods.
+ *
+ * @version $Id:$
+ */
+public final class MessageUtils {
+
+ public static final int STRICT_IGNORE = 1;
+ public static final int STRICT_ERROR = 2;
+ public static final int LENIENT = 3;
+
+ public static final Charset ASCII = CharsetUtil.getCharset("US-ASCII");
+
+ public static final Charset ISO_8859_1 = CharsetUtil.getCharset("ISO-8859-1");
+
+ public static final Charset DEFAULT_CHARSET = ASCII;
+
+ public static final String CRLF = "\r\n";
+
+ public static boolean isASCII(char ch) {
+ return ((int)ch & 0xFF80) == 0;
+ }
+
+ public static boolean isASCII(final String s) {
+ if (s == null) {
+ throw new IllegalArgumentException("String may not be null");
+ }
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ if (!isASCII(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/HeaderTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/HeaderTest.java?rev=620259&r1=620258&r2=620259&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/HeaderTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/HeaderTest.java Sun Feb 10 03:28:21 2008
@@ -19,8 +19,11 @@
package org.apache.james.mime4j.message;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.message.Header;
+import org.apache.james.mime4j.util.MessageUtils;
import junit.framework.TestCase;
@@ -41,4 +44,64 @@
assertEquals("Headers equals", SUBJECT + "\r\n" + TO + "\r\n", header
.toString());
}
+
+ static final int SWISS_GERMAN_HELLO [] = {
+ 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
+ };
+
+ private static String constructString(int [] unicodeChars) {
+ StringBuffer buffer = new StringBuffer();
+ if (unicodeChars != null) {
+ for (int i = 0; i < unicodeChars.length; i++) {
+ buffer.append((char)unicodeChars[i]);
+ }
+ }
+ return buffer.toString();
+ }
+
+ public void testWriteInStrictMode() throws Exception {
+ String hello = constructString(SWISS_GERMAN_HELLO);
+ Header header = new Header();
+ header.addField(Field.parse("Hello: " + hello));
+
+ Field field = header.getField("Hello");
+ assertNotNull(field);
+ assertEquals(hello, field.getBody());
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ header.writeTo(buffer, MessageUtils.STRICT_IGNORE);
+ String s = buffer.toString(MessageUtils.ASCII.name());
+
+ assertEquals("Hello: Gr?ezi_z?m?\r\n\r\n", s);
+
+ buffer.reset();
+
+ try {
+ header.writeTo(buffer, MessageUtils.STRICT_ERROR);
+ fail("MimeException should have been thrown");
+ } catch (MimeException expected) {
+ }
+ }
+
+ public void testWriteInLenientMode() throws Exception {
+ String hello = constructString(SWISS_GERMAN_HELLO);
+ Header header = new Header();
+ header.addField(Field.parse("Hello: " + hello));
+ header.addField(Field.parse("Content-type: text/plain; charset=" +
+ MessageUtils.ISO_8859_1.name()));
+
+ Field field = header.getField("Hello");
+ assertNotNull(field);
+ assertEquals(hello, field.getBody());
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ header.writeTo(buffer, MessageUtils.LENIENT);
+ String s = buffer.toString(MessageUtils.ISO_8859_1.name());
+
+ assertEquals("Hello: " + hello + "\r\n" +
+ "Content-type: text/plain; charset=ISO-8859-1\r\n\r\n", s);
+ }
+
}
Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/MessageUtilsTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/MessageUtilsTest.java?rev=620259&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/MessageUtilsTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/MessageUtilsTest.java Sun Feb 10 03:28:21 2008
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.james.mime4j.util;
+
+import junit.framework.TestCase;
+
+public class MessageUtilsTest extends TestCase {
+
+ public void testAllASCII() {
+ String s = "Like hello and stuff";
+ assertTrue(MessageUtils.isASCII(s));
+ }
+
+ static final int RUSSIAN_HELLO [] = {
+ 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
+ 0x432, 0x435, 0x442
+ };
+
+ static final int SWISS_GERMAN_HELLO [] = {
+ 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
+ };
+
+ private static String constructString(int [] unicodeChars) {
+ StringBuffer buffer = new StringBuffer();
+ if (unicodeChars != null) {
+ for (int i = 0; i < unicodeChars.length; i++) {
+ buffer.append((char)unicodeChars[i]);
+ }
+ }
+ return buffer.toString();
+ }
+
+ public void testNonASCII() {
+ String s = constructString(SWISS_GERMAN_HELLO);
+ assertFalse(MessageUtils.isASCII(s));
+ s = constructString(RUSSIAN_HELLO);
+ assertFalse(MessageUtils.isASCII(s));
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org