/*
* MultipartSection.java
*
* Created on Nov 18, 2007, 12:28:56 PM
*
* Parses a multipart/form-data section and then allows access
* to the data it contains.
*
*/
package com.pugh.sockso.web;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.apache.log4j.Logger;
public class MultipartSection {
private static final Logger log = Logger.getLogger( MultipartSection.class );
private String name, filename, contentType, data;
private File temporaryFile;
/**
* creates a new MultipartSection
*
* @throws java.io.IOException
*
*/
public MultipartSection() throws IOException {
name = "";
filename = "";
contentType = "text/plain";
temporaryFile = File.createTempFile( "sockso-upload-" +Math.random(), "dat" );
}
/**
* takes a string containing a multipart section and
* parses it for all it's info
*
* @param buffer
* @param boundary
*
* @throws IOException
*
*/
public void process( final InputBuffer buffer, final String boundary ) throws IOException {
// parse headers so we know what we're dealing with
parseHeaders( buffer );
final boolean writeToFile = !filename.equals( "" );
final DataOutput out = writeToFile
? new DataOutputStream( new FileOutputStream(temporaryFile) )
: new StringOutputStream();
// parse data
parseData( buffer, boundary, out );
// transform data if needed
if ( !writeToFile ) {
data = ( (StringOutputStream) out ).toString();
}
}
/**
* writes data from the input stream to a temporary file
*
* @param buffer
* @param boundary
* @param out
*
* @throws IOException
*
*/
protected void parseData( final InputBuffer buffer, final String boundary, final DataOutput out ) throws IOException {
final String terminator = HttpResponse.HTTP_EOL + "--" +boundary;
// read through data
final int length = terminator.length();
final int firstChar = terminator.charAt( 0 );
final int[] term = new int[ length ];
for ( int i=0; i<length; i++ ) {
term[ i ] = terminator.charAt( i );
}
// read through
matchFound:
while ( true ) {
final int c = buffer.read();
if ( c == -1 ) break;
// if we've matched the first char, lets see if we can match
// the whole of the terminator string
if ( c == firstChar ) {
for ( int i=1; i<length; i++ ) {
final int d = buffer.readDirectly();
buffer.putBack( d );
// if this char doesn't match, fail...
if ( (char) d != term[i] ) {
break;
}
// if we've gone along the whole string matching we've
// found what we're looking for!
if ( i == length - 1 ) {
break matchFound;
}
}
}
out.writeByte( c );
}
buffer.skip( 1 );
}
/**
* parses the chunk of headers and extracts the info to
* populate ourself
*
* @param headerInfo multiline header string
*
*/
protected void parseHeaders( final InputBuffer buffer ) throws IOException {
while ( true ) {
final String header = buffer.readLine();
if ( header == null || header.equals("") ) {
break;
}
// extract header name and data, we then have to get the data
// out of each header, which is stored kinda ugly i think, so
// it's a pain in the arse to get it out!! too many literals...
final Pattern p = Pattern.compile( "(.*?): (.*)" );
final Matcher m = p.matcher( header );
if ( m.matches() ) {
final String headerName = m.group( 1 ).toLowerCase();
final String headerData = m.group( 2 );
// content-type
if ( headerName.equals("content-type") ) {
contentType = headerData;
}
// content-disposition
else if ( headerName.equals("content-disposition") ) {
processContentDisposition( headerData );
}
}
}
}
/**
* processes a "Content-disposition" header
*
* @param headerData
*
*/
protected void processContentDisposition( final String headerData ) {
for ( final String pairData : headerData.split(";") ) {
final String parts[] = pairData.split( "=" );
if ( parts.length == 2 ) {
final String pairName = parts[ 0 ].trim();
// remove enclosing quotes from value
final String pairValue = parts[ 1 ].substring( 1, parts[1].length()-1 );
if ( pairName.equals("name") )
name = pairValue;
else if ( pairName.equals("filename") )
filename = pairValue;
}
}
}
public String getData() { return data; }
public String getName() { return name; }
public String getFilename() { return filename; }
public File getTemporaryFile() { return temporaryFile; }
public String getContentType() { return contentType; }
@Override
public String toString() {
return "(MultipartSection) " +
"Name: '" +name+ "'; " +
"Filename: '" +filename+ "'; " +
"content-type: '" +contentType+ "';";
}
}