Index: src/test/java/org/apache/james/mime4j/message/HeaderTest.java
===================================================================
--- src/test/java/org/apache/james/mime4j/message/HeaderTest.java	(revision 746269)
+++ src/test/java/org/apache/james/mime4j/message/HeaderTest.java	(working copy)
@@ -50,7 +50,7 @@
     public void testWriteInStrictMode() throws Exception {
         String hello = SWISS_GERMAN_HELLO;
         Header header = new Header();
-        header.addField(AbstractField.parse("Hello: " + hello));
+        header.addField(AbstractField.parse("Hello: " + hello, CharsetUtil.ISO_8859_1));
         
         Field field = header.getField("Hello");
         assertNotNull(field);
@@ -75,7 +75,7 @@
     public void testWriteInLenientMode() throws Exception {
         String hello = SWISS_GERMAN_HELLO;
         Header header = new Header();
-        header.addField(AbstractField.parse("Hello: " + hello));
+        header.addField(AbstractField.parse("Hello: " + hello, CharsetUtil.ISO_8859_1));
         header.addField(AbstractField.parse("Content-type: text/plain; charset=" + 
                 CharsetUtil.ISO_8859_1.name()));
         
Index: src/main/java/org/apache/james/mime4j/field/MailboxField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/MailboxField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/MailboxField.java	(working copy)
@@ -19,6 +19,8 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.james.mime4j.field.address.AddressList;
@@ -26,6 +28,7 @@
 import org.apache.james.mime4j.field.address.MailboxList;
 import org.apache.james.mime4j.field.address.parser.ParseException;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Mailbox field such as <code>Sender</code> or <code>Resent-Sender</code>.
@@ -38,8 +41,12 @@
     private Mailbox mailbox;
     private ParseException parseException;
 
-    MailboxField(final String name, final String body, final String raw) {
-        super(name, body, raw);
+    MailboxField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     public Mailbox getMailbox() {
@@ -77,8 +84,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new MailboxField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new MailboxField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/DateTimeField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/DateTimeField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/DateTimeField.java	(working copy)
@@ -25,8 +25,10 @@
 import org.apache.james.mime4j.field.datetime.parser.ParseException;
 import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 import java.io.StringReader;
+import java.nio.charset.Charset;
 import java.util.Date;
 
 /**
@@ -40,8 +42,12 @@
     private Date date;
     private ParseException parseException;
 
-    DateTimeField(String name, String body, String raw) {
-        super(name, body, raw);
+    DateTimeField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     public Date getDate() {
@@ -82,8 +88,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new DateTimeField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new DateTimeField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/FieldParser.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/FieldParser.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/FieldParser.java	(working copy)
@@ -19,10 +19,13 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 public interface FieldParser {
     
-    Field parse(final String name, final String body, final String raw);
+    Field parse(String name, String body, ByteArrayBuffer raw, Charset charset);
     
 }
Index: src/main/java/org/apache/james/mime4j/field/ContentDispositionField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/ContentDispositionField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/ContentDispositionField.java	(working copy)
@@ -20,6 +20,7 @@
 package org.apache.james.mime4j.field;
 
 import java.io.StringReader;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -33,6 +34,7 @@
 import org.apache.james.mime4j.field.contentdisposition.parser.TokenMgrError;
 import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Represents a <code>Content-Disposition</code> field.
@@ -76,8 +78,12 @@
     private boolean readDateParsed;
     private Date readDate;
 
-    ContentDispositionField(String name, String body, String raw) {
-        super(name, body, raw);
+    ContentDispositionField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     /**
@@ -319,8 +325,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new ContentDispositionField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new ContentDispositionField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/Fields.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/Fields.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/Fields.java	(working copy)
@@ -19,6 +19,7 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
@@ -31,6 +32,9 @@
 import org.apache.james.mime4j.field.address.Address;
 import org.apache.james.mime4j.field.address.Mailbox;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.ContentUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -569,9 +573,14 @@
     private static <F extends Field> F parse(Class<F> fieldClass,
             String fieldName, String fieldBody) {
         try {
-            String raw = MimeUtil.fold(fieldName + ": " + fieldBody, 0);
+            String s = MimeUtil.fold(fieldName + ": " + fieldBody, 0);
 
-            Field field = AbstractField.parse(raw);
+            Charset charset = CharsetUtil.US_ASCII;
+            byte[] b = ContentUtil.toBytes(s, charset.name());
+            ByteArrayBuffer raw = new ByteArrayBuffer(b.length);
+            raw.append(b, 0, b.length);
+
+            Field field = AbstractField.parse(raw, charset);
             if (!fieldClass.isInstance(field)) {
                 throw new IllegalArgumentException("Incompatible field name: "
                         + fieldName);
Index: src/main/java/org/apache/james/mime4j/field/AbstractField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/AbstractField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/AbstractField.java	(working copy)
@@ -19,17 +19,23 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.james.mime4j.MimeException;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.parser.RawField;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.ContentUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
  * The base class of all field classes.
  */
-public abstract class AbstractField implements Field {
+public abstract class AbstractField implements RawField {
+    
 
     private static final String FIELD_NAME_PATTERN = 
         "^([\\x21-\\x39\\x3b-\\x7e]+):";
@@ -40,12 +46,32 @@
     
     private final String name;
     private final String body;
-    private final String raw;
+    private final ByteArrayBuffer raw;
+    private final Charset charset;
     
-    protected AbstractField(final String name, final String body, final String raw) {
+    protected AbstractField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super();
+        if (name == null) {
+            throw new IllegalArgumentException("Field name may not be null");
+        }
+        if (body == null) {
+            throw new IllegalArgumentException("Field body may not be null");
+        }
+        if (raw == null) {
+            throw new IllegalArgumentException("Raw buffer may not be null");
+        }
         this.name = name;
         this.body = body;
         this.raw = raw;
+        if (charset != null) {
+            this.charset = charset;
+        } else {
+            this.charset = CharsetUtil.US_ASCII;
+        }
     }
     
     /**
@@ -65,17 +91,24 @@
      *   <tr><td>{@link UnstructuredField}</td><td>Subject and others</td></tr>
      * </table>
      * 
-     * @param raw the string to parse.
+     * @param raw raw content to parse.
      * @return a <code>Field</code> instance.
      * @throws MimeException if the raw string cannot be split into field name and body.
      * @see #isValidField()
      */
-    public static Field parse(final String raw) throws MimeException {
-        
+    public static Field parse(
+            final ByteArrayBuffer raw, Charset charset) throws MimeException {
+        if (raw == null) {
+            throw new IllegalArgumentException("Raw content may not be null");
+        }
+        if (charset == null) {
+            charset = CharsetUtil.US_ASCII;
+        }
+
         /*
          * Unfold the field.
          */
-        final String unfolded = MimeUtil.unfold(raw);
+        final String unfolded = MimeUtil.unfold(ContentUtil.toString(raw, charset.name()));
         
         /*
          * Split into name and value.
@@ -91,10 +124,50 @@
             body = body.substring(1);
         }
         
-        return parser.parse(name, body, raw);
+        return parser.parse(name, body, raw, charset);
     }
 
     /**
+     * Parses the given string and returns an instance of the 
+     * <code>Field</code> class. The type of the class returned depends on
+     * the field name:
+     * <p>
+     * <table>
+     *   <tr><th>Class returned</th><th>Field names</th></tr>
+     *   <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr>
+     *   <tr><td>{@link ContentTransferEncodingField}</td><td>Content-Transfer-Encoding</td></tr>
+     *   <tr><td>{@link ContentDispositionField}</td><td>Content-Disposition</td></tr>
+     *   <tr><td>{@link DateTimeField}</td><td>Date, Resent-Date</td></tr>
+     *   <tr><td>{@link MailboxField}</td><td>Sender, Resent-Sender</td></tr>
+     *   <tr><td>{@link MailboxListField}</td><td>From, Resent-From</td></tr>
+     *   <tr><td>{@link AddressListField}</td><td>To, Cc, Bcc, Reply-To, Resent-To, Resent-Cc, Resent-Bcc</td></tr>
+     *   <tr><td>{@link UnstructuredField}</td><td>Subject and others</td></tr>
+     * </table>
+     * 
+     * @param raw raw content to parse.
+     * @return a <code>Field</code> instance.
+     * @throws MimeException if the raw string cannot be split into field name and body.
+     * @see #isValidField()
+     */
+    public static Field parse(
+            final String raw, Charset charset) throws MimeException {
+        if (raw == null) {
+            throw new IllegalArgumentException("Raw content may not be null");
+        }
+        if (charset == null) {
+            charset = CharsetUtil.US_ASCII;
+        }
+        byte[] b = ContentUtil.toBytes(raw, charset.name());
+        ByteArrayBuffer buffer = new ByteArrayBuffer(b.length);
+        buffer.append(b, 0, b.length);
+        return parse(buffer, charset);
+    }
+
+    public static Field parse(final String raw) throws MimeException {
+        return parse(raw, CharsetUtil.US_ASCII);
+    }
+    
+    /**
      * Parses the given field name and field body strings and returns an
      * instance of the <code>Field</code> class. The type of the class
      * returned depends on the field name (see {@link #parse(String)}).
@@ -114,16 +187,22 @@
      * @return a <code>Field</code> instance.
      */
     public static Field parse(String name, String body) {
+        Charset charset = CharsetUtil.US_ASCII;
         if (body.length() > 0 && body.charAt(0) == ' ') {
             body = body.substring(1);
         }
 
-        String raw = name + ": " + body;
-
+        String s = name + ": " + body;
+        
+        // Prepare buffer with raw content
+        byte[] b = ContentUtil.toBytes(s, charset.name());
+        ByteArrayBuffer raw = new ByteArrayBuffer(b.length);
+        raw.append(b, 0, b.length);
+        
         // Unfold body
         body = MimeUtil.unfold(body);
 
-        return parser.parse(name, body, raw);
+        return parser.parse(name, body, raw, charset);
     }
 
     /**
@@ -145,12 +224,21 @@
     }
     
     /**
+     * Gets the original raw field bytes.
+     * 
+     * @return the original raw field bytes.
+     */
+    public ByteArrayBuffer getRawBytes() {
+        return raw;
+    }
+    
+    /**
      * Gets the original raw field string.
      * 
      * @return the original raw field string.
      */
     public String getRaw() {
-        return raw;
+        return ContentUtil.toString(raw, charset.name());
     }
     
     /**
@@ -232,6 +320,6 @@
      */
     @Override
     public String toString() {
-        return raw;
+        return ContentUtil.toString(raw, charset.name());
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/UnstructuredField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/UnstructuredField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/UnstructuredField.java	(working copy)
@@ -19,8 +19,11 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.james.mime4j.codec.DecoderUtil;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Simple unstructured field such as <code>Subject</code>.
@@ -30,8 +33,12 @@
 
     private String value;
 
-    UnstructuredField(String name, String body, String raw) {
-        super(name, body, raw);
+    UnstructuredField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     public String getValue() {
@@ -51,8 +58,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new UnstructuredField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new UnstructuredField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/ContentTypeField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/ContentTypeField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/ContentTypeField.java	(working copy)
@@ -20,6 +20,7 @@
 package org.apache.james.mime4j.field;
 
 import java.io.StringReader;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -31,6 +32,7 @@
 import org.apache.james.mime4j.field.contenttype.parser.ParseException;
 import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Represents a <code>Content-Type</code> field.
@@ -62,8 +64,12 @@
     private Map<String, String> parameters = new HashMap<String, String>();
     private ParseException parseException;
 
-    ContentTypeField(String name, String body, String raw) {
-        super(name, body, raw);
+    ContentTypeField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     /**
@@ -251,8 +257,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new ContentTypeField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new ContentTypeField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/DelegatingFieldParser.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/DelegatingFieldParser.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/DelegatingFieldParser.java	(working copy)
@@ -19,10 +19,12 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 public class DelegatingFieldParser implements FieldParser {
     
@@ -46,8 +48,9 @@
         return field;
     }
     
-    public Field parse(final String name, final String body, final String raw) {
+    public Field parse(final String name, final String body, 
+            final ByteArrayBuffer raw, final Charset charset) {
         final FieldParser parser = getParser(name);
-        return parser.parse(name, body, raw);
+        return parser.parse(name, body, raw, charset);
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/AddressListField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/AddressListField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/AddressListField.java	(working copy)
@@ -19,11 +19,14 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.james.mime4j.field.address.AddressList;
 import org.apache.james.mime4j.field.address.parser.ParseException;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Address list field such as <code>To</code> or <code>Reply-To</code>.
@@ -36,8 +39,12 @@
     private AddressList addressList;
     private ParseException parseException;
 
-    AddressListField(String name, String body, String raw) {
-        super(name, body, raw);
+    AddressListField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     public AddressList getAddressList() {
@@ -72,8 +79,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new AddressListField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new AddressListField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/ContentTransferEncodingField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/ContentTransferEncodingField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/ContentTransferEncodingField.java	(working copy)
@@ -19,7 +19,10 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -28,8 +31,12 @@
 public class ContentTransferEncodingField extends AbstractField {
     private String encoding;
 
-    ContentTransferEncodingField(String name, String body, String raw) {
-        super(name, body, raw);
+    ContentTransferEncodingField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
         encoding = body.trim().toLowerCase();
     }
 
@@ -58,8 +65,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new ContentTransferEncodingField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new ContentTransferEncodingField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/field/MailboxListField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/field/MailboxListField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/field/MailboxListField.java	(working copy)
@@ -19,12 +19,15 @@
 
 package org.apache.james.mime4j.field;
 
+import java.nio.charset.Charset;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.james.mime4j.field.address.AddressList;
 import org.apache.james.mime4j.field.address.MailboxList;
 import org.apache.james.mime4j.field.address.parser.ParseException;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Mailbox-list field such as <code>From</code> or <code>Resent-From</code>.
@@ -37,8 +40,12 @@
     private MailboxList mailboxList;
     private ParseException parseException;
 
-    MailboxListField(final String name, final String body, final String raw) {
-        super(name, body, raw);
+    MailboxListField(
+            final String name, 
+            final String body, 
+            final ByteArrayBuffer raw,
+            final Charset charset) {
+        super(name, body, raw, charset);
     }
 
     public MailboxList getMailboxList() {
@@ -73,8 +80,8 @@
 
     static class Parser implements FieldParser {
         public Field parse(final String name, final String body,
-                final String raw) {
-            return new MailboxListField(name, body, raw);
+                final ByteArrayBuffer raw, final Charset charset) {
+            return new MailboxListField(name, body, raw, charset);
         }
     }
 }
Index: src/main/java/org/apache/james/mime4j/message/MessageWriter.java
===================================================================
--- src/main/java/org/apache/james/mime4j/message/MessageWriter.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/message/MessageWriter.java	(working copy)
@@ -19,22 +19,19 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
 
 import org.apache.james.mime4j.MimeIOException;
 import org.apache.james.mime4j.codec.CodecUtil;
 import org.apache.james.mime4j.field.ContentTypeField;
 import org.apache.james.mime4j.field.FieldName;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.parser.RawField;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.ContentUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -51,14 +48,25 @@
 public class MessageWriter {
 
     private static final Charset LENIENT_FALLBACK_CHARSET = CharsetUtil.ISO_8859_1;
-    private static final String CRLF = CharsetUtil.CRLF;
-    private static final int WRITER_BUFFER_SIZE = 4096;
+    private static final byte[] CR_LF = ContentUtil.toAsciiBytes(CharsetUtil.CRLF);
+    private static final byte[] TWO_DASHES = ContentUtil.toAsciiBytes("--");
 
     /**
      * A message writer that uses US-ASCII for encoding and throws
      * {@link MimeIOException} if a non ASCII character is encountered.
      */
-    public static final MessageWriter STRICT_ERROR = new MessageWriter();
+    public static final MessageWriter STRICT_ERROR = new MessageWriter() {
+        @Override
+        void writeBytes(byte[] b, int off, int len, OutputStream out) 
+                throws IOException, MimeIOException {
+            for (int i = off; i < off + len; i++) {
+                if (!CharsetUtil.isASCII((char) (b[i] & 0xff))) {
+                    throw new MimeIOException("MIME element may not contain non ASCII characters");
+                }
+            }
+            out.write(b, off, len);
+        }
+    };
 
     /**
      * A message writer that uses US-ASCII for encoding but ignores non ASCII
@@ -66,8 +74,16 @@
      */
     public static final MessageWriter STRICT_IGNORE = new MessageWriter() {
         @Override
-        protected CharsetEncoder getCharsetEncoder(ContentTypeField contentType) {
-            return ignoreEncoder(CharsetUtil.DEFAULT_CHARSET);
+        void writeBytes(byte[] b, int off, int len, OutputStream out) 
+                throws IOException, MimeIOException {
+            for (int i = off; i < off + len; i++) {
+                int ch = (b[i] & 0xff);
+                if (CharsetUtil.isASCII((char) ch)) {
+                    out.write(ch);
+                } else {
+                    out.write('?');
+                }
+            }
         }
     };
 
@@ -77,15 +93,15 @@
      */
     public static final MessageWriter LENIENT = new MessageWriter() {
         @Override
-        protected CharsetEncoder getCharsetEncoder(ContentTypeField contentType) {
+        Charset getCharset(ContentTypeField contentType) {
             if (contentType == null) {
-                return ignoreEncoder(LENIENT_FALLBACK_CHARSET);
+                return LENIENT_FALLBACK_CHARSET;
             } else {
                 String charset = contentType.getCharset();
                 if (charset != null) {
-                    return ignoreEncoder(CharsetUtil.getCharset(charset));
+                    return CharsetUtil.getCharset(charset);
                 } else {
-                    return ignoreEncoder(LENIENT_FALLBACK_CHARSET);
+                    return LENIENT_FALLBACK_CHARSET;
                 }
             }
         }
@@ -97,6 +113,26 @@
     protected MessageWriter() {
     }
 
+    Charset getCharset(ContentTypeField contentType) {
+        return CharsetUtil.DEFAULT_CHARSET;
+    }
+
+    void writeBytes(byte[] b, int off, int len, OutputStream out) 
+        throws IOException, MimeIOException {
+        out.write(b, off, len);
+    }
+    
+    void writeBytes(byte[] b, OutputStream out) 
+        throws IOException, MimeIOException {
+        writeBytes(b, 0, b.length, out);
+    }
+
+    void writeString(String s, Charset charset, OutputStream out) 
+        throws IOException, MimeIOException {
+        byte[] b = ContentUtil.toBytes(s, charset.name());
+        writeBytes(b, 0, b.length, out);
+    }
+
     /**
      * Write the specified <code>Body</code> to the specified
      * <code>OutputStream</code>.
@@ -127,6 +163,8 @@
     /**
      * Write the specified <code>Entity</code> to the specified
      * <code>OutputStream</code>.
+     * <p>
+     * Please note this method does not flush the output stream.
      * 
      * @param entity
      *            the <code>Entity</code> to write.
@@ -144,7 +182,6 @@
             throw new IllegalArgumentException("Missing header");
 
         writeHeader(header, out);
-        out.flush();
 
         final Body body = entity.getBody();
         if (body == null)
@@ -164,6 +201,8 @@
     /**
      * Write the specified <code>Multipart</code> to the specified
      * <code>OutputStream</code>.
+     * <p>
+     * Please note this method does not flush the output stream.
      * 
      * @param multipart
      *            the <code>Multipart</code> to write.
@@ -177,41 +216,31 @@
     public void writeMultipart(Multipart multipart, OutputStream out)
             throws IOException, MimeIOException {
         ContentTypeField contentType = getContentType(multipart);
-
         String boundary = getBoundary(contentType);
+        Charset charset = getCharset(contentType);
+        writeString(multipart.getPreamble(), charset, out);
+        out.write(CR_LF);
 
-        Writer writer = getWriter(contentType, out);
+        for (BodyPart bodyPart : multipart.getBodyParts()) {
+            out.write(TWO_DASHES);
+            writeString(boundary, charset, out);
+            out.write(CR_LF);
+            writeEntity(bodyPart, out);
+            out.write(CR_LF);
+        }
+        out.write(TWO_DASHES);
+        writeString(boundary, charset, out);
+        out.write(TWO_DASHES);
+        out.write(CR_LF);
 
-        try {
-            writer.write(multipart.getPreamble());
-            writer.write(CRLF);
-
-            for (BodyPart bodyPart : multipart.getBodyParts()) {
-                writer.write("--");
-                writer.write(boundary);
-                writer.write(CRLF);
-                writer.flush();
-
-                writeEntity(bodyPart, out);
-                writer.write(CRLF);
-            }
-
-            writer.write("--");
-            writer.write(boundary);
-            writer.write("--");
-            writer.write(CRLF);
-
-            writer.write(multipart.getEpilogue());
-
-            writer.flush();
-        } catch (CharacterCodingException e) {
-            throw new MimeIOException("Multipart violates RFC 822");
-        }
+        writeString(multipart.getEpilogue(), charset, out);
     }
 
     /**
      * Write the specified <code>Header</code> to the specified
      * <code>OutputStream</code>.
+     * <p>
+     * Please note this method does not flush the output stream.
      * 
      * @param header
      *            the <code>Header</code> to write.
@@ -224,31 +253,25 @@
      */
     public void writeHeader(Header header, OutputStream out)
             throws IOException, MimeIOException {
-        Writer writer = getWriter((ContentTypeField) header
-                .getField(FieldName.CONTENT_TYPE), out);
-
-        try {
-            for (Field field : header) {
-                writer.write(field.getRaw());
-                writer.write(CRLF);
+        Charset charset = null;
+        for (Field field : header) {
+            if (field instanceof RawField) {
+                ByteArrayBuffer raw = ((RawField) field).getRawBytes();
+                writeBytes(raw.buffer(), 0, raw.length(), out);
+            } else {
+                if (charset == null) {
+                    ContentTypeField ctf = (ContentTypeField) header.getField(
+                            FieldName.CONTENT_TYPE);
+                    charset = getCharset(ctf);
+                }
+                byte[] raw = ContentUtil.toBytes(field.getRaw(), charset.name());
+                writeBytes(raw, out);
             }
-
-            writer.write(CRLF);
-            writer.flush();
-        } catch (CharacterCodingException e) {
-            throw new MimeIOException("Header violates RFC 822");
+            out.write(CR_LF);
         }
+        out.write(CR_LF);
     }
 
-    CharsetEncoder getCharsetEncoder(ContentTypeField contentType) {
-        return CharsetUtil.DEFAULT_CHARSET.newEncoder();
-    }
-
-    CharsetEncoder ignoreEncoder(Charset charset) {
-        return charset.newEncoder().onMalformedInput(CodingErrorAction.REPLACE)
-                .onUnmappableCharacter(CodingErrorAction.REPLACE);
-    }
-
     private OutputStream encodeStream(OutputStream out, String encoding,
             boolean binaryBody) throws IOException {
         if (MimeUtil.isBase64Encoding(encoding)) {
@@ -289,11 +312,4 @@
         return boundary;
     }
 
-    private Writer getWriter(ContentTypeField contentType, OutputStream out) {
-        CharsetEncoder encoder = getCharsetEncoder(contentType);
-
-        return new BufferedWriter(new OutputStreamWriter(out, encoder),
-                WRITER_BUFFER_SIZE);
-    }
-
 }
Index: src/main/java/org/apache/james/mime4j/message/SimpleContentHandler.java
===================================================================
--- src/main/java/org/apache/james/mime4j/message/SimpleContentHandler.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/message/SimpleContentHandler.java	(working copy)
@@ -26,10 +26,14 @@
 import org.apache.james.mime4j.field.AbstractField;
 import org.apache.james.mime4j.parser.AbstractContentHandler;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.parser.RawField;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.CharsetUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 import java.io.InputStream;
 import java.io.IOException;
+import java.nio.charset.Charset;
 
 /**
  * Abstract implementation of ContentHandler that automates common
@@ -77,7 +81,14 @@
      */
     @Override
     public final void field(Field field) throws MimeException {
-        Field parsedField = AbstractField.parse(field.getRaw()); 
+        Charset charset = CharsetUtil.US_ASCII;
+        Field parsedField;
+        if (field instanceof RawField) {
+            ByteArrayBuffer raw = ((RawField) field).getRawBytes();
+            parsedField = AbstractField.parse(raw, charset); 
+        } else {
+            parsedField = AbstractField.parse(field.getRaw(), charset); 
+        }
         currHeader.addField(parsedField);
     }
 
Index: src/main/java/org/apache/james/mime4j/message/MessageBuilder.java
===================================================================
--- src/main/java/org/apache/james/mime4j/message/MessageBuilder.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/message/MessageBuilder.java	(working copy)
@@ -21,6 +21,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Stack;
 
 import org.apache.james.mime4j.MimeException;
@@ -31,7 +32,10 @@
 import org.apache.james.mime4j.parser.ContentHandler;
 import org.apache.james.mime4j.parser.Field;
 import org.apache.james.mime4j.parser.MimeStreamParser;
+import org.apache.james.mime4j.parser.RawField;
 import org.apache.james.mime4j.storage.StorageProvider;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.CharsetUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -96,7 +100,14 @@
      */
     public void field(Field field) throws MimeException {
         expect(Header.class);
-        Field parsedField = AbstractField.parse(field.getRaw()); 
+        Charset charset = CharsetUtil.US_ASCII;
+        Field parsedField;
+        if (field instanceof RawField) {
+            ByteArrayBuffer raw = ((RawField) field).getRawBytes();
+            parsedField = AbstractField.parse(raw, charset); 
+        } else {
+            parsedField = AbstractField.parse(field.getRaw(), charset); 
+        }
         ((Header) stack.peek()).addField(parsedField);
     }
     
Index: src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java
===================================================================
--- src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java	(working copy)
@@ -33,7 +33,6 @@
 import org.apache.james.mime4j.io.MaxHeaderLimitException;
 import org.apache.james.mime4j.io.MaxLineLimitException;
 import org.apache.james.mime4j.util.ByteArrayBuffer;
-import org.apache.james.mime4j.util.CharArrayBuffer;
 import org.apache.james.mime4j.util.CharsetUtil;
 
 /**
@@ -48,16 +47,14 @@
     protected final int endState;
     protected final MimeEntityConfig config;
     protected final MutableBodyDescriptor body;
+    private final ByteArrayBuffer linebuf;
     
     protected int state;
-
-    private final ByteArrayBuffer linebuf;
-    private final CharArrayBuffer fieldbuf;
-
     private int lineCount;
     private Field field;
     private boolean endOfHeader;
     private int headerCount;
+    private ByteArrayBuffer fieldbuf;
 
     private static final BitSet fieldChars = new BitSet();
 
@@ -92,7 +89,6 @@
         this.config = config;
         this.body = newBodyDescriptor(parent);
         this.linebuf = new ByteArrayBuffer(64);
-        this.fieldbuf = new CharArrayBuffer(64);
         this.lineCount = 0;
         this.endOfHeader = false;
         this.headerCount = 0;
@@ -110,9 +106,9 @@
     protected MutableBodyDescriptor newBodyDescriptor(BodyDescriptor pParent) {
         final MutableBodyDescriptor result;
         if (config.isMaximalBodyDescriptor()) {
-            result = new MaximalBodyDescriptor(pParent);
+            result = new MaximalBodyDescriptor(pParent, config.isStrictParsing());
         } else {
-            result = new DefaultBodyDescriptor(pParent);
+            result = new DefaultBodyDescriptor(pParent, config.isStrictParsing());
         }
         return result;
     }
@@ -131,7 +127,11 @@
         }
         int maxLineLen = config.getMaxLineLen();
         LineReaderInputStream instream = getDataStream();
-        fieldbuf.clear();
+        if (fieldbuf == null) {
+            fieldbuf = new ByteArrayBuffer(64);
+        } else {
+            fieldbuf.clear();
+        }
         for (;;) {
             // If there's still data stuck in the line buffer
             // copy it to the field buffer
@@ -140,7 +140,7 @@
                 throw new MaxLineLimitException("Maximum line length limit exceeded");
             }
             if (len > 0) {
-                fieldbuf.append(linebuf, 0, len);
+                fieldbuf.append(linebuf.buffer(), 0, len);
             }
             linebuf.clear();
             if (instream.readLine(linebuf) == -1) {
@@ -186,36 +186,32 @@
             
             // Strip away line delimiter
             int len = fieldbuf.length();
-            if (len > 0 && fieldbuf.charAt(len - 1) == '\n') {
+            if (len > 0 && fieldbuf.byteAt(len - 1) == '\n') {
                 len--;
             }
-            if (len > 0 && fieldbuf.charAt(len - 1) == '\r') {
+            if (len > 0 && fieldbuf.byteAt(len - 1) == '\r') {
                 len--;
             }
             fieldbuf.setLength(len);
             
             boolean valid = true;
             
-            String fieldName = null;
-            String fieldValue = null;
-            
             int pos = fieldbuf.indexOf(':');
             if (pos <= 0) {
                 monitor(Event.INALID_HEADER);
                 valid = false;
             } else {
-                fieldName = fieldbuf.substring(0, pos);
-                for (int i = 0; i < fieldName.length(); i++) {
-                    if (!fieldChars.get(fieldName.charAt(i))) {
+                for (int i = 0; i < pos; i++) {
+                    if (!fieldChars.get(fieldbuf.byteAt(i))) {
                         monitor(Event.INALID_HEADER);
                         valid = false;
                         break;
                     }
                 }
-                fieldValue = fieldbuf.substring(pos + 1, fieldbuf.length());
             }
             if (valid) {
-                field = new RawField(fieldName, fieldValue, fieldbuf.toString());
+                field = new RawFieldImpl(fieldbuf);
+                fieldbuf = null;
                 body.addField(field);            
                 return true;
             }
Index: src/main/java/org/apache/james/mime4j/parser/RawFieldImpl.java
===================================================================
--- src/main/java/org/apache/james/mime4j/parser/RawFieldImpl.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/parser/RawFieldImpl.java	(working copy)
@@ -19,20 +19,30 @@
 
 package org.apache.james.mime4j.parser;
 
-/**
- * The basic immutable MIME field.
- */
-public class RawField implements Field {
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.ContentUtil;
+
+class RawFieldImpl implements RawField {
     
     private final String name;
-    private final String body;
-    private final String raw;
+    private final ByteArrayBuffer raw;
+    private final int valuepos;
     
-    public RawField(String name, String body, String raw) {
+    private String body;
+    
+    public RawFieldImpl(ByteArrayBuffer raw) {
         super();
-        this.name = name;
-        this.body = body;
+        if (raw == null) {
+            throw new IllegalArgumentException("Raw content buffer not be null");
+        }
         this.raw = raw;
+        int i = raw.indexOf(':');
+        if (i == -1) {
+            throw new IllegalArgumentException("Invalid MIME field: " + 
+                    ContentUtil.toAsciiString(raw));
+        }
+        this.valuepos = i + 1;
+        this.name = ContentUtil.toAsciiString(raw.buffer(), 0, i);
     }
     
     public String getName() {
@@ -40,15 +50,25 @@
     }
 
     public String getBody() {
+        if (this.body == null) {
+            int off = this.valuepos;
+            int len = this.raw.length() - off;
+            this.body = ContentUtil.toAsciiString(this.raw.buffer(), off, len);
+        }
         return this.body;
     }
 
-    public String getRaw() {
+    public ByteArrayBuffer getRawBytes() {
         return this.raw;
     }
+
+    public String getRaw() {
+        return ContentUtil.toAsciiString(this.raw);
+    }
     
+    @Override
     public String toString() {
-        return this.raw;
+        return ContentUtil.toAsciiString(this.raw);
     }
     
 }
Index: src/main/java/org/apache/james/mime4j/parser/MimeEntity.java
===================================================================
--- src/main/java/org/apache/james/mime4j/parser/MimeEntity.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/parser/MimeEntity.java	(working copy)
@@ -32,6 +32,7 @@
 import org.apache.james.mime4j.io.LineReaderInputStream;
 import org.apache.james.mime4j.io.LineReaderInputStreamAdaptor;
 import org.apache.james.mime4j.io.MimeBoundaryInputStream;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 import org.apache.james.mime4j.util.MimeUtil;
 
 public class MimeEntity extends AbstractEntity {
@@ -94,10 +95,10 @@
             throw new IllegalStateException("Invalid state: " + stateToString(state));
         }
         skipHeader = true;
-        body.addField(new RawField(
-                "Content-Type", 
-                contentType,
-                "Content-Type: " +contentType));
+        ByteArrayBuffer raw = new ByteArrayBuffer(64);
+        char[] b = ("Content-Type: " + contentType).toCharArray();
+        raw.append(b, 0, b.length);
+        body.addField(new RawFieldImpl(raw));
     }
     
     @Override
Index: src/main/java/org/apache/james/mime4j/parser/RawField.java
===================================================================
--- src/main/java/org/apache/james/mime4j/parser/RawField.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/parser/RawField.java	(working copy)
@@ -19,37 +19,13 @@
 
 package org.apache.james.mime4j.parser;
 
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+
 /**
- * The basic immutable MIME field.
+ * Abstract MIME field backed by the original {@link ByteArrayBuffer}.
  */
-public class RawField implements Field {
+public interface RawField extends Field {
 
-    private final String name;
-    private final String body;
-    private final String raw;
+    ByteArrayBuffer getRawBytes();
 
-    public RawField(String name, String body, String raw) {
-        super();
-        this.name = name;
-        this.body = body;
-        this.raw = raw;
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    public String getBody() {
-        return this.body;
-    }
-
-    public String getRaw() {
-        return this.raw;
-    }
-
-    @Override
-    public String toString() {
-        return this.raw;
-    }
-
 }
Index: src/main/java/org/apache/james/mime4j/util/ContentUtil.java
===================================================================
--- src/main/java/org/apache/james/mime4j/util/ContentUtil.java	(revision 0)
+++ src/main/java/org/apache/james/mime4j/util/ContentUtil.java	(revision 0)
@@ -0,0 +1,202 @@
+/****************************************************************
+ * 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.io.UnsupportedEncodingException;
+
+/**
+ * Utility methods for converting textual content.
+ */
+public final class ContentUtil {
+
+    private static final String US_ASCII = "US-ASCII";
+    
+    /**
+     * Converts the array of bytes to a string using <code>US-ASCII</code>. 
+     * If for some reason <code>US-ASCII</code> is not supported, the content 
+     * is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * @param  off     the index of the first byte to decode
+     * @param  len     the number of bytes to decode
+     * 
+     * @return textual representation of the content
+     */
+    public static String toAsciiString(byte[] b, int off, int len) {
+        if (b == null) {
+            return null;
+        }
+        try {
+            return new String(b, off, len, US_ASCII);
+        } catch (UnsupportedEncodingException ex2) {
+            // should never happen
+            return new String(b, off, len);
+        }
+    }
+    
+    /**
+     * Converts the array of bytes to a string using <code>US-ASCII</code>. 
+     * If for some reason <code>US-ASCII</code> is not supported, the content 
+     * is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * 
+     * @return textual representation of the content
+     */
+    public static String toAsciiString(byte[] b) {
+        return toAsciiString(b, 0, b.length);
+    }
+
+    /**
+     * Converts the array of bytes to a string using <code>US-ASCII</code>. 
+     * If for some reason <code>US-ASCII</code> is not supported, the content 
+     * is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * 
+     * @return textual representation of the content
+     */
+    public static String toAsciiString(ByteArrayBuffer b) {
+        if (b == null) {
+            return null;
+        }
+        try {
+            return new String(b.buffer(), 0, b.length(), US_ASCII);
+        } catch (UnsupportedEncodingException ex2) {
+            // should never happen
+            return new String(b.buffer(), 0, b.length());
+        }
+    }
+    
+    /**
+     * Converts the array of bytes to a string using given </code>charset<code>.
+     * If the </code>charset<code> is not supported, the content is decoded
+     * using <code>US-ASCII</code>. If for some reason <code>US-ASCII</code>
+     * is not supported, the content is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * @param  off     the index of the first byte to decode
+     * @param  len     the number of bytes to decode
+     * @param  charset the name of a supported {@link java.nio.charset.Charset}
+     * 
+     * @return textual representation of the content
+     */
+    public static String toString(byte[] b, int off, int len, String charset) {
+        if (b == null) {
+            return null;
+        }
+        if (charset == null) {
+            charset = US_ASCII;
+        }
+        try {
+            return new String(b, off, len, charset);
+        } catch (UnsupportedEncodingException ex) {
+            return toAsciiString(b, off, len);
+        }
+    }
+    
+    /**
+     * Converts the array of bytes to a string using given </code>charset<code>.
+     * If the </code>charset<code> is not supported, the content is decoded
+     * using <code>US-ASCII</code>. If for some reason <code>US-ASCII</code>
+     * is not supported, the content is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * @param  charset the name of a supported {@link java.nio.charset.Charset}
+     * 
+     * @return textual representation of the content
+     */
+    public static String toString(byte[] b, String charset) {
+        return toString(b, 0, b.length, charset);
+    }
+    
+    /**
+     * Converts the array of bytes to a string using given </code>charset<code>.
+     * If the </code>charset<code> is not supported, the content is decoded
+     * using <code>US-ASCII</code>. If for some reason <code>US-ASCII</code>
+     * is not supported, the content is decoded using the default system coding. 
+     * 
+     * @param  b       the bytes to be decoded into characters
+     * @param  charset the name of a supported {@link java.nio.charset.Charset}
+     * 
+     * @return textual representation of the content
+     */
+    public static String toString(ByteArrayBuffer b, String charset) {
+        if (b == null) {
+            return null;
+        }
+        if (charset == null) {
+            charset = US_ASCII;
+        }
+        try {
+            return new String(b.buffer(), 0, b.length(), charset);
+        } catch (UnsupportedEncodingException ex) {
+            return toAsciiString(b.buffer(), 0, b.length());
+        }
+    }
+ 
+    /**
+     * Converts the string to a byte array using given </code>charset<code>.
+     * If the </code>charset<code> is not supported, the content is encoded
+     * using <code>US-ASCII</code>. If for some reason <code>US-ASCII</code> 
+     * is not supported, the content is encoded using the default system coding. 
+     * 
+     * @param  s       the string to be encoded into bytes
+     * 
+     * @return binary representation of the content
+     */
+    public static byte[] toAsciiBytes(String s) {
+        if (s == null) {
+            return null;
+        }
+        try {
+            return s.getBytes(US_ASCII);
+        } catch (UnsupportedEncodingException ex2) {
+            // should never happen
+            return s.getBytes();
+        }
+    }
+    
+    /**
+     * Converts the string to a byte array using <code>US-ASCII</code> coding. 
+     * If for some reason <code>US-ASCII</code> is not supported, the content 
+     * is encoded using the default system coding. 
+     * 
+     * @param  s       the string to be encoded into bytes
+     * @param  charset the name of a supported {@link java.nio.charset.Charset}
+     * 
+     * @return binary representation of the content
+     */
+    public static byte[] toBytes(String s, String charset) {
+        if (s == null) {
+            return null;
+        }
+        if (charset == null) {
+            charset = US_ASCII;
+        }
+        try {
+            return s.getBytes(charset);
+        } catch (UnsupportedEncodingException ex2) {
+            // should never happen
+            return toAsciiBytes(s);
+        }
+    }
+    
+}
\ No newline at end of file
Index: src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java
===================================================================
--- src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java	(working copy)
@@ -118,7 +118,29 @@
     public byte[] buffer() {
         return this.buffer;
     }
-        
+
+    public int indexOf(int b, int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            beginIndex = 0;
+        }
+        if (endIndex > this.len) {
+            endIndex = this.len;
+        }
+        if (beginIndex > endIndex) {
+            return -1;
+        }
+        for (int i = beginIndex; i < endIndex; i++) {
+            if (this.buffer[i] == b) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public int indexOf(int b) {
+        return indexOf(b, 0, this.len);
+    }
+
     public void setLength(int len) {
         if (len < 0 || len > this.buffer.length) {
             throw new IndexOutOfBoundsException();
Index: src/main/java/org/apache/james/mime4j/descriptor/DefaultBodyDescriptor.java
===================================================================
--- src/main/java/org/apache/james/mime4j/descriptor/DefaultBodyDescriptor.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/descriptor/DefaultBodyDescriptor.java	(working copy)
@@ -19,12 +19,17 @@
 
 package org.apache.james.mime4j.descriptor;
 
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.parser.RawField;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.CharsetUtil;
+import org.apache.james.mime4j.util.ContentUtil;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -50,6 +55,8 @@
 
     private static Log log = LogFactory.getLog(DefaultBodyDescriptor.class);
     
+    private final boolean strictParsing;
+    
     private String mediaType = DEFAULT_MEDIA_TYPE;
     private String subType = DEFAULT_SUB_TYPE;
     private String mimeType = DEFAULT_MIME_TYPE;
@@ -63,9 +70,12 @@
     
     /**
      * Creates a new root <code>BodyDescriptor</code> instance.
+     * 
+     * @param strictParsing <code>true</code> if the strict parsing mode is on,
+     *   <code>false</code> otherwise.
      */
-    public DefaultBodyDescriptor() {
-        this(null);
+    public DefaultBodyDescriptor(boolean strictParsing) {
+        this(null, strictParsing);
     }
 
     /**
@@ -73,8 +83,11 @@
      * 
      * @param parent the descriptor of the parent or <code>null</code> if this
      *        is the root descriptor.
+     * @param strictParsing <code>true</code> if the strict parsing mode is on,
+     *   <code>false</code> otherwise.
      */
-    public DefaultBodyDescriptor(BodyDescriptor parent) {
+    public DefaultBodyDescriptor(BodyDescriptor parent, boolean strictParsing) {
+        this.strictParsing =  strictParsing;
         if (parent != null && MimeUtil.isSameMimeType("multipart/digest", parent.getMimeType())) {
             mimeType = EMAIL_MESSAGE_MIME_TYPE;
             subType = SUB_TYPE_EMAIL;
@@ -87,17 +100,50 @@
     }
     
     /**
+     * Creates a new root <code>BodyDescriptor</code> instance.
+     */
+    public DefaultBodyDescriptor() {
+        this(null, false);
+    }
+
+    /**
+     * Creates a new <code>BodyDescriptor</code> instance.
+     * 
+     * @param parent the descriptor of the parent or <code>null</code> if this
+     *        is the root descriptor.
+     */
+    public DefaultBodyDescriptor(BodyDescriptor parent) {
+        this(parent, false);
+    }
+    
+    /**
      * Should be called for each <code>Content-</code> header field of 
      * a MIME message or part.
      * 
      * @param field the MIME field.
      */
-    public void addField(Field field) {
+    public final void addField(Field field) {
         String name = field.getName();
-        String value = field.getBody();
-
         name = name.trim().toLowerCase();
-        
+        String value = null;
+        if (field instanceof RawField) {
+            ByteArrayBuffer raw = ((RawField) field).getRawBytes();
+            int i = raw.indexOf(':');
+            if (i != -1) {
+                Charset enc = strictParsing ? 
+                        CharsetUtil.DEFAULT_CHARSET : CharsetUtil.getCharset(charset);
+                
+                int off = i + 1;
+                int len = raw.length() - off;
+                value = ContentUtil.toString(raw.buffer(), off, len, enc.name());
+            }
+        } else {
+            value = field.getBody();
+        }
+        addField(name, value);
+    }
+    
+    protected void addField(String name, String value) {
         if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {
             contentTransferEncSet = true;
             
Index: src/main/java/org/apache/james/mime4j/descriptor/MaximalBodyDescriptor.java
===================================================================
--- src/main/java/org/apache/james/mime4j/descriptor/MaximalBodyDescriptor.java	(revision 746269)
+++ src/main/java/org/apache/james/mime4j/descriptor/MaximalBodyDescriptor.java	(working copy)
@@ -31,7 +31,6 @@
 import org.apache.james.mime4j.field.language.parser.ContentLanguageParser;
 import org.apache.james.mime4j.field.mimeversion.parser.MimeVersionParser;
 import org.apache.james.mime4j.field.structured.parser.StructuredFieldParser;
-import org.apache.james.mime4j.parser.Field;
 import org.apache.james.mime4j.util.MimeUtil;
 
 /**
@@ -70,12 +69,20 @@
     private String contentMD5Raw;
     private boolean isContentMD5Set;
     
+    protected MaximalBodyDescriptor(boolean strictParsing) {
+        this(null, strictParsing);
+    }
+
+    protected MaximalBodyDescriptor(BodyDescriptor parent) {
+        this(parent, false);
+    }
+
     protected MaximalBodyDescriptor() {
-        this(null);
+        this(null, false);
     }
 
-    public MaximalBodyDescriptor(BodyDescriptor parent) {
-        super(parent);
+    public MaximalBodyDescriptor(BodyDescriptor parent, boolean strictParsing) {
+        super(parent, strictParsing);
         isMimeVersionSet = false;
         mimeMajorVersion = DEFAULT_MAJOR_VERSION;
         mimeMinorVersion = DEFAULT_MINOR_VERSION;
@@ -105,10 +112,7 @@
     }
     
     @Override
-    public void addField(Field field) {
-        String name = field.getName();
-        String value = field.getBody();
-        name = name.trim().toLowerCase();
+    protected void addField(String name, String value) {
         if (MimeUtil.MIME_HEADER_MIME_VERSION.equals(name) && !isMimeVersionSet) {
             parseMimeVersion(value);
         } else if (MimeUtil.MIME_HEADER_CONTENT_ID.equals(name) && !isContentIdSet) {
@@ -124,7 +128,7 @@
         } else if (MimeUtil.MIME_HEADER_MD5.equals(name) && !isContentMD5Set) {
             parseMD5(value);
         } else {
-            super.addField(field);
+            super.addField(name, value);
         }
     }
     
