Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.

Commit ef7e541

Browse files
committed
MimeUtility.fold ensures that line separators are always followed by whitespace
and InternetAddress.toString(Address[],int) now uses MimeUtility.fold - bug 7529
1 parent b72a136 commit ef7e541

5 files changed

Lines changed: 229 additions & 7 deletions

File tree

doc/release/CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ K 7472 Store finalizers should not talk to server
3535
K 7506 MailHandler verify should load additional content handlers
3636
K 7512 NullPointerException if SASL is enabled on Android
3737
K 7513 write timeouts don't work with SSL on Android
38+
K 7529 JavaMail allows injection of unwanted headers
3839

3940

4041
CHANGES IN THE 1.5.5 RELEASE

mail/src/main/java/javax/mail/internet/InternetAddress.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,17 +459,22 @@ public static String toString(Address[] addresses, int used) {
459459
if (addresses == null || addresses.length == 0)
460460
return null;
461461

462-
StringBuffer sb = new StringBuffer();
462+
StringBuilder sb = new StringBuilder();
463463

464464
for (int i = 0; i < addresses.length; i++) {
465465
if (i != 0) { // need to append comma
466466
sb.append(", ");
467467
used += 2;
468468
}
469469

470-
String s = addresses[i].toString();
470+
// prefer not to split a single address across lines so used=0 below
471+
String s = MimeUtility.fold(0, addresses[i].toString());
471472
int len = lengthOfFirstSegment(s); // length till CRLF
472473
if (used + len > 76) { // overflows ...
474+
// smash trailing space from ", " above
475+
int curlen = sb.length();
476+
if (curlen > 0 && sb.charAt(curlen - 1) == ' ')
477+
sb.setLength(curlen - 1);
473478
sb.append("\r\n\t"); // .. start new continuation line
474479
used = 8; // account for the starting <tab> char
475480
}
@@ -480,7 +485,8 @@ public static String toString(Address[] addresses, int used) {
480485
return sb.toString();
481486
}
482487

483-
/* Return the length of the first segment within this string.
488+
/*
489+
* Return the length of the first segment within this string.
484490
* If no segments exist, the length of the whole line is returned.
485491
*/
486492
private static int lengthOfFirstSegment(String s) {

mail/src/main/java/javax/mail/internet/MimeUtility.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,10 +1070,10 @@ public static String fold(int used, String s) {
10701070

10711071
// if the string fits now, just return it
10721072
if (used + s.length() <= 76)
1073-
return s;
1073+
return makesafe(s);
10741074

10751075
// have to actually fold the string
1076-
StringBuffer sb = new StringBuffer(s.length() + 4);
1076+
StringBuilder sb = new StringBuilder(s.length() + 4);
10771077
char lastc = 0;
10781078
while (used + s.length() > 76) {
10791079
int lastspace = -1;
@@ -1101,6 +1101,46 @@ public static String fold(int used, String s) {
11011101
used = 1;
11021102
}
11031103
sb.append(s);
1104+
return makesafe(sb);
1105+
}
1106+
1107+
/**
1108+
* If the String or StringBuilder has any embedded newlines,
1109+
* make sure they're followed by whitespace, to prevent header
1110+
* injection errors.
1111+
*/
1112+
private static String makesafe(CharSequence s) {
1113+
int i;
1114+
for (i = 0; i < s.length(); i++) {
1115+
char c = s.charAt(i);
1116+
if (c == '\r' || c == '\n')
1117+
break;
1118+
}
1119+
if (i == s.length()) // went through whole string with no CR or LF
1120+
return s.toString();
1121+
1122+
// read the lines in the string and reassemble them,
1123+
// eliminating blank lines and inserting whitespace as necessary
1124+
StringBuilder sb = new StringBuilder(s.length() + 1);
1125+
BufferedReader r = new BufferedReader(new StringReader(s.toString()));
1126+
String line;
1127+
try {
1128+
while ((line = r.readLine()) != null) {
1129+
if (line.trim().length() == 0)
1130+
continue; // ignore empty lines
1131+
if (sb.length() > 0) {
1132+
sb.append("\r\n");
1133+
assert line.length() > 0; // proven above
1134+
char c = line.charAt(0);
1135+
if (c != ' ' && c != '\t')
1136+
sb.append(' ');
1137+
}
1138+
sb.append(line);
1139+
}
1140+
} catch (IOException ex) {
1141+
// XXX - should never happen when reading from a string
1142+
return s.toString();
1143+
}
11041144
return sb.toString();
11051145
}
11061146

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package javax.mail.internet;
42+
43+
import java.io.*;
44+
import java.util.*;
45+
import javax.mail.internet.InternetAddress;
46+
47+
import org.junit.Test;
48+
import org.junit.Assert;
49+
import org.junit.runner.RunWith;
50+
import org.junit.runners.Parameterized;
51+
import org.junit.runners.Parameterized.Parameters;
52+
53+
/**
54+
* Test InternetAddress folding.
55+
*
56+
* @author Bill Shannon
57+
*/
58+
59+
@RunWith(Parameterized.class)
60+
public class InternetAddressFoldTest {
61+
private InternetAddress[] orig;
62+
private String expect;
63+
64+
private static List<Object[]> testData;
65+
66+
public InternetAddressFoldTest(InternetAddress[] orig, String expect) {
67+
this.orig = orig;
68+
this.expect = expect;
69+
}
70+
71+
@Parameters
72+
public static Collection<Object[]> data() throws Exception {
73+
testData = new ArrayList<Object[]>();
74+
parse(new BufferedReader(new InputStreamReader(
75+
InternetAddressFoldTest.class.getResourceAsStream("addrfolddata"))));
76+
return testData;
77+
}
78+
79+
/**
80+
* Read the data from the test file. Format is:
81+
*
82+
* FOLD N
83+
* address1$
84+
* ...
85+
* addressN$
86+
* EXPECT
87+
* address1, ..., addressN$
88+
*/
89+
private static void parse(BufferedReader in) throws Exception {
90+
String line;
91+
while ((line = in.readLine()) != null) {
92+
if (line.startsWith("#") || line.length() == 0)
93+
continue;
94+
if (!line.startsWith("FOLD"))
95+
throw new IOException("TEST DATA FORMAT ERROR, MISSING FOLD");
96+
int count = Integer.parseInt(line.substring(5));
97+
InternetAddress[] orig = new InternetAddress[count];
98+
for (int i = 0; i < count; i++)
99+
orig[i] = new InternetAddress(readString(in));
100+
String e = in.readLine();
101+
if (!e.equals("EXPECT"))
102+
throw new IOException("TEST DATA FORMAT ERROR, MISSING EXPECT");
103+
String expect = readString(in);
104+
testData.add(new Object[] { orig, expect });
105+
}
106+
}
107+
108+
/**
109+
* Read a string that ends with '$', preserving all characters,
110+
* especially including CR and LF.
111+
*/
112+
private static String readString(BufferedReader in) throws IOException {
113+
StringBuffer sb = new StringBuffer();
114+
int c;
115+
while ((c = in.read()) != '$')
116+
sb.append((char)c);
117+
in.readLine(); // throw away rest of line
118+
return sb.toString();
119+
}
120+
121+
@Test
122+
public void testFold() {
123+
Assert.assertEquals("Fold", expect, InternetAddress.toString(orig, 0));
124+
}
125+
}

mail/src/test/resources/javax/mail/internet/folddata

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
#
4-
# Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
4+
# Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
55
#
66
# The contents of this file are subject to the terms of either the GNU
77
# General Public License Version 2 only ("GPL") or the Common Development
@@ -39,7 +39,7 @@
3939
#
4040

4141
#
42-
# Test data used by foldtest.java to test the MimeUtility.fold
42+
# Test data used by FoldTest.java to test the MimeUtility.fold
4343
# and MimeUtility.unfold methods.
4444
#
4545
# First, tests that ensure simple strings aren't changed.
@@ -265,3 +265,53 @@ a
265265
b$
266266
EXPECT
267267
a b$
268+
#
269+
# Fold with embedded newlines
270+
#
271+
FOLD
272+
a
273+
b$
274+
EXPECT
275+
a
276+
b$
277+
FOLD
278+
a
279+
280+
b$
281+
EXPECT
282+
a
283+
b$
284+
FOLD
285+
ab$
286+
EXPECT
287+
a
288+
b$
289+
FOLD
290+
ab$
291+
EXPECT
292+
a
293+
b$
294+
FOLD
295+
a
296+
b$
297+
EXPECT
298+
a
299+
b$
300+
FOLD
301+
a
302+
b$
303+
EXPECT
304+
a
305+
b$
306+
FOLD
307+
a
308+
b$
309+
EXPECT
310+
a
311+
b$
312+
FOLD
313+
a
314+
b$
315+
EXPECT
316+
a
317+
b$

0 commit comments

Comments
 (0)