diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index 5a4d7a34a..3735f1f58 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -135,6 +135,9 @@ public abstract class Message implements Part, CompositeBody { @Override public abstract void addHeader(String name, String value) throws MessagingException; + @Override + public abstract void addRawHeader(String name, String raw) throws MessagingException; + @Override public abstract void setHeader(String name, String value) throws MessagingException; diff --git a/src/com/fsck/k9/mail/Part.java b/src/com/fsck/k9/mail/Part.java index 38c1ad9da..d7970d433 100644 --- a/src/com/fsck/k9/mail/Part.java +++ b/src/com/fsck/k9/mail/Part.java @@ -7,6 +7,8 @@ import java.io.OutputStream; public interface Part { public void addHeader(String name, String value) throws MessagingException; + public void addRawHeader(String name, String raw) throws MessagingException; + public void removeHeader(String name) throws MessagingException; public void setHeader(String name, String value) throws MessagingException; diff --git a/src/com/fsck/k9/mail/internet/MimeBodyPart.java b/src/com/fsck/k9/mail/internet/MimeBodyPart.java index c4bca428a..a542e47fc 100644 --- a/src/com/fsck/k9/mail/internet/MimeBodyPart.java +++ b/src/com/fsck/k9/mail/internet/MimeBodyPart.java @@ -47,6 +47,11 @@ public class MimeBodyPart extends BodyPart { mHeader.addHeader(name, value); } + @Override + public void addRawHeader(String name, String raw) { + mHeader.addRawHeader(name, raw); + } + @Override public void setHeader(String name, String value) { mHeader.setHeader(name, value); diff --git a/src/com/fsck/k9/mail/internet/MimeHeader.java b/src/com/fsck/k9/mail/internet/MimeHeader.java index 12bd5fd31..f7313a4f2 100644 --- a/src/com/fsck/k9/mail/internet/MimeHeader.java +++ b/src/com/fsck/k9/mail/internet/MimeHeader.java @@ -52,7 +52,13 @@ public class MimeHeader { } public void addHeader(String name, String value) { - mFields.add(new Field(name, MimeUtility.foldAndEncode(value))); + Field field = Field.newNameValueField(name, MimeUtility.foldAndEncode(value)); + mFields.add(field); + } + + void addRawHeader(String name, String raw) { + Field field = Field.newRawField(name, raw); + mFields.add(field); } public void setHeader(String name, String value) { @@ -66,7 +72,7 @@ public class MimeHeader { public Set getHeaderNames() { Set names = new LinkedHashSet(); for (Field field : mFields) { - names.add(field.name); + names.add(field.getName()); } return names; } @@ -74,8 +80,8 @@ public class MimeHeader { public String[] getHeader(String name) { List values = new ArrayList(); for (Field field : mFields) { - if (field.name.equalsIgnoreCase(name)) { - values.add(field.value); + if (field.getName().equalsIgnoreCase(name)) { + values.add(field.getValue()); } } if (values.isEmpty()) { @@ -87,7 +93,7 @@ public class MimeHeader { public void removeHeader(String name) { List removeFields = new ArrayList(); for (Field field : mFields) { - if (field.name.equalsIgnoreCase(name)) { + if (field.getName().equalsIgnoreCase(name)) { removeFields.add(field); } } @@ -97,27 +103,35 @@ public class MimeHeader { public void writeTo(OutputStream out) throws IOException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); for (Field field : mFields) { - if (!Utility.arrayContains(writeOmitFields, field.name)) { - String v = field.value; - - if (hasToBeEncoded(v)) { - Charset charset = null; - - if (mCharset != null) { - charset = Charset.forName(mCharset); - } - v = EncoderUtil.encodeEncodedWord(field.value, charset); + if (!Utility.arrayContains(writeOmitFields, field.getName())) { + if (field.hasRawData()) { + writer.write(field.getRaw()); + } else { + writeNameValueField(writer, field); } - - writer.write(field.name); - writer.write(": "); - writer.write(v); writer.write("\r\n"); } } writer.flush(); } + private void writeNameValueField(BufferedWriter writer, Field field) throws IOException { + String value = field.getValue(); + + if (hasToBeEncoded(value)) { + Charset charset = null; + + if (mCharset != null) { + charset = Charset.forName(mCharset); + } + value = EncoderUtil.encodeEncodedWord(field.getValue(), charset); + } + + writer.write(field.getName()); + writer.write(": "); + writer.write(value); + } + // encode non printable characters except LF/CR/TAB codes. private boolean hasToBeEncoded(String text) { for (int i = 0; i < text.length(); i++) { @@ -133,19 +147,67 @@ public class MimeHeader { private static class Field { private final String name; - private final String value; + private final String raw; + + public static Field newNameValueField(String name, String value) { + if (value == null) { + throw new NullPointerException("Argument 'value' cannot be null"); + } + + return new Field(name, value, null); + } + + public static Field newRawField(String name, String raw) { + if (raw == null) { + throw new NullPointerException("Argument 'raw' cannot be null"); + } + if (name != null && !raw.startsWith(name + ":")) { + throw new IllegalArgumentException("The value of 'raw' needs to start with the supplied field name " + + "followed by a colon"); + } + + return new Field(name, null, raw); + } + + private Field(String name, String value, String raw) { + if (name == null) { + throw new NullPointerException("Argument 'name' cannot be null"); + } - public Field(String name, String value) { this.name = name; this.value = value; + this.raw = raw; + } + + public String getName() { + return name; + } + + public String getValue() { + if (value != null) { + return value; + } + + int delimiterIndex = raw.indexOf(':'); + if (delimiterIndex == raw.length() - 1) { + return ""; + } + + return raw.substring(delimiterIndex + 1).trim(); + } + + public String getRaw() { + return raw; + } + + public boolean hasRawData() { + return raw != null; } @Override public String toString() { - StringBuilder sb = new StringBuilder("("); - sb.append(name).append('=').append(value).append(')'); - return sb.toString(); + return (hasRawData()) ? getRaw() : getName() + ": " + getValue(); } } diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java index 75fa217d8..c870fda35 100644 --- a/src/com/fsck/k9/mail/internet/MimeMessage.java +++ b/src/com/fsck/k9/mail/internet/MimeMessage.java @@ -423,6 +423,11 @@ public class MimeMessage extends Message { mHeader.addHeader(name, value); } + @Override + public void addRawHeader(String name, String raw) { + mHeader.addRawHeader(name, raw); + } + @Override public void setHeader(String name, String value) throws UnavailableStorageException { mHeader.setHeader(name, value); @@ -598,7 +603,9 @@ public class MimeMessage extends Message { public void field(Field parsedField) throws MimeException { expect(Part.class); try { - ((Part)stack.peek()).addHeader(parsedField.getName(), parsedField.getBody().trim()); + String name = parsedField.getName(); + String raw = parsedField.getRaw().toString(); + ((Part) stack.peek()).addRawHeader(name, raw); } catch (MessagingException me) { throw new Error(me); } diff --git a/src/com/fsck/k9/mail/store/local/LocalMessage.java b/src/com/fsck/k9/mail/store/local/LocalMessage.java index 474a4c849..ba3274b54 100644 --- a/src/com/fsck/k9/mail/store/local/LocalMessage.java +++ b/src/com/fsck/k9/mail/store/local/LocalMessage.java @@ -507,6 +507,11 @@ public class LocalMessage extends MimeMessage { super.addHeader(name, value); } + @Override + public void addRawHeader(String name, String raw) { + throw new RuntimeException("Not supported"); + } + @Override public void setHeader(String name, String value) throws UnavailableStorageException { if (!mHeadersLoaded)