/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Nam Nguyen
*/
package com.caucho.quercus.lib.curl;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import com.caucho.quercus.QuercusContext;
import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.StringValue;
import com.caucho.quercus.env.Value;
import com.caucho.util.L10N;
import com.caucho.util.RandomUtil;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.TempBuffer;
public class MultipartBody extends PostBody
{
private static final L10N L = new L10N(CurlHttpRequest.class);
private ArrayList<MultipartEntry> _postItems
= new ArrayList<MultipartEntry>();
private String _boundary;
private byte []_boundaryBytes;
private long _length;
protected boolean init(Env env, Value body)
{
_boundary = createBoundary();
_boundaryBytes = _boundary.getBytes();
Iterator<Map.Entry<Value,Value>> iter = body.getIterator(env);
while (iter.hasNext()) {
Map.Entry<Value,Value> entry = iter.next();
StringValue key = entry.getKey().toString(env);
StringValue value = entry.getValue().toString(env);
if (value.length() > 0 && value.charAt(0) == '@') {
StringValue fileName = value.substring(1);
Path path = env.lookup(fileName);
if (path == null || ! path.canRead()) {
env.warning(L.l("cannot read file '{0}'", fileName));
return false;
}
_postItems.add(new PathEntry(env, key.toString(), path));
}
else
_postItems.add(new UrlEncodedEntry(env, key.toString(), value));
}
_length = getContentLength(_postItems, _boundary);
return true;
}
private static String createBoundary()
{
return "boundary" + RandomUtil.getRandomLong();
}
private static long getContentLength(ArrayList<MultipartEntry> list,
String boundary)
{
long size = (boundary.length() + 2) + 4;
for (MultipartEntry entry : list) {
size += entry.getLength() + (boundary.length() + 4) + 2;
}
return size;
}
public String getContentType()
{
return "multipart/form-data; boundary=\"" + _boundary + "\"";
}
public long getContentLength()
{
return _length;
}
public void writeTo(Env env,
OutputStream os)
throws IOException
{
for (MultipartEntry entry : _postItems) {
os.write('-');
os.write('-');
os.write(_boundaryBytes);
os.write('\r');
os.write('\n');
entry.write(env, os);
os.write('\r');
os.write('\n');
}
os.write('-');
os.write('-');
os.write(_boundaryBytes);
os.write('-');
os.write('-');
os.write('\r');
os.write('\n');
}
static abstract class MultipartEntry {
final String _name;
final String _header;
MultipartEntry(Env env, String name, String header)
{
_name = name;
_header = header;
}
final String getName()
{
return _name;
}
static String getHeader(String name,
String contentType,
String fileName)
{
StringBuilder sb = new StringBuilder();
sb.append("Content-Disposition: form-data;");
sb.append(" name=\"");
sb.append(name);
sb.append("\"");
if (fileName != null) {
sb.append("; filename=\"");
sb.append(fileName);
sb.append("\"");
sb.append('\r');
sb.append('\n');
sb.append("Content-Type: ");
sb.append(contentType);
}
return sb.toString();
}
final void write(Env env, OutputStream os)
throws IOException
{
int len = _header.length();
for (int i = 0; i < len; i++) {
os.write(_header.charAt(i));
}
os.write('\r');
os.write('\n');
os.write('\r');
os.write('\n');
writeData(env, os);
}
final long getLength()
{
return _header.length() + 4 + getLengthImpl();
}
abstract long getLengthImpl();
abstract void writeData(Env env, OutputStream os) throws IOException;
}
static class UrlEncodedEntry extends MultipartEntry {
StringValue _value;
UrlEncodedEntry(Env env, String name, StringValue value)
{
super(env, name, getHeader(name,
"application/x-www-form-urlencoded",
null));
_value = value;
}
long getLengthImpl()
{
return _value.length();
}
void writeData(Env env, OutputStream os)
throws IOException
{
os.write(_value.toString().getBytes());
}
}
static class PathEntry extends MultipartEntry {
Path _path;
PathEntry(Env env, String name, Path path)
{
super(env, name, getHeader(name,
getContentType(env, path.getTail()),
path.getTail()));
_path = path;
}
long getLengthImpl()
{
return _path.getLength();
}
void writeData(Env env, OutputStream os)
throws IOException
{
TempBuffer tempBuf = null;
try {
tempBuf = TempBuffer.allocate();
byte []buf = tempBuf.getBuffer();
ReadStream is = _path.openRead();
int len;
while ((len = is.read(buf, 0, buf.length)) > 0) {
os.write(buf, 0, len);
}
} finally {
if (tempBuf != null)
TempBuffer.free(tempBuf);
}
}
private static String getContentType(Env env, String name)
{
QuercusContext quercus = env.getQuercus();
ServletContext context = quercus.getServletContext();
if (context != null) {
String mimeType = context.getMimeType(name);
if (mimeType != null)
return mimeType;
else
return "application/octet-stream";
}
else {
int i = name.lastIndexOf('.');
if (i < 0)
return "application/octet-stream";
else if (name.endsWith(".txt"))
return "text/plain";
else if (name.endsWith(".jpg") || name.endsWith(".jpeg"))
return "image/jpeg";
else if (name.endsWith(".gif"))
return "image/gif";
else if (name.endsWith(".tif") || name.endsWith(".tiff"))
return "image/tiff";
else if (name.endsWith(".png"))
return "image/png";
else if (name.endsWith(".htm") || name.endsWith(".html"))
return "text/html";
else if (name.endsWith(".xml"))
return "text/xml";
else
return "application/octet-stream";
}
}
}
}