blob: 3b6708931436860df076c966c4828d0bff7996c9 [file] [log] [blame]
package org.bouncycastle.mime;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.bouncycastle.util.Iterable;
import org.bouncycastle.util.Strings;
public class Headers
implements Iterable<String>
{
private final Map<String, List> headers = new TreeMap<String, List>(String.CASE_INSENSITIVE_ORDER);
private final List<String> headersAsPresented;
private final String contentTransferEncoding;
private String boundary;
private boolean multipart;
private String contentType;
private Map<String, String> contentTypeParameters;
private static List<String> parseHeaders(InputStream src)
throws IOException
{
String s;
List<String> headerLines = new ArrayList<String>();
LineReader rd = new LineReader(src);
while ((s = rd.readLine()) != null)
{
if (s.length() == 0)
{
break;
}
headerLines.add(s);
}
return headerLines;
}
public Headers(InputStream source, String defaultContentTransferEncoding)
throws IOException
{
this(parseHeaders(source), defaultContentTransferEncoding);
}
public Headers(List<String> headerLines, String defaultContentTransferEncoding)
{
this.headersAsPresented = headerLines;
String header = "";
for (Iterator it = headerLines.iterator(); it.hasNext();)
{
String line = (String)it.next();
if (line.startsWith(" ") || line.startsWith("\t"))
{
header = header + line.trim();
}
else
{
if (header.length() != 0)
{
this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
}
header = line;
}
}
// pick up last header line
if (header.trim().length() != 0)
{
this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
}
String contentTypeHeader = (this.getValues("Content-Type") == null) ? "text/plain" : this.getValues("Content-Type")[0];
int parameterIndex = contentTypeHeader.indexOf(';');
if (parameterIndex < 0)
{
contentType = contentTypeHeader;
contentTypeParameters = Collections.EMPTY_MAP;
}
else
{
contentType = contentTypeHeader.substring(0, parameterIndex);
contentTypeParameters = createContentTypeParameters(contentTypeHeader.substring(parameterIndex + 1).trim());
}
contentTransferEncoding = this.getValues("Content-Transfer-Encoding") == null ? defaultContentTransferEncoding : this.getValues("Content-Transfer-Encoding")[0];
if (contentType.indexOf("multipart") >= 0)
{
multipart = true;
String bound = (String)contentTypeParameters.get("boundary");
boundary = bound.substring(1, bound.length() - 1); // quoted-string
}
else
{
boundary = null;
multipart = false;
}
}
/**
* Return the a Map of the ContentType attributes and their values.
*
* @return a Map of ContentType parameters - empty if none present.
*/
public Map<String, String> getContentTypeAttributes()
{
return contentTypeParameters;
}
/**
* Return the a list of the ContentType parameters.
*
* @return a list of ContentType parameters - empty if none present.
*/
private Map<String, String> createContentTypeParameters(String contentTypeParameters)
{
String[] parameterSplit = contentTypeParameters.split(";");
Map<String, String> rv = new LinkedHashMap<String, String>();
for (int i = 0; i != parameterSplit.length; i++)
{
String parameter = parameterSplit[i];
int eqIndex = parameter.indexOf('=');
if (eqIndex < 0)
{
throw new IllegalArgumentException("malformed Content-Type header");
}
rv.put(parameter.substring(0, eqIndex).trim(), parameter.substring(eqIndex + 1).trim());
}
return Collections.unmodifiableMap(rv);
}
public boolean isMultipart()
{
return multipart;
}
public String getBoundary()
{
return boundary;
}
public String getContentType()
{
return contentType;
}
public String getContentTransferEncoding()
{
return contentTransferEncoding;
}
private void put(String field, String value)
{
synchronized (this)
{
KV kv = new KV(field, value);
List<KV> list = (List<KV>)headers.get(field);
if (list == null)
{
list = new ArrayList<KV>();
headers.put(field, list);
}
list.add(kv);
}
}
public Iterator<String> getNames()
{
return headers.keySet().iterator();
}
public String[] getValues(String header)
{
synchronized (this)
{
List<KV> kvList = (List<KV>)headers.get(header);
if (kvList == null)
{
return null;
}
String[] out = new String[kvList.size()];
for (int t = 0; t < kvList.size(); t++)
{
out[t] = ((KV)kvList.get(t)).value;
}
return out;
}
}
public boolean isEmpty()
{
synchronized (this)
{
return headers.isEmpty();
}
}
public boolean containsKey(String s)
{
return headers.containsKey(s);
}
public Iterator<String> iterator()
{
return headers.keySet().iterator();
}
public void dumpHeaders(OutputStream outputStream)
throws IOException
{
for (Iterator it = headersAsPresented.iterator(); it.hasNext();)
{
outputStream.write(Strings.toUTF8ByteArray(it.next().toString()));
outputStream.write('\r');
outputStream.write('\n');
}
}
private class KV
{
public final String key;
public final String value;
public KV(String key, String value)
{
this.key = key;
this.value = value;
}
public KV(KV kv)
{
this.key = kv.key;
this.value = kv.value;
}
}
}