diff --git a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java index fb25b842..56712612 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java +++ b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import jakarta.activation.DataHandler; import jakarta.activation.DataSource; @@ -87,29 +88,30 @@ public class MimeBodyPart extends BodyPart implements MimePart { // Paranoia: // allow this last minute change to be disabled if it causes problems - private static final boolean setDefaultTextCharset = - MimeUtility.getBooleanSystemProperty( - "mail.mime.setdefaulttextcharset", true); - - private static final boolean setContentTypeFileName = - MimeUtility.getBooleanSystemProperty( - "mail.mime.setcontenttypefilename", true); - - private static final boolean encodeFileName = - MimeUtility.getBooleanSystemProperty("mail.mime.encodefilename", false); - private static final boolean decodeFileName = - MimeUtility.getBooleanSystemProperty("mail.mime.decodefilename", false); - private static final boolean ignoreMultipartEncoding = - MimeUtility.getBooleanSystemProperty( - "mail.mime.ignoremultipartencoding", true); - private static final boolean allowutf8 = - MimeUtility.getBooleanSystemProperty("mail.mime.allowutf8", true); - + static final boolean SET_DEFAULT_TEXT_CHARSET = + MimeUtility.getBooleanSystemProperty("mail.mime.setdefaulttextcharset", true); + static final boolean SET_CONTENT_TYPE_FILE_NAME = + MimeUtility.getBooleanSystemProperty("mail.mime.setcontenttypefilename", true); + static final boolean ENCODE_FILE_NAME = + MimeUtility.getBooleanSystemProperty("mail.mime.encodefilename", false); + static final boolean DECODE_FILE_NAME = + MimeUtility.getBooleanSystemProperty("mail.mime.decodefilename", false); + static final boolean IGNORE_MULTIPART_ENCODING = + MimeUtility.getBooleanSystemProperty("mail.mime.ignoremultipartencoding", true); + static final boolean ALLOW_UTF8 = + MimeUtility.getBooleanSystemProperty("mail.mime.allowutf8", true); // Paranoia: // allow this last minute change to be disabled if it causes problems - static final boolean cacheMultipart = // accessed by MimeMessage - MimeUtility.getBooleanSystemProperty("mail.mime.cachemultipart", true); + static final boolean CACHE_MULTIPART = // accessed by MimeMessage + MimeUtility.getBooleanSystemProperty("mail.mime.cachemultipart", true); + protected boolean setDefaultTextCharset = SET_DEFAULT_TEXT_CHARSET; + protected boolean setContentTypeFileName = SET_CONTENT_TYPE_FILE_NAME; + protected boolean encodeFileName = ENCODE_FILE_NAME; + protected boolean decodeFileName = DECODE_FILE_NAME; + protected boolean ignoreMultipartEncoding = IGNORE_MULTIPART_ENCODING; + protected boolean allowutf8 = ALLOW_UTF8; + protected boolean cacheMultipart = CACHE_MULTIPART; /** * The DataHandler object representing this Part's content. @@ -216,6 +218,36 @@ public MimeBodyPart(InternetHeaders headers, byte[] content) this.content = content; } + /** + * Initializes MimeBodyPart from the InputStream is and it overwrites + * properties from session. + * + * @param session Session object for this message + * @param is the message input stream + * @exception MessagingException for failures + * + * @since JavaMail 2.1 + */ + public MimeBodyPart(Session session, InputStream is) throws MessagingException { + this(is); + initializeProperties(session); + } + + /** + * Set the values from session properties if exist, otherwise it keeps the previous value. + * @param session the not null session + */ + private void initializeProperties(Session session) { + Properties props = session.getProperties(); + setDefaultTextCharset = MimeUtility.getBooleanProperty(props, "mail.mime.setdefaulttextcharset", setDefaultTextCharset); + setContentTypeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.setcontenttypefilename", setContentTypeFileName); + encodeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.encodefilename", encodeFileName); + decodeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.decodefilename", decodeFileName); + ignoreMultipartEncoding = MimeUtility.getBooleanProperty(props, "mail.mime.ignoremultipartencoding", ignoreMultipartEncoding); + allowutf8 = MimeUtility.getBooleanProperty(props, "mail.mime.allowutf8", allowutf8); + cacheMultipart = MimeUtility.getBooleanProperty(props, "mail.mime.cachemultipart", cacheMultipart); + } + /** * Return the size of the content of this body part in bytes. * Return -1 if the size cannot be determined.

@@ -546,7 +578,7 @@ public void setDescription(String description, String charset) */ @Override public String getFileName() throws MessagingException { - return getFileName(this); + return getFileName(this, decodeFileName); } /** @@ -574,7 +606,7 @@ public String getFileName() throws MessagingException { */ @Override public void setFileName(String filename) throws MessagingException { - setFileName(this, filename); + setFileName(this, filename, encodeFileName, setContentTypeFileName); } /** @@ -998,7 +1030,7 @@ public void saveFile(String file) throws IOException, MessagingException { @Override public void writeTo(OutputStream os) throws IOException, MessagingException { - writeTo(this, os, null); + writeTo(this, os, null, allowutf8, ignoreMultipartEncoding); } /** @@ -1174,7 +1206,7 @@ public Enumeration getNonMatchingHeaderLines(String[] names) * @exception MessagingException for failures */ protected void updateHeaders() throws MessagingException { - updateHeaders(this); + updateHeaders(this, setDefaultTextCharset, setContentTypeFileName, encodeFileName); /* * If we've cached a Multipart or Message object then * we're now committed to using this instance of the @@ -1290,7 +1322,7 @@ static String getDescription(MimePart part) } } - static String getFileName(MimePart part) throws MessagingException { + static String getFileName(MimePart part, boolean decodeFileName) throws MessagingException { String filename = null; String s = part.getHeader("Content-Disposition", null); @@ -1320,7 +1352,8 @@ static String getFileName(MimePart part) throws MessagingException { return filename; } - static void setFileName(MimePart part, String name) + static void setFileName(MimePart part, String name, boolean encodeFileName, + boolean setContentTypeFileName) throws MessagingException { if (encodeFileName && name != null) { try { @@ -1469,7 +1502,7 @@ static void setEncoding(MimePart part, String encoding) * Content-Type of the specified MimePart. Returns * either the original encoding or null. */ - static String restrictEncoding(MimePart part, String encoding) + static String restrictEncoding(MimePart part, String encoding, boolean ignoreMultipartEncoding) throws MessagingException { if (!ignoreMultipartEncoding || encoding == null) return encoding; @@ -1502,7 +1535,8 @@ static String restrictEncoding(MimePart part, String encoding) return encoding; } - static void updateHeaders(MimePart part) throws MessagingException { + static void updateHeaders(MimePart part, boolean setDefaultTextCharset, boolean setContentTypeFileName, + boolean encodeFileName) throws MessagingException { DataHandler dh = part.getDataHandler(); if (dh == null) // Huh ? return; @@ -1649,8 +1683,8 @@ static void invalidateContentHeaders(MimePart part) part.removeHeader("Content-Transfer-Encoding"); } - static void writeTo(MimePart part, OutputStream os, String[] ignoreList) - throws IOException, MessagingException { + static void writeTo(MimePart part, OutputStream os, String[] ignoreList, boolean allowutf8, + boolean ignoreMultipartEncoding) throws IOException, MessagingException { // see if we already have a LOS LineOutputStream los = null; @@ -1697,7 +1731,7 @@ static void writeTo(MimePart part, OutputStream os, String[] ignoreList) os.write(buf, 0, len); } else { os = MimeUtility.encode(os, - restrictEncoding(part, part.getEncoding())); + restrictEncoding(part, part.getEncoding(), ignoreMultipartEncoding)); part.getDataHandler().writeTo(os); } } finally { diff --git a/api/src/main/java/jakarta/mail/internet/MimeMessage.java b/api/src/main/java/jakarta/mail/internet/MimeMessage.java index ccaea8b9..93842bb7 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeMessage.java +++ b/api/src/main/java/jakarta/mail/internet/MimeMessage.java @@ -45,6 +45,8 @@ import jakarta.mail.Session; import jakarta.mail.util.LineOutputStream; +import static jakarta.mail.internet.MimeBodyPart.*; + /** @@ -176,8 +178,20 @@ public class MimeMessage extends Message implements MimePart { // Should addresses in headers be parsed in "strict" mode? private boolean strict = true; + + protected boolean setDefaultTextCharset = SET_DEFAULT_TEXT_CHARSET; + protected boolean setContentTypeFileName = SET_CONTENT_TYPE_FILE_NAME; + protected boolean encodeFileName = ENCODE_FILE_NAME; + protected boolean decodeFileName = DECODE_FILE_NAME; + protected boolean ignoreMultipartEncoding = IGNORE_MULTIPART_ENCODING; + /* + * This is not a duplicate of allowutf8Headers. When mail.mime.allowutf8 + * is not defined, this value is 'true'. Meanwhile allowutf8Headers is 'false' + */ + protected boolean allowutf8 = ALLOW_UTF8; + protected boolean cacheMultipart = CACHE_MULTIPART; // Is UTF-8 allowed in headers? - private boolean allowutf8 = false; + private boolean allowutf8Headers = false; /** * Default constructor. An empty message object is created. @@ -312,14 +326,21 @@ protected MimeMessage(Folder folder, InternetHeaders headers, } /** - * Set the strict flag based on property. + * Set the properties from session if exists, otherwise it keeps the previous value. */ private void initStrict() { - if (session != null) { - Properties props = session.getProperties(); - strict = MimeUtility.getBooleanProperty(props, "mail.mime.address.strict", true); - allowutf8 = MimeUtility.getBooleanProperty(props, "mail.mime.allowutf8", false); - } + if (session != null) { + Properties props = session.getProperties(); + strict = MimeUtility.getBooleanProperty(props, "mail.mime.address.strict", true); + allowutf8Headers = MimeUtility.getBooleanProperty(props, "mail.mime.allowutf8", false); + setDefaultTextCharset = MimeUtility.getBooleanProperty(props, "mail.mime.setdefaulttextcharset", setDefaultTextCharset); + setContentTypeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.setcontenttypefilename", setContentTypeFileName); + encodeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.encodefilename", encodeFileName); + decodeFileName = MimeUtility.getBooleanProperty(props, "mail.mime.decodefilename", decodeFileName); + ignoreMultipartEncoding = MimeUtility.getBooleanProperty(props, "mail.mime.ignoremultipartencoding", ignoreMultipartEncoding); + allowutf8 = MimeUtility.getBooleanProperty(props, "mail.mime.allowutf8", allowutf8); + cacheMultipart = MimeUtility.getBooleanProperty(props, "mail.mime.cachemultipart", cacheMultipart); + } } /** @@ -750,7 +771,7 @@ private Address[] getAddressHeader(String name) private void setAddressHeader(String name, Address[] addresses) throws MessagingException { String s; - if (allowutf8) + if (allowutf8Headers) s = InternetAddress.toUnicodeString(addresses, name.length() + 2); else s = InternetAddress.toString(addresses, name.length() + 2); @@ -774,7 +795,7 @@ private void addAddressHeader(String name, Address[] addresses) System.arraycopy(addresses, 0, anew, a.length, addresses.length); } String s; - if (allowutf8) + if (allowutf8Headers) s = InternetAddress.toUnicodeString(anew, name.length() + 2); else s = InternetAddress.toString(anew, name.length() + 2); @@ -1318,7 +1339,7 @@ public String getMessageID() throws MessagingException { */ @Override public String getFileName() throws MessagingException { - return MimeBodyPart.getFileName(this); + return MimeBodyPart.getFileName(this, decodeFileName); } /** @@ -1343,7 +1364,7 @@ public String getFileName() throws MessagingException { */ @Override public void setFileName(String filename) throws MessagingException { - MimeBodyPart.setFileName(this, filename); + MimeBodyPart.setFileName(this, filename, encodeFileName, setContentTypeFileName); } private String getHeaderName(Message.RecipientType type) @@ -1506,7 +1527,7 @@ public Object getContent() throws IOException, MessagingException { throw e; } } - if (MimeBodyPart.cacheMultipart && + if (cacheMultipart && (c instanceof Multipart || c instanceof Message) && (content != null || contentStream != null)) { cachedContent = c; @@ -1904,14 +1925,14 @@ public void writeTo(OutputStream os, String[] ignoreList) saveChanges(); if (modified) { - MimeBodyPart.writeTo(this, os, ignoreList); + MimeBodyPart.writeTo(this, os, ignoreList, allowutf8, ignoreMultipartEncoding); return; } // Else, the content is untouched, so we can just output it // First, write out the header Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList); - LineOutputStream los = session.getStreamProvider().outputLineStream(os, allowutf8); + LineOutputStream los = session.getStreamProvider().outputLineStream(os, allowutf8Headers); while (hdrLines.hasMoreElements()) los.writeln(hdrLines.nextElement()); @@ -2262,7 +2283,7 @@ protected void updateMessageID() throws MessagingException { * @exception MessagingException for other failures */ protected synchronized void updateHeaders() throws MessagingException { - MimeBodyPart.updateHeaders(this); + MimeBodyPart.updateHeaders(this, setDefaultTextCharset, setContentTypeFileName, encodeFileName); setHeader("MIME-Version", "1.0"); if (getHeader("Date") == null) setSentDate(new Date()); @@ -2295,7 +2316,7 @@ protected synchronized void updateHeaders() throws MessagingException { */ protected InternetHeaders createInternetHeaders(InputStream is) throws MessagingException { - return new InternetHeaders(is, allowutf8); + return new InternetHeaders(is, allowutf8Headers); } /** diff --git a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java index 16c97df2..9737b2ec 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java +++ b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -32,6 +32,7 @@ import jakarta.mail.MessagingException; import jakarta.mail.Multipart; import jakarta.mail.MultipartDataSource; +import jakarta.mail.Session; import jakarta.mail.util.LineInputStream; import jakarta.mail.util.LineOutputStream; @@ -1002,7 +1003,13 @@ protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, */ protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException { - return new MimeBodyPart(is); + if (ds instanceof MessageAware) { + Session session = ((MessageAware)ds).getMessageContext().getSession(); + if (session != null) { + return new MimeBodyPart(session, is); + } + } + return new MimeBodyPart(is); } private MimeBodyPart createMimeBodyPartIs(InputStream is) diff --git a/api/src/main/java/jakarta/mail/internet/MimePartDataSource.java b/api/src/main/java/jakarta/mail/internet/MimePartDataSource.java index ba7a1cc0..93ae18c2 100644 --- a/api/src/main/java/jakarta/mail/internet/MimePartDataSource.java +++ b/api/src/main/java/jakarta/mail/internet/MimePartDataSource.java @@ -26,7 +26,9 @@ import jakarta.mail.MessageAware; import jakarta.mail.MessageContext; import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import static jakarta.mail.internet.MimeBodyPart.*; /** @@ -39,6 +41,8 @@ */ public class MimePartDataSource implements DataSource, MessageAware { + + protected boolean ignoreMultipartEncoding = IGNORE_MULTIPART_ENCODING; /** * The MimePart that provides the data for this DataSource. * @@ -54,7 +58,13 @@ public class MimePartDataSource implements DataSource, MessageAware { * @param part the MimePart */ public MimePartDataSource(MimePart part) { - this.part = part; + this.part = part; + this.context = new MessageContext(part); + Session session = context.getSession(); + if (session != null) { + this.ignoreMultipartEncoding = MimeUtility.getBooleanProperty(session.getProperties(), + "mail.mime.ignoremultipartencoding", ignoreMultipartEncoding); + } } /** @@ -86,7 +96,7 @@ else if (part instanceof MimeMessage) throw new MessagingException("Unknown part"); String encoding = - MimeBodyPart.restrictEncoding(part, part.getEncoding()); + MimeBodyPart.restrictEncoding(part, part.getEncoding(), ignoreMultipartEncoding); if (encoding != null) return MimeUtility.decode(is, encoding); else @@ -149,9 +159,7 @@ public String getName() { * @since JavaMail 1.1 */ @Override - public synchronized MessageContext getMessageContext() { - if (context == null) - context = new MessageContext(part); - return context; + public MessageContext getMessageContext() { + return context; } } diff --git a/api/src/test/java/jakarta/mail/internet/MimeBodyPartTest.java b/api/src/test/java/jakarta/mail/internet/MimeBodyPartTest.java new file mode 100644 index 00000000..b0917ec4 --- /dev/null +++ b/api/src/test/java/jakarta/mail/internet/MimeBodyPartTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.mail.internet; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.junit.Test; + +import jakarta.mail.MessagingException; +import jakarta.mail.Session; + +public class MimeBodyPartTest { + + @Test + public void sessionProperties() throws MessagingException, IOException { + InputStream input = new ByteArrayInputStream(new byte[0]); + Properties prop = new Properties(); + Session session = Session.getInstance(prop); + MimeMessage orig = new MimeMessage(session, input); + MimeBodyPart obp = new MimeBodyPart(session, input); + // No properties added, so default will be checked + // MimeMessage + assertTrue(orig.setDefaultTextCharset); + assertTrue(orig.setContentTypeFileName); + assertFalse(orig.encodeFileName); + assertFalse(orig.decodeFileName); + assertTrue(orig.ignoreMultipartEncoding); + assertTrue(orig.allowutf8); + assertTrue(orig.cacheMultipart); + // MimeBodyPart + assertTrue(obp.setDefaultTextCharset); + assertTrue(obp.setContentTypeFileName); + assertFalse(obp.encodeFileName); + assertFalse(obp.decodeFileName); + assertTrue(obp.ignoreMultipartEncoding); + assertTrue(obp.allowutf8); + assertTrue(obp.cacheMultipart); + // Change the properties in opposite way + prop.put("mail.mime.setdefaulttextcharset", Boolean.FALSE.toString()); + prop.put("mail.mime.setcontenttypefilename", Boolean.FALSE.toString()); + prop.put("mail.mime.encodefilename", Boolean.TRUE.toString()); + prop.put("mail.mime.decodefilename", Boolean.TRUE.toString()); + prop.put("mail.mime.ignoremultipartencoding", Boolean.FALSE.toString()); + prop.put("mail.mime.allowutf8", Boolean.FALSE.toString()); + prop.put("mail.mime.cachemultipart", Boolean.FALSE.toString()); + + session = Session.getInstance(prop); + orig = new MimeMessage(session, input); + obp = new MimeBodyPart(session, input); + + // MimeMessage + assertFalse(orig.setDefaultTextCharset); + assertFalse(orig.setContentTypeFileName); + assertTrue(orig.encodeFileName); + assertTrue(orig.decodeFileName); + assertFalse(orig.ignoreMultipartEncoding); + assertFalse(orig.allowutf8); + assertFalse(orig.cacheMultipart); + // MimeBodyPart + assertFalse(obp.setDefaultTextCharset); + assertFalse(obp.setContentTypeFileName); + assertTrue(obp.encodeFileName); + assertTrue(obp.decodeFileName); + assertFalse(obp.ignoreMultipartEncoding); + assertFalse(obp.allowutf8); + assertFalse(obp.cacheMultipart); + } +} diff --git a/api/src/test/java/jakarta/mail/internet/NonAsciiFileNamesTest.java b/api/src/test/java/jakarta/mail/internet/NonAsciiFileNamesTest.java index 1a74f9fb..410597b4 100644 --- a/api/src/test/java/jakarta/mail/internet/NonAsciiFileNamesTest.java +++ b/api/src/test/java/jakarta/mail/internet/NonAsciiFileNamesTest.java @@ -42,7 +42,7 @@ public void testNonAsciiFileName() throws Exception { MimeBodyPart mbp = new MimeBodyPart(); mbp.setText("test\n"); mbp.setFileName("test\u00a1\u00a2\u00a3"); - MimeBodyPart.updateHeaders(mbp); + MimeBodyPart.updateHeaders(mbp, true, true, false); String s = mbp.getHeader("Content-Disposition", null); assertTrue("Content-Disposition filename", s.indexOf("filename*") >= 0); @@ -60,7 +60,7 @@ public void testNonAsciiFileNameWithContentType() throws Exception { mbp.setText("test\n"); mbp.setHeader("Content-Type", "text/x-test"); mbp.setFileName("test\u00a1\u00a2\u00a3"); - MimeBodyPart.updateHeaders(mbp); + MimeBodyPart.updateHeaders(mbp, true, true, false); String s = mbp.getHeader("Content-Disposition", null); assertTrue("Content-Disposition filename", s.indexOf("filename*") >= 0); diff --git a/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java b/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java index b3e20a9e..4c9f2829 100644 --- a/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java +++ b/api/src/test/java/jakarta/mail/util/DummyStreamProvider.java @@ -97,4 +97,4 @@ public OutputStream outputUU(OutputStream out, String filename) { return null; } -} \ No newline at end of file +}