
com.upokecenter.mail.Rfc2047 Maven / Gradle / Ivy
package com.upokecenter.mail;
/*
Written by Peter O. in 2014.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
If you like this, you should donate to Peter O.
at: http://upokecenter.dreamhosters.com/articles/donate-now-2/
*/
import java.util.*;
import com.upokecenter.util.*;
import com.upokecenter.mail.transforms.*;
import com.upokecenter.text.*;
final class Rfc2047 {
private Rfc2047() {
}
private static boolean HasSuspiciousTextInComments(String str) {
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
if ((c < 0x20 && c != '\t') || c == '(' || c == ')' || c == '\\' ||
c == 0x7f) {
return true;
}
}
return false;
}
private static boolean HasAsciiCtl(String str) {
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
if ((c < 0x20 && c != '\t') || c == 0x7f) {
return true;
}
}
return false;
}
private static boolean HasSuspiciousTextInStructured(String str) {
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
// Has specials, or CTLs other than tab
if ((c < 0x20 && c != '\t') || c == 0x7F || c == 0x28 || c == 0x29 ||
c == 0x3c || c == 0x3e || c == 0x5b || c == 0x5d || c == 0x3a || c
== 0x3b || c == 0x40 ||
c == 0x5c || c == 0x2c || c == 0x2e || c == '"') {
return true;
}
}
return false;
}
public static String EncodeComment(String str, int index, int endIndex) {
// NOTE: Assumes that the comment is syntactically valid
int length = endIndex - index;
if (length < 2 || str.charAt(index) != '(' || str.charAt(endIndex - 1) != ')') {
return str.substring(index, (index)+(length));
}
EncodedWordEncoder encoder;
int nextComment = str.indexOf('(',index + 1);
int nextBackslash = str.indexOf('\\',index + 1);
// don't count comments or backslashes beyond
// the desired portion
if (nextComment >= endIndex) {
nextComment = -1;
}
if (nextBackslash >= endIndex) {
nextBackslash = -1;
}
boolean haveEscape = nextBackslash >= 0;
if (!haveEscape) {
// Check for possible folding whitespace
nextBackslash = str.indexOf('\n',index + 1);
if (nextBackslash >= endIndex) {
nextBackslash = -1;
}
haveEscape = nextBackslash >= 0;
}
if (nextComment < 0 && nextBackslash < 0) {
// No escapes or nested comments, so it's relatively easy
if (length == 2) {
return "()";
}
encoder = new EncodedWordEncoder();
encoder.AddPrefix("(");
encoder.AddString(str, index + 1, length - 2);
encoder.FinalizeEncoding(")");
return encoder.toString();
}
if (nextBackslash < 0) {
// No escapes; just look for '(' and ')'
encoder = new EncodedWordEncoder();
while (true) {
int parenStart = index;
// Get the next run of parentheses
while (index < endIndex) {
if (str.charAt(index) == '(' || str.charAt(index) == ')') {
++index;
} else {
break;
}
}
// Get the next run of non-parentheses
int parenEnd = index;
while (index < endIndex) {
if (str.charAt(index) == '(' || str.charAt(index) == ')') {
break;
}
++index;
}
if (parenEnd == index) {
encoder.FinalizeEncoding(
str.substring(
parenStart, (
parenStart)+(parenEnd - parenStart)));
break;
}
encoder.AddPrefix(str.substring(parenStart, (parenStart)+(parenEnd - parenStart)));
encoder.AddString(str, parenEnd, index - parenEnd);
}
return encoder.toString();
}
StringBuilder builder = new StringBuilder();
// escapes, but no nested comments
if (nextComment < 0) {
// skip the first parenthesis
++index;
while (index < endIndex) {
if (str.charAt(index) == ')') {
// End of the comment
break;
}
if (str.charAt(index) == '\r' && index + 2 < endIndex &&
str.charAt(index + 1) == '\n' && (str.charAt(index + 2) == 0x20 || str.charAt(index + 2) ==
0x09)) {
// Folding whitespace
builder.append(str.charAt(index + 2));
index += 3;
} else if (str.charAt(index) == '\\' && index + 1 < endIndex) {
// Quoted pair
int cp = DataUtilities.CodePointAt(str, index + 1);
if (cp <= 0xffff) {
{
builder.append((char)cp);
}
} else if (cp <= 0x10ffff) {
builder.append((char)((((cp - 0x10000) >> 10) & 0x3ff) + 0xd800));
builder.append((char)(((cp - 0x10000) & 0x3ff) + 0xdc00));
}
index += 1 + (cp >= 0x10000 ? 2 : 1);
} else {
// Other comment text
builder.append(str.charAt(index));
++index;
}
}
if (builder.length() == 0) {
return "()";
}
encoder = new EncodedWordEncoder();
encoder.AddPrefix("(");
encoder.AddString(builder.toString());
encoder.FinalizeEncoding(")");
return encoder.toString();
}
// escapes and nested comments
encoder = new EncodedWordEncoder();
while (true) {
int parenStart = index;
// Get the next run of parentheses
while (index < endIndex) {
if (str.charAt(index) == '(' || str.charAt(index) == ')') {
++index;
} else {
break;
}
}
// Get the next run of non-parentheses
int parenEnd = index;
builder.delete(0, (0)+(builder.length()));
while (index < endIndex) {
if (str.charAt(index) == '(' || str.charAt(index) == ')') {
break;
}
if (str.charAt(index) == '\r' && index + 2 < endIndex &&
str.charAt(index + 1) == '\n' && (str.charAt(index + 2) == 0x20 || str.charAt(index + 2) ==
0x09)) {
// Folding whitespace
builder.append(str.charAt(index + 2));
index += 3;
} else if (str.charAt(index) == '\\' && index + 1 < endIndex) {
// Quoted pair
int cp = DataUtilities.CodePointAt(str, index + 1);
if (cp <= 0xffff) {
builder.append((char)cp);
} else if (cp <= 0x10ffff) {
builder.append((char)((((cp - 0x10000) >> 10) & 0x3ff) + 0xd800));
builder.append((char)(((cp - 0x10000) & 0x3ff) + 0xdc00));
}
index += 1 + (cp >= 0x10000 ? 2 : 1);
} else {
// Other comment text
builder.append(str.charAt(index));
++index;
}
}
if (builder.length() == 0) {
encoder.FinalizeEncoding(
str.substring(
parenStart, (
parenStart)+(parenEnd - parenStart)));
break;
}
encoder.AddPrefix(str.substring(parenStart, (parenStart)+(parenEnd - parenStart)));
encoder.AddString(builder.toString());
}
return encoder.toString();
}
private static int SkipCharsetOrEncoding(
String str,
int index,
int endIndex) {
String specials = "()<>@,;:\\\"/[]?=.";
int i = index;
while (i < endIndex) {
char c = str.charAt(i);
if (c <= 0x20 || c >= 0x7f || ((c & 0x7f) == c &&
specials.indexOf(c) >= 0)) {
break;
}
++i;
}
return i;
}
private static int SkipEncodedText(
String str,
int index,
int endIndex,
boolean inComments) {
int i = index;
while (i < endIndex) {
char c = str.charAt(i);
if (c <= 0x20 || c >= 0x7F || c == '?') {
break;
}
if (inComments && (c == '(' || c == ')' || c == '\\')) {
break;
}
++i;
}
return i;
}
public static String DecodeEncodedWordsLenient(
String str,
int index,
int endIndex) {
int state = 0;
int markStart = 0;
int wordStart = 0;
int charsetStart = -1;
int charsetEnd = -1;
int dataStart = -1;
int encoding = 0;
if (str.indexOf('=') < 0) {
// Contains no equal sign, and therefore no
// encoded words
return str.substring(index, (index)+(endIndex - index));
}
StringBuilder builder = new StringBuilder();
while (index < endIndex) {
switch (state) {
case 0:
// normal
if (str.charAt(index) == '=' && index + 1 < endIndex && str.charAt(index + 1) == '?'
) {
wordStart = index;
state = 1;
index += 2;
charsetStart = index;
} else {
++index;
}
break;
case 1:
// charset
if (str.charAt(index) == '?') {
charsetEnd = index;
state = 2;
++index;
} else {
++index;
}
break;
case 2:
// encoding
if ((str.charAt(index) == 'b' || str.charAt(index) == 'B') && index + 1 <
endIndex && str.charAt(index + 1) == '?') {
encoding = 1;
state = 3;
index += 2;
dataStart = index;
} else if ((str.charAt(index) == 'q' || str.charAt(index) == 'Q') && index +
1 < endIndex && str.charAt(index + 1) == '?') {
encoding = 2;
state = 3;
index += 2;
dataStart = index;
} else {
state = 0;
index = charsetStart;
}
break;
case 3:
// data
if (str.charAt(index) == '?' && index + 1 < endIndex && str.charAt(index + 1) == '='
) {
String charset = str.substring(
charsetStart, (
charsetStart)+(charsetEnd - charsetStart));
String data = str.substring(dataStart, (dataStart)+(index - dataStart));
state = 0;
index += 2;
int endData = index;
boolean acceptedEncodedWord = true;
int asterisk = charset.indexOf('*');
String decodedWord = null;
if (asterisk >= 1) {
charset = str.substring(0, asterisk);
// Ignore language parameter after the asterisk
} else {
acceptedEncodedWord &= asterisk != 0;
}
if (acceptedEncodedWord) {
IByteReader transform = (encoding == 1) ?
(IByteReader)new BEncodingStringTransform(data) :
(IByteReader)new QEncodingStringTransform(data);
ICharacterEncoding charEncoding = Encodings.GetEncoding(
charset,
true);
if (charEncoding != null) {
decodedWord = Encodings.DecodeToString(
charEncoding,
transform);
}
}
if (decodedWord == null) {
index = charsetStart;
} else {
builder.append(str.substring(markStart, (markStart)+(wordStart - markStart)));
builder.append(decodedWord);
markStart = endData;
}
} else {
++index;
}
break;
default:
throw new IllegalStateException();
}
}
builder.append(str.substring(markStart, (markStart)+(str.length() - markStart)));
return builder.toString();
}
public static String DecodeEncodedWords(
String str,
int index,
int endIndex,
EncodedWordContext context) {
if (endIndex - index < 9) {
// Too short for encoded words to appear
return str.substring(index, (index)+(endIndex - index));
}
if (str.indexOf('=') < 0) {
// Contains no equal sign, and therefore no
// encoded words
return str.substring(index, (index)+(endIndex - index));
}
int start = index;
StringBuilder builder = new StringBuilder();
boolean hasSuspiciousText = false;
boolean lastWordWasEncodedWord = false;
int whitespaceStart = -1;
int whitespaceEnd = -1;
boolean wordsWereDecoded = false;
while (index < endIndex) {
int charCount = 2;
boolean acceptedEncodedWord = false;
String decodedWord = null;
int startIndex = 0;
boolean havePossibleEncodedWord = false;
boolean startParen = false;
if (index + 1 < endIndex && str.charAt(index) == '=' &&
str.charAt(index + 1) == '?') {
startIndex = index + 2;
index += 2;
havePossibleEncodedWord = true;
} else if (context == EncodedWordContext.Comment &&
index + 2 < endIndex && str.charAt(index) == '(' &&
str.charAt(index + 1) == '=' && str.charAt(index + 2) == '?') {
startIndex = index + 3;
index += 3;
startParen = true;
havePossibleEncodedWord = true;
}
if (havePossibleEncodedWord) {
boolean maybeWord = true;
int afterLast = endIndex;
while (index < endIndex) {
char c = str.charAt(index);
++index;
// Check for a run of printable ASCII characters (except space)
// with length up to 75 (also exclude '(' and ')' if the context
// is a comment)
if (c >= 0x21 && c < 0x7e && (context !=
EncodedWordContext.Comment || (c != '(' && c != ')'))) {
++charCount;
if (charCount > 75) {
maybeWord = false;
index = startIndex - 2;
break;
}
} else {
afterLast = index - 1;
break;
}
}
if (maybeWord) {
// May be an encoded word
// DebugUtility.Log("maybe "
// +str.substring(startIndex-2, (startIndex-2)+(afterLast-(startIndex-2))));
index = startIndex;
int i2;
// Parse charset
// (NOTE: Compatible with RFC 2231's addition of language
// to charset, since charset is defined as a 'token' in
// RFC 2047, which includes '*')
int charsetEnd = -1;
int encodedTextStart = -1;
boolean base64 = false;
i2 = SkipCharsetOrEncoding(str, index, afterLast);
if (i2 != index && i2 < endIndex && str.charAt(i2) == '?') {
// Parse encoding
charsetEnd = i2;
index = i2 + 1;
i2 = SkipCharsetOrEncoding(str, index, afterLast);
if (i2 != index && i2 < endIndex && str.charAt(i2) == '?') {
// check for supported encoding (B or Q)
char encodingChar = str.charAt(index);
if (i2 - index == 1 && (encodingChar == 'b' ||
encodingChar == 'B' ||
encodingChar == 'q' || encodingChar == 'Q')) {
// Parse encoded text
base64 = encodingChar == 'b' || encodingChar == 'B';
index = i2 + 1;
encodedTextStart = index;
i2 = SkipEncodedText(
str,
index,
afterLast,
context == EncodedWordContext.Comment);
if (i2 != index && i2 + 1 < endIndex && str.charAt(i2) == '?' && str.charAt(i2 + 1) == '=' &&
i2 + 2 == afterLast) {
acceptedEncodedWord = true;
i2 += 2;
}
}
}
}
if (acceptedEncodedWord) {
String charset = str.substring(
startIndex, (
startIndex)+(charsetEnd - startIndex));
String encodedText = str.substring(
encodedTextStart, (
encodedTextStart)+((afterLast - 2) - encodedTextStart));
// DebugUtility.Log("enctext " + encodedText);
int asterisk = charset.indexOf('*');
if (asterisk >= 1) {
charset = str.substring(0, asterisk);
String language = str.substring(
asterisk + 1, (
asterisk + 1)+(str.length() - (asterisk + 1)));
if (!ParserUtility.IsValidLanguageTag(language)) {
acceptedEncodedWord = false;
}
} else {
acceptedEncodedWord &= asterisk != 0;
}
if (acceptedEncodedWord) {
IByteReader transform = base64 ?
(IByteReader)new BEncodingStringTransform(encodedText) :
(IByteReader)new QEncodingStringTransform(encodedText);
ICharacterEncoding encoding = Encodings.GetEncoding(
charset,
true);
if (encoding == null) {
// System.out.println("Unknown charset " + charset);
decodedWord = str.substring(
startIndex - 2, (
startIndex - 2)+(afterLast - (startIndex - 2)));
acceptedEncodedWord = false;
} else {
// System.out.println("Encoded " + (base64 ? "B" : "Q") +
// " to: " + (encoding.GetString(transform)));
decodedWord = Encodings.DecodeToString(encoding, transform);
// Check for text in the decoded String
// that could render the comment syntactically invalid
// (the encoded
// word could even encode ASCII control characters and
// specials)
if (context == EncodedWordContext.Phrase &&
HasSuspiciousTextInStructured(decodedWord)) {
hasSuspiciousText = true;
} else {
hasSuspiciousText |= context == EncodedWordContext.Comment &&
HasSuspiciousTextInComments(decodedWord);
}
wordsWereDecoded = true;
}
} else {
decodedWord = str.substring(
startIndex - 2, (
startIndex - 2)+(afterLast - (startIndex - 2)));
}
} else {
decodedWord = str.substring(
startIndex - 2, (
startIndex - 2)+(afterLast - (startIndex - 2)));
}
index = afterLast;
}
}
if (whitespaceStart >= 0 && whitespaceStart < whitespaceEnd &&
(!acceptedEncodedWord || !lastWordWasEncodedWord)) {
// Append whitespace as long as it doesn't occur between two
// encoded words
builder.append(
str.substring(
whitespaceStart, (
whitespaceStart)+(whitespaceEnd - whitespaceStart)));
}
if (startParen) {
builder.append('(');
}
if (decodedWord != null) {
builder.append(decodedWord);
}
// System.out.println(builder);
// System.out.println("" + index + " " + endIndex + " [" +
// (index= 0 && retval.indexOf(
"?=") >= 0))) {
if (context == EncodedWordContext.Comment) {
String wrappedComment = "(" + retval + ")";
if (
HeaderParserUtility.ParseCommentStrict(
wrappedComment,
0,
wrappedComment.length()) != wrappedComment.length()) {
// Comment is syntactically invalid after decoding, so
// don't decode any of the encoded words
return str.substring(start, (start)+(endIndex - start));
}
}
if (context == EncodedWordContext.Phrase) {
if (HasAsciiCtl(retval)) {
return str.substring(start, (start)+(endIndex - start));
}
retval = HeaderParserUtility.QuoteValue(retval);
}
}
return retval;
}
private static boolean FollowedByEndOrLinearWhitespace(
String str,
int index,
int endIndex) {
if (index == endIndex) {
return true;
}
if (str.charAt(index) != 0x09 && str.charAt(index) != 0x20 && str.charAt(index) != 0x0d) {
return false;
}
int cws = HeaderParser.ParseCFWS(str, index, endIndex, null);
return cws != index;
}
private static boolean PrecededByStartOrLinearWhitespace(
String str,
int index) {
return (index == 0) || (index - 1 >= 0 && (str.charAt(index - 1) == 0x09 ||
str.charAt(index - 1) == 0x20));
}
private static int IndexOfNextPossibleEncodedWord(
String str,
int index,
int endIndex) {
int cws = HeaderParser.ParseCFWS(str, index, endIndex, null);
if (cws == index) {
// No linear whitespace
return -1;
}
while (index < cws) {
if (str.charAt(index) == '(') {
// Has a comment, so no encoded word
// immediately follows
return -1;
}
++index;
}
if (index + 1 < endIndex && str.charAt(index) == '=' && str.charAt(index + 1) == '?') {
// Has a possible encoded word
return index;
}
return -1;
}
public static String DecodePhraseText(
String str,
int index,
int endIndex,
List tokens,
boolean withComments) {
// Assumes the value matches the production "phrase",
// and assumes that endIndex is the end of all CFWS
// found after the phrase.
if (withComments) {
int io = str.indexOf("=?",index);
if (io < 0 || io >= endIndex) {
// No encoded words found in the given area
return str.substring(index, (index)+(endIndex - index));
}
}
StringBuilder builder = new StringBuilder();
int lastIndex = index;
boolean appendSpace = false;
// Get each relevant token sorted by starting index
for (int[] token : tokens) {
boolean hasCFWS = false;
if (!(token[1] >= lastIndex && token[1] >= index && token[1] <= endIndex &&
token[2] >= index && token[2] <= endIndex)) {
continue;
}
if (token[0] == HeaderParserUtility.TokenComment && withComments) {
// This is a comment token
int startIndex = token[1];
builder.append(str.substring(lastIndex, (lastIndex)+(startIndex + 1 - lastIndex)));
String newComment = Rfc2047.DecodeEncodedWords(
str,
startIndex + 1,
token[2] - 1,
EncodedWordContext.Comment);
builder.append(newComment);
lastIndex = token[2] - 1;
} else if (token[0] == HeaderParserUtility.TokenPhraseAtom ||
token[0] == HeaderParserUtility.TokenPhraseAtomOrDot) {
// This is an atom token; only words within
// a phrase can be encoded words; the first character
// starts the actual atom rather than a comment or whitespace
int wordStart = token[1];
int wordEnd = wordStart;
int previousWord = wordStart;
if (wordStart < token[2] && str.charAt(wordStart) == '=') {
// This word may be an encoded word
wordEnd = wordStart;
while (true) {
if (!PrecededByStartOrLinearWhitespace(str, wordEnd)) {
// The encoded word is not preceded by whitespace and
// doesn't start the String, so it's not valid
break;
}
// Find the end of the atom
wordEnd = HeaderParser.ParsePhraseAtom(
str,
wordEnd,
endIndex,
null);
if (!FollowedByEndOrLinearWhitespace(str, wordEnd, endIndex)) {
// The encoded word is not followed by whitespace, so it's
// not valid
wordEnd = previousWord;
break;
}
int nextWord = IndexOfNextPossibleEncodedWord(
str,
wordEnd,
endIndex);
if (nextWord < 0) {
// The next word isn't an encoded word
break;
}
previousWord = nextWord;
wordEnd = nextWord;
}
}
if (withComments) {
builder.append(str.substring(lastIndex, (lastIndex)+(wordStart - lastIndex)));
} else {
if (appendSpace) {
builder.append(' ');
appendSpace = false;
}
}
if (wordStart == wordEnd) {
wordEnd = token[2];
builder.append(str.substring(wordStart, (wordStart)+(wordEnd - wordStart)));
} else {
String replacement = Rfc2047.DecodeEncodedWords(
str,
wordStart,
wordEnd,
EncodedWordContext.Phrase);
builder.append(replacement);
}
hasCFWS = HeaderParser.ParseCFWS(str, wordEnd, endIndex, null) !=
wordEnd;
lastIndex = wordEnd;
} else if (token[0] == HeaderParserUtility.TokenQuotedString &&
!withComments) {
if (appendSpace) {
builder.append(' ');
appendSpace = false;
}
int tokenIndex = MediaType.skipQuotedString(
str,
token[1],
token[2],
builder);
// tokenIndex is now just after the end quote
hasCFWS = HeaderParser.ParseCFWS(str, tokenIndex, endIndex, null) !=
tokenIndex;
}
appendSpace |= hasCFWS;
}
if (withComments) {
builder.append(str.substring(lastIndex, (lastIndex)+(endIndex - lastIndex)));
}
return builder.toString();
}
private static void EncodePhraseTextInternal(
String str,
int index,
int endIndex,
List tokens,
StringBuilder builder) {
// Assumes the value matches the production "phrase"
// and that there are no comments in the value
if (index == endIndex) {
return; // Empty, so nothing to do
}
int index2 = HeaderParser.ParseCFWS(str, index, endIndex, null);
if (index2 == endIndex) {
// Just linear whitespace
builder.append(str.substring(index, (index)+(endIndex - index)));
return;
}
if (!PrecededByStartOrLinearWhitespace(str, index2)) {
// Append a space before the encoded words
builder.append(' ');
} else {
// Append the linear whitespace
builder.append(str.substring(index, (index)+(index2 - index)));
}
EncodedWordEncoder encoder = new EncodedWordEncoder();
StringBuilder builderPhrase = new StringBuilder();
index = index2;
while (index < endIndex) {
if (str.charAt(index) == '"') {
// Quoted String
index = MediaType.skipQuotedString(
str,
index,
endIndex,
builderPhrase);
} else {
// Atom
index2 = HeaderParser.ParsePhraseAtomOrDot(
str,
index,
endIndex,
null);
builderPhrase.append(str.substring(index, (index)+(index2 - index)));
index = index2;
}
index2 = HeaderParser.ParseFWS(str, index, endIndex, null);
if (index2 == endIndex) {
encoder.AddString(builderPhrase.toString());
encoder.FinalizeEncoding();
builder.append(encoder.toString());
if (index2 != index) {
builder.append(str.substring(index, (index)+(index2 - index)));
} else if (!FollowedByEndOrLinearWhitespace(
str,
endIndex,
str.length())) {
// Add a space if no linear whitespace follows
builder.append(' ');
}
break;
}
if (index2 != index) {
builderPhrase.append(' ');
}
index = index2;
}
}
public static String EncodeString(String str) {
if (str == null) {
throw new NullPointerException("str");
}
return EncodeString(str, 0, str.length());
}
public static String EncodeString(String str, int index, int endIndex) {
if (str == null) {
throw new NullPointerException("str");
}
if (index < 0) {
throw new IllegalArgumentException("index (" + index + ") is less than " +
"0");
}
if (index > str.length()) {
throw new IllegalArgumentException("index (" + index + ") is more than " +
str.length());
}
if (endIndex < 0) {
throw new IllegalArgumentException("endIndex (" + endIndex +
") is less than " + "0");
}
if (endIndex > str.length()) {
throw new IllegalArgumentException("endIndex (" + endIndex +
") is more than " + str.length());
}
if (str.length() - index < endIndex) {
throw new IllegalArgumentException("str's length minus " + index + " (" +
(str.length() - index) + ") is less than " + endIndex);
}
return new EncodedWordEncoder().AddString(
str.substring(index, (index)+(endIndex))).FinalizeEncoding().toString();
}
public static String EncodePhraseText(
String str,
int index,
int endIndex,
List tokens) {
// Assumes the value matches the production "phrase",
// and assumes that endIndex is the end of all whitespace
// found after the phrase. Doesn't encode text within comments.
if (index == endIndex) {
return "";
}
if (!Message.HasTextToEscape(str, index, endIndex)) {
// No need to use encoded words
return str.substring(index, (index)+(endIndex - index));
}
int lastIndex = index;
StringBuilder builder = new StringBuilder();
for (int[] token : tokens) {
if (!(token[1] >= lastIndex && token[1] >= index && token[1] <= endIndex &&
token[2] >= index && token[2] <= endIndex)) {
continue;
}
if (token[0] == HeaderParserUtility.TokenComment) {
// Process this piece of the phrase
EncodePhraseTextInternal(str, lastIndex, token[1], tokens, builder);
// Append the comment
builder.append(str.substring(token[1], (token[1])+(token[2] - token[1])));
lastIndex = token[2];
}
}
EncodePhraseTextInternal(str, lastIndex, endIndex, tokens, builder);
return builder.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy