blob: ab80b89e8b8621859053f95ca3a80db901d1b02a [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.attachment;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
public class MimeBodyPartInputStream extends InputStream {
PushbackInputStream inStream;
boolean boundaryFound;
int pbAmount;
byte[] boundary;
byte[] boundaryBuffer;
private boolean closed;
public MimeBodyPartInputStream(PushbackInputStream inStreamParam,
byte[] boundaryParam,
int pbsize) {
super();
this.inStream = inStreamParam;
this.boundary = boundaryParam;
this.pbAmount = pbsize;
}
@Override
public int read(byte[] buf, int origOff, int origLen) throws IOException {
byte[] b = buf;
int off = origOff;
int len = origLen;
if (boundaryFound || closed) {
return -1;
}
if ((off < 0) || (off > b.length) || (len < 0)
|| ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
boolean bufferCreated = false;
if (len < boundary.length * 2) {
//buffer is too short to detect boundaries with it. We'll need to create a larger buffer
bufferCreated = true;
if (boundaryBuffer == null) {
boundaryBuffer = new byte[boundary.length * 2];
}
b = boundaryBuffer;
off = 0;
len = boundaryBuffer.length;
}
if (len > pbAmount) {
len = pbAmount; //can only pushback that much so make sure we can
}
int read = 0;
int idx = 0;
while (read >= 0 && idx < len && idx < (boundary.length * 2)) {
//make sure we read enough to detect the boundary
read = inStream.read(b, off + idx, len - idx);
if (read != -1) {
idx += read;
}
}
if (read == -1 && idx == 0) {
return -1;
}
len = idx;
int i = processBuffer(b, off, len);
if (bufferCreated && i > 0) {
// read more than we need, push it back
if (origLen >= i) {
System.arraycopy(b, 0, buf, origOff, i);
} else {
System.arraycopy(b, 0, buf, origOff, origLen);
inStream.unread(b, origLen, i - origLen);
i = origLen;
}
} else if (i == 0 && boundaryFound) {
return -1;
}
return i;
}
//Has Data after encountering CRLF
private boolean hasData(byte[] b, int initialPointer, int pointer, int off, int len)
throws IOException {
if (pointer < (off + len)) {
return true;
} else if (pointer >= 1000000000) {
inStream.unread(b, initialPointer, (off + len) - initialPointer);
return false;
} else {
int x = inStream.read();
if (x != -1) {
inStream.unread(x);
inStream.unread(b, initialPointer, (off + len) - initialPointer);
return false;
}
return true;
}
}
protected int processBuffer(byte[] buffer, int off, int len) throws IOException {
for (int i = off; i < (off + len); i++) {
boolean needUnread0d0a = false;
int value = buffer[i];
int initialI = i;
if (value == 13) {
if (!hasData(buffer, initialI, initialI + 1, off, len)) {
return initialI - off;
}
value = buffer[initialI + 1];
if (value != 10) {
continue;
}
if (!hasData(buffer, initialI, initialI + 2, off, len)) {
return initialI - off;
}
value = buffer[initialI + 2];
if ((byte) value != boundary[0]) {
i++;
continue;
}
needUnread0d0a = true;
i += 2; //i after this points to boundary[0] element
} else if (value != boundary[0]) {
continue;
}
int boundaryIndex = 0;
while ((boundaryIndex < boundary.length) && (value == boundary[boundaryIndex])) {
if (!hasData(buffer, initialI, i + 1, off, len)) {
return initialI - off;
}
value = buffer[++i];
boundaryIndex++;
}
if (boundaryIndex == boundary.length) {
// read the end of line character
if (initialI != off) {
i = 1000000000;
}
if (initialI - off != 0
&& !hasData(buffer, initialI, i + 1, off, len)) {
return initialI - off;
}
boundaryFound = true;
int j = i + 1;
if (j < len && buffer[j] == 45 && value == 45) {
// Last mime boundary should have a succeeding "--"
// as we are on it, read the terminating CRLF
i += 2;
//last mime boundary
}
//boundary matched (may or may not be last mime boundary)
int processed = initialI - off;
if ((len - ((i - off) + 2)) > 0) {
inStream.unread(buffer, i + 2, len - (i + 2) + off);
}
return processed;
}
// Boundary not found. Restoring bytes skipped.
// write first skipped byte, push back the rest
if (value != -1) { //pushing back first byte of boundary
// Stream might have ended
i--;
}
if (needUnread0d0a) { //Pushing all, returning 13
i = i - boundaryIndex;
i--; //for 10
// value = 13;
} else {
i = i - boundaryIndex;
i++;
// value = boundary[0];
}
}
return len;
}
public int read() throws IOException {
boolean needUnread0d0a = false;
if (boundaryFound) {
return -1;
}
// read the next value from stream
int value = inStream.read();
// A problem occurred because all the mime parts tends to have a /r/n
// at the end. Making it hard to transform them to correct
// DataSources.
// This logic introduced to handle it
if (value == 13) {
value = inStream.read();
if (value != 10) {
inStream.unread(value);
return 13;
}
value = inStream.read();
if ((byte) value != boundary[0]) {
inStream.unread(value);
inStream.unread(10);
return 13;
}
needUnread0d0a = true;
} else if ((byte) value != boundary[0]) {
return value;
}
// 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 (boundaryIndex == boundary.length) {
// boundary found
boundaryFound = true;
int dashNext = inStream.read();
// read the end of line character
if (dashNext == 45 && value == 45) {
// Last mime boundary should have a succeeding "--"
// as we are on it, read the terminating CRLF
inStream.read();
inStream.read();
}
return -1;
}
// Boundary not found. Restoring bytes skipped.
// write first skipped byte, push back the rest
if (value != -1) {
// Stream might have ended
inStream.unread(value);
}
if (needUnread0d0a) {
inStream.unread(boundary, 0, boundaryIndex);
inStream.unread(10);
value = 13;
} else {
inStream.unread(boundary, 1, boundaryIndex - 1);
value = boundary[0];
}
return value;
}
@Override
public void close() throws IOException {
this.closed = true;
}
}