/* ******************************************************************************
* Copyright (c) 2006-2016 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
/**
*
*/
package org.xmind.core.net.http;
import static org.xmind.core.net.internal.EncodingUtils.toAsciiBytes;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import org.xmind.core.net.Field;
import org.xmind.core.net.FieldSet;
import org.xmind.core.net.internal.EncodingUtils;
/**
*
* @author Frank Shaka
* @since 3.6.50
*/
public class MultipartEntity extends HttpEntity {
private static final String MULTIPART_CONTENT_TYPE = "multipart/form-data; boundary="; //$NON-NLS-1$
/**
* The pool of ASCII chars to be used for generating a multipart boundary.
*/
private static final byte[] BOUNDARY_CHARS = toAsciiBytes(
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); //$NON-NLS-1$
/** Carriage return/linefeed as a byte array */
private static final byte[] CRLF = toAsciiBytes("\r\n"); //$NON-NLS-1$
/** Content dispostion as a byte array */
private static final byte[] QUOTE = toAsciiBytes("\""); //$NON-NLS-1$
/** Extra characters as a byte array */
private static final byte[] EXTRA = toAsciiBytes("--"); //$NON-NLS-1$
/** Content dispostion as a byte array */
private static final byte[] CONTENT_DISPOSITION = toAsciiBytes(
"Content-Disposition: form-data; name="); //$NON-NLS-1$
/** Content type header as a byte array */
private static final byte[] CONTENT_TYPE = toAsciiBytes("Content-Type: "); //$NON-NLS-1$
/** Content type header as a byte array */
private static final byte[] CONTENT_TRANSFER_ENCODING = toAsciiBytes(
"Content-Transfer-Encoding: "); //$NON-NLS-1$
/** Attachment's file name as a byte array */
private static final byte[] FILE_NAME = toAsciiBytes("; filename="); //$NON-NLS-1$
private static final byte[] TEXT_CONTENT_TYPE = toAsciiBytes(
"text/plain; charset=utf-8"); //$NON-NLS-1$
private static final byte[] FILE_TRANSFER_ENCODING = toAsciiBytes("binary"); //$NON-NLS-1$
private static final byte[] TEXT_TRANSFER_ENCODING = toAsciiBytes("8bit"); //$NON-NLS-1$
private FieldSet parts;
private byte[] boundary = null;
/**
*
*/
public MultipartEntity(FieldSet parts) {
this.parts = parts;
}
/**
* @return the parts
*/
public FieldSet getParts() {
return parts;
}
private byte[] getBoundary() {
if (boundary != null)
return boundary;
Random rand = new Random();
byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size
// from 30 to 40
for (int i = 0; i < bytes.length; i++) {
bytes[i] = BOUNDARY_CHARS[rand.nextInt(BOUNDARY_CHARS.length)];
}
boundary = bytes;
return boundary;
}
public String getContentType() {
StringBuffer typeBuffer = new StringBuffer(MULTIPART_CONTENT_TYPE);
typeBuffer.append(EncodingUtils.toAsciiString(getBoundary()));
return typeBuffer.toString();
}
private static String toSafeName(String name) {
return name.replaceAll("\"", "%22"); //$NON-NLS-1$ //$NON-NLS-2$
}
public long getContentLength() {
if (parts.isEmpty())
return 0;
long length = 0;
for (Field part : parts) {
length += EXTRA.length;
length += getBoundary().length;
length += CRLF.length;
length += CONTENT_DISPOSITION.length;
length += QUOTE.length;
length += toAsciiBytes(toSafeName(part.name)).length;
length += QUOTE.length;
length += CRLF.length;
if (part.value instanceof HttpEntity) {
String fileName = ((HttpEntity) part.value).getFileName();
if (fileName == null)
fileName = part.name;
length += FILE_NAME.length;
length += QUOTE.length;
length += toAsciiBytes(toSafeName(fileName)).length;
length += QUOTE.length;
}
length += CONTENT_TYPE.length;
length += getContentType(part.value).length;
length += CRLF.length;
length += CONTENT_TRANSFER_ENCODING.length;
length += getTransferEncoding(part.value).length;
length += CRLF.length;
length += CRLF.length;
length += getPartDataLength(part);
length += CRLF.length;
}
length += EXTRA.length;
length += getBoundary().length;
length += EXTRA.length;
length += CRLF.length;
return length;
}
public void writeTo(OutputStream stream) throws IOException {
if (parts.isEmpty())
return;
for (Field part : parts) {
stream.write(EXTRA);
stream.write(getBoundary());
stream.write(CRLF);
stream.write(CONTENT_DISPOSITION);
stream.write(QUOTE);
stream.write(toAsciiBytes(toSafeName(part.name)));
stream.write(QUOTE);
if (part.value instanceof HttpEntity) {
String fileName = ((HttpEntity) part.value).getFileName();
if (fileName == null)
fileName = part.name;
stream.write(FILE_NAME);
stream.write(QUOTE);
stream.write(toAsciiBytes(toSafeName(fileName)));
stream.write(QUOTE);
}
stream.write(CRLF);
stream.write(CONTENT_TYPE);
stream.write(getContentType(part.value));
stream.write(CRLF);
stream.write(CONTENT_TRANSFER_ENCODING);
stream.write(getTransferEncoding(part.value));
stream.write(CRLF);
stream.write(CRLF);
writePartData(stream, part);
stream.write(CRLF);
}
stream.write(EXTRA);
stream.write(getBoundary());
stream.write(EXTRA);
stream.write(CRLF);
}
private static byte[] getContentType(Object value) {
if (value instanceof HttpEntity)
return EncodingUtils
.toAsciiBytes(((HttpEntity) value).getContentType());
return TEXT_CONTENT_TYPE;
}
private static byte[] getTransferEncoding(Object value) {
if (value instanceof HttpEntity)
return FILE_TRANSFER_ENCODING;
return TEXT_TRANSFER_ENCODING;
}
private static long getPartDataLength(Field part) {
if (part.value instanceof HttpEntity) {
return ((HttpEntity) part.value).getContentLength();
}
return toAsciiBytes(part.getValue()).length;
}
private static void writePartData(OutputStream stream, Field part)
throws IOException {
if (part.value instanceof HttpEntity) {
((HttpEntity) part.value).writeTo(stream);
} else {
writeFromText(stream, part.getValue());
}
}
private static void writeFromText(OutputStream writeStream,
String encodedText) throws IOException {
writeStream.write(encodedText.getBytes("UTF-8")); //$NON-NLS-1$
}
}