in geronimo-mail_2.1_spec/src/main/java/jakarta/mail/internet/MimeMultipart.java [580:736]
public int read() throws IOException {
if (boundaryFound) {
return -1;
}
// read the next value from stream
final int firstChar = inStream.read();
// premature end? Handle it like a boundary located
if (firstChar == -1) {
//DO NOT treat this a a boundary because if we do so we have no chance to detect missing end boundaries
return -1;
}
// we first need to look for a line boundary. If we find a boundary, it can be followed by the
// boundary marker, so we need to remember what sort of thing we found, then read ahead looking
// for the part boundary.
// NB:, we only handle [\r]\n--boundary marker[--]
// we need to at least accept what most mail servers would consider an
// invalid format using just '\n'
if (firstChar != '\r' && firstChar != '\n') {
// not a \r, just return the byte as is
return firstChar;
}
// we might need to rewind to this point. The padding is to allow for
// line terminators and linear whitespace on the boundary lines
inStream.mark(boundary.length + 1000);
// we need to keep track of the first read character in case we need to
// rewind back to the mark point
int value = firstChar;
// if this is a '\r', then we require the '\n'
if (value == '\r') {
// now scan ahead for the second character
value = inStream.read();
if (value != '\n') {
// only a \r, so this can't be a boundary. Return the
// \r as if it was data, after first resetting
inStream.reset();
return '\r';
}
}
value = inStream.read();
// if the next character is not a boundary start, we
// need to handle this as a normal line end
if ((byte) value != boundary[0]) {
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
// we're here because we found a "\r\n-" sequence, which is a potential
// boundary marker. Read the individual characters of the next line until
// we have a mismatch
// read value is the first byte of the boundary. Start matching the
// next characters to find a boundary
int boundaryIndex = 0;
while ((boundaryIndex < boundary.length) && ((byte) value == boundary[boundaryIndex])) {
value = inStream.read();
boundaryIndex++;
}
// if we didn't match all the way, we need to push back what we've read and
// return the EOL character
if (boundaryIndex != boundary.length) {
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
// The full boundary sequence should be \r\n--boundary string[--]\r\n
// if the last character we read was a '-', check for the end terminator
if (value == '-') {
value = inStream.read();
// crud, we have a bad boundary terminator. We need to unwind this all the way
// back to the lineend and pretend none of this ever happened
if (value != '-') {
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
// on the home stretch, but we need to verify the LWSP/EOL sequence
value = inStream.read();
// first skip over the linear whitespace
while (value == ' ' || value == '\t') {
value = inStream.read();
}
// We've matched the final boundary, skipped any whitespace, but
// we've hit the end of the stream. This is highly likely when
// we have nested multiparts, since the linend terminator for the
// final boundary marker is eated up as the start of the outer
// boundary marker. No CRLF sequence here is ok.
if (value == -1) {
// we've hit the end of times...
finalBoundaryFound = true;
// we have a boundary, so return this as an EOF condition
boundaryFound = true;
return -1;
}
// this must be a CR or a LF...which leaves us even more to push back and forget
if (value != '\r' && value != '\n') {
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
// if this is carriage return, check for a linefeed
if (value == '\r') {
// last check, this must be a line feed
value = inStream.read();
if (value != '\n') {
// SO CLOSE!
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
}
// we've hit the end of times...
finalBoundaryFound = true;
}
else {
// first skip over the linear whitespace
while (value == ' ' || value == '\t') {
value = inStream.read();
}
// this must be a CR or a LF...which leaves us even more to push back and forget
if (value != '\r' && value != '\n') {
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
// if this is carriage return, check for a linefeed
if (value == '\r') {
// last check, this must be a line feed
value = inStream.read();
if (value != '\n') {
// SO CLOSE!
// Boundary not found. Restoring bytes skipped.
// just reset and return the first character as data
inStream.reset();
return firstChar;
}
}
}
// we have a boundary, so return this as an EOF condition
boundaryFound = true;
return -1;
}