Skip to content

Commit

Permalink
UniqueValue should use UUID::randomUUID for boundary and messageid #460
Browse files Browse the repository at this point in the history
Signed-off-by: jmehrens <[email protected]>
  • Loading branch information
jmehrens committed Feb 20, 2021
1 parent 226d2d6 commit c744f3a
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 22 deletions.
2 changes: 1 addition & 1 deletion mail/src/main/java/jakarta/mail/internet/MimeMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import jakarta.mail.*;
import jakarta.activation.*;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.text.ParseException;
Expand Down Expand Up @@ -2212,6 +2211,7 @@ public void saveChanges() throws MessagingException {
* to override only the algorithm for choosing a Message-ID.
*
* @exception MessagingException for failures
* @see InternetAddress#getLocalAddress
* @since JavaMail 1.4
*/
protected void updateMessageID() throws MessagingException {
Expand Down
65 changes: 45 additions & 20 deletions mail/src/main/java/jakarta/mail/internet/UniqueValue.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
Expand All @@ -16,9 +16,10 @@

package jakarta.mail.internet;

import java.net.*;
import com.sun.mail.util.PropUtil;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.mail.Session;
import java.util.UUID;

/**
* This is a utility class that generates unique values. The generated
Expand All @@ -36,7 +37,7 @@ class UniqueValue {
/**
* A global unique number, to ensure uniqueness of generated strings.
*/
private static AtomicInteger id = new AtomicInteger();
private static final AtomicInteger id = new AtomicInteger();

/**
* Get a unique value for use in a multipart boundary string.
Expand All @@ -47,12 +48,16 @@ class UniqueValue {
*/
public static String getUniqueBoundaryValue() {
StringBuilder s = new StringBuilder();
long hash = s.hashCode();

// Unique string is ----=_Part_<part>_<hashcode>.<currentTime>
s.append("----=_Part_").append(id.getAndIncrement()).append("_").
append(hash).append('.').
append(System.currentTimeMillis());
s.append("----=_Part_");
if (PropUtil.getBooleanSystemProperty(
"mail.mime.multipart.boundary.format", true)) {
s.append(UUID.randomUUID());
} else {
// Unique string is ----=_Part_<part>_<hashcode>.<currentTime>
s.append(id.getAndIncrement()).append("_").
append(System.identityHashCode(s)).append('.').
append(System.currentTimeMillis());
}
return s.toString();
}

Expand All @@ -71,25 +76,45 @@ public static String getUniqueBoundaryValue() {
* @see jakarta.mail.internet.InternetAddress
*/
public static String getUniqueMessageIDValue(Session ssn) {
String suffix = null;
StringBuilder s = new StringBuilder();
if (messageIdFormat(ssn)) {
s.append(UUID.randomUUID());
} else {
// Unique string is <hashcode>.<id>.<currentTime><suffix>
s.append(System.identityHashCode(s)).append('.').
append(id.getAndIncrement()).append('.').
append(System.currentTimeMillis());
}

String suffix;
InternetAddress addr = InternetAddress.getLocalAddress(ssn);
if (addr != null)
suffix = addr.getAddress();
else {
suffix = "jakartamailuser@localhost"; // worst-case default
}
int at = suffix.lastIndexOf('@');
if (at >= 0)
suffix = suffix.substring(at);

StringBuilder s = new StringBuilder();

// Unique string is <hashcode>.<id>.<currentTime><suffix>
s.append(s.hashCode()).append('.').
append(id.getAndIncrement()).append('.').
append(System.currentTimeMillis()).
append(suffix);
int at = suffix.lastIndexOf('@');
if (at >= 0) {
s.append(suffix, at, suffix.length());
} else {
s.append(suffix);
}
return s.toString();
}

private static boolean messageIdFormat(Session ssn) {
String k = "mail.mime.messageid.format";
boolean def = true;
if (ssn != null) {
return PropUtil.getBooleanProperty(ssn.getProperties(), k, def);
} else { //Act like default default session without creating it.
return PropUtil.getBooleanSystemProperty(k, def);
}
}

// No one should instantiate this class.
private UniqueValue() throws IllegalAccessException {
throw new IllegalAccessException(UniqueValue.class.getName());
}
}
27 changes: 26 additions & 1 deletion mail/src/main/java/jakarta/mail/internet/package.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 1997, 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
Expand Down Expand Up @@ -290,6 +290,19 @@
</TD>
</TR>

<TR>
<TD><A ID="mail.mime.messageid.format">mail.mime.messageid.format</A></TD>
<TD>boolean</TD>
<TD>
Sets the format of the
{@link jakarta.mail.internet.MimeMessage#updateMessageID message id} of the
MimeMessage class. If set to <code>"false"</code>, the format is compatible with
older versions of JakartaMail. If set to <code>"true"</code> the message id
will contain a randomly generator UUID. The default is <code>"true"</code> if
not specified or format is invalid.
</TD>
</TR>

<TR>
<TD><A ID="mail.replyallcc">mail.replyallcc</A></TD>
<TD>boolean</TD>
Expand Down Expand Up @@ -344,6 +357,18 @@
</TD>
</TR>

<TR>
<TD><A ID="mail.mime.multipart.boundary.format">mail.mime.multipart.boundary.format</A></TD>
<TD>boolean</TD>
<TD>
Sets the format of the MimeMultipart boundary line. If set to
<code>"false"</code>, the format is compatible with older versions of
JakartaMail. If set to <code>"true"</code> the boundary line will contain a
randomly generator UUID. The default is <code>"true"</code> if not specified or
format is invalid.
</TD>
</TR>

<TR>
<TD><A ID="mail.mime.setcontenttypefilename">mail.mime.setcontenttypefilename</A></TD>
<TD>boolean</TD>
Expand Down
215 changes: 215 additions & 0 deletions mail/src/test/java/jakarta/mail/internet/UniqueValueTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* 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 java.util.Properties;
import java.util.UUID;
import jakarta.mail.Session;
import org.junit.Test;
import static org.junit.Assert.*;

public class UniqueValueTest {

private static final String MESSAGEID_KEY = "mail.mime.messageid.format";

private static final String BOUNDARY_KEY
= "mail.mime.multipart.boundary.format";

public UniqueValueTest() {
}

@Test(expected = java.lang.IllegalAccessException.class)
public void testDeclaredConstructor() throws ReflectiveOperationException {
UniqueValue.class.getDeclaredConstructor().newInstance();
}

@Test
public void testDefGetUniqueBoundaryValue() {
String v = System.getProperty(BOUNDARY_KEY);
assertNull(v, v);
expectUuidBoundary();
}

private void expectUuidBoundary() {
String start = "----=_Part_";
String result = UniqueValue.getUniqueBoundaryValue();
assertTrue(result, result.startsWith(start));
try {
UUID.fromString(result.substring(start.length()));
} catch (Throwable t) {
throw new RuntimeException(result, t);
}
}

@Test
public void testInvalidUuidGetUniqueBoundaryValue() {
System.setProperty(BOUNDARY_KEY, UniqueValueTest.class.getName());
try {
expectUuidBoundary();
} finally {
System.getProperties().remove(BOUNDARY_KEY);
}
}

@Test
public void testUuidGetUniqueBoundaryValue() {
System.setProperty(BOUNDARY_KEY, "true");
try {
expectUuidBoundary();
} finally {
System.getProperties().remove(BOUNDARY_KEY);
}
}

@Test
public void testUvGetUniqueBoundaryValue() {
System.setProperty(BOUNDARY_KEY, "false");
try {
String start = "----=_Part_";
String result = UniqueValue.getUniqueBoundaryValue();
assertTrue(result, result.startsWith(start));
try {
int s = start.length();
int n = result.indexOf('_', s);
Long.parseLong(result.substring(s, n)); //id

s = n + 1;
n = result.indexOf('.', s);
Long.parseLong(result.substring(s, n)); //hashCode

Long.parseLong(result.substring(n + 1)); //millis
} catch (Throwable t) {
throw new RuntimeException(result, t);
}
} finally {
System.getProperties().remove(BOUNDARY_KEY);
}
}

@Test
public void testDefGetUniqueMessageIDValue() {
Properties p = new Properties();
String v = p.getProperty(MESSAGEID_KEY);
assertNull(v, v);
expectUuidMessageID(Session.getInstance(p));
}

/**
* If a session is given but the value is not defined ensure code will not
* fallback to using system properties.
*/
@Test
public void testUuidNoheritGetUniqueMessageIDValue() {
System.setProperty(MESSAGEID_KEY, "false");
try {
Properties p = new Properties();
String v = p.getProperty(MESSAGEID_KEY);
assertNull(v, v);
expectUuidMessageID(Session.getInstance(p));
} finally {
System.getProperties().remove(MESSAGEID_KEY);
}
}


@Test
public void testUvGetUniqueMessageIDValue() {
Properties p = new Properties();
p.put(MESSAGEID_KEY, "false");
String result = UniqueValue.getUniqueMessageIDValue(
Session.getInstance(p));
try {
int s = 0;
int n = result.indexOf('.', s);
Long.parseLong(result.substring(s, n)); //id

s = n + 1;
n = result.indexOf('.', s);
Long.parseLong(result.substring(s, n)); //hashCode

s = n + 1;
n = result.indexOf('@', s);
Long.parseLong(result.substring(s, n)); //millis
} catch (Throwable t) {
throw new RuntimeException(result, t);
}
}

@Test
public void testUuidGetUniqueMessageIDValue() {
Properties p = new Properties();
p.put(MESSAGEID_KEY, "true");
expectUuidMessageID(Session.getInstance(p));
}

@Test
public void testDefSystemGetUniqueMessageIDValue() {
String v = System.getProperty(MESSAGEID_KEY);
assertNull(v, v);
expectUuidMessageID((Session) null);
}

@Test
public void testInvalidGetUniqueMessageIDValue() {
Properties p = new Properties();
p.put(MESSAGEID_KEY, UniqueValueTest.class.getName());
expectUuidMessageID(Session.getInstance(p));
}

private void expectUuidMessageID(Session s) {
String result = UniqueValue.getUniqueMessageIDValue(s);
try {
UUID.fromString(result.substring(0, result.indexOf('@')));
} catch (Throwable t) {
throw new RuntimeException(result, t);
}
}

@Test
public void testUuidSystemGetUniqueMessageIDValue() {
System.setProperty(MESSAGEID_KEY, "true");
try {
expectUuidMessageID((Session) null);
} finally {
System.getProperties().remove(MESSAGEID_KEY);
}
}

@Test
public void testUvSystemGetUniqueMessageIDValue() {
System.setProperty(MESSAGEID_KEY, "false");
try {
String result = UniqueValue.getUniqueMessageIDValue((Session) null);
try {
int s = 0;
int n = result.indexOf('.', s);
Long.parseLong(result.substring(s, n)); //id

s = n + 1;
n = result.indexOf('.', s);
Long.parseLong(result.substring(s, n)); //hashCode

s = n + 1;
n = result.indexOf('@', s);
Long.parseLong(result.substring(s, n)); //millis
} catch (Throwable t) {
throw new RuntimeException(result, t);
}
} finally {
System.getProperties().remove(MESSAGEID_KEY);
}
}
}

0 comments on commit c744f3a

Please sign in to comment.