/***
* jwma Java WebMail
* Copyright (c) 2000-2003 jwma team
*
* jwma is free software; you can distribute and use this source
* under the terms of the BSD-style license received along with
* the distribution.
***/
package dtw.webmail.util;
import java.io.*;
import java.util.Hashtable;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import org.apache.log4j.Logger;
//import dtw.webmail.JwmaKernel;
/**
* Class that implements a Multipart that handles
* the multipart/form-data content type.
*
* @author Dieter Wimberger
* @version 0.9.7 07/02/2003
*/
public class FormdataMultipart extends MimeMultipart {
//logging
private static Logger log = Logger.getLogger(FormdataMultipart.class);
//instance attributes
private Hashtable m_Params = new Hashtable();
private boolean m_Removed = false;
/**
* Constructs a FormdataMultipart instance.
* This implementation just calls the superclass constructor.
*
* @return the newly created FormdataMultipart instance.
*/
public FormdataMultipart() {
super();
}//constructor
/**
* Constructs a FormdataMultipart instance.
* Automatically processes the body parts to extract parameters,
* and attachments.
*
* @param DataSource to construct the Multipart from, will be a
* MultipartInputStream.
*
* @return the newly created FormdataMultipart instance.
*/
public FormdataMultipart(DataSource ds)
throws MessagingException, IOException {
super(ds);
processBodyParts();
updateHeaders();
}//constructor
/**
* Returns the extracted parameters (with the extrcted values)
* as Hashtable.
*
* @return the extracted parameter data as Hashtable
*/
public Hashtable getParameters() {
return m_Params;
}//getParameters
/**
* Processes the body parts of the form-data.
* Extracts parameters and set values, and
* leaves over the attachments.
*
* @throws IOException if i/o operations fail.
* @throws MessagingException if parsing or part handling with
* Mail API classes fails.
*/
private void processBodyParts()
throws IOException, MessagingException {
//if write out to log for debug reasons!
//ByteArrayOutputStream bout=new ByteArrayOutputStream();
//writeTo(bout);
//JwmaKernel.getReference().debugLog().write(bout.toString());
for (int i = 0; i < getCount(); i++) {
MimeBodyPart mbp = (MimeBodyPart) getBodyPart(i);
processBodyPart(mbp);
if (m_Removed) {
m_Removed = false;
//decrease index i approbiately
i--;
}
}
setSubType("mixed");
//JwmaKernel.getReference().debugLog().write(
// "Processed multipart/form-data. Attachment parts:"+getCount()
//);
}//processParts
/**
* Processes a body part of the form-data.
* Extracts parameters and set values, and
* leaves over the attachments.
*
* @param mbp the MimeBodyPart to be processed.
*
* @throws IOException if i/o operations fail.
* @throws MessagingException if parsing or part handling with
* Mail API classes fails.
*/
private void processBodyPart(MimeBodyPart mbp)
throws MessagingException, IOException {
//String contenttype=new String(mbp.getContentType());
//JwmaKernel.getReference().debugLog().write("Processing "+contenttype);
//check if a content-type is given
String[] cts = mbp.getHeader("Content-Type");
if (cts == null || cts.length == 0) {
//this is a parameter, get it out and
//remove the part.
String controlname = extractName(
(mbp.getHeader("Content-Disposition"))[0]);
//JwmaKernel.getReference().debugLog().write("Processing control:"+controlname);
//retrieve value observing encoding
InputStream in = mbp.getInputStream();
String[] encoding = mbp.getHeader("Content-Transfer-Encoding");
if (encoding != null && encoding.length > 0) {
in = MimeUtility.decode(in, encoding[0]);
}
String value = extractValue(in);
if (value != null || !value.trim().equals("")) {
addParameter(controlname, value);
}
//flag removal
m_Removed = true;
removeBodyPart(mbp);
} else {
String filename = extractFileName(
(mbp.getHeader("Content-Disposition"))[0]);
//normally without file the control should be not successful.
//but neither netscape nor mircosoft iexploder care much.
//the only feature is an empty filename.
if (filename.equals("")) {
//kick it out too
m_Removed = true;
removeBodyPart(mbp);
} else {
//JwmaKernel.getReference().debugLog().write("Incoming filename="+filename);
//IExploder sends files with complete path.
//jwma doesnt want this.
int lastindex = filename.lastIndexOf("\\");
if (lastindex != -1) {
filename = filename.substring(lastindex + 1, filename.length());
}
//JwmaKernel.getReference().debugLog().write("Outgoing filename="+filename);
//Observe a possible encoding
InputStream in = mbp.getInputStream();
String[] encoding = mbp.getHeader("Content-Transfer-Encoding");
if (encoding != null && encoding.length > 0) {
in = MimeUtility.decode(in, encoding[0]);
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
OutputStream out = (OutputStream) bout;
int i = 0;
while ((i = in.read()) != -1) {
//maybe more efficient in buffers, but well
out.write(i);
}
out.flush();
out.close();
//create the datasource
MimeBodyPartDataSource mbpds =
new MimeBodyPartDataSource(
// contenttype,filename,bout.toByteArray()
cts[0], filename, bout.toByteArray()
);
//Re-set the Content-Disposition header, in case
//the file name was changed
mbp.removeHeader("Content-Disposition");
mbp.addHeader(
"Content-Disposition", "attachment; filename=\"" +
filename +
"\""
);
//set a base64 transferencoding und the data handler
mbp.addHeader("Content-Transfer-Encoding", "base64");
mbp.setDataHandler(new DataHandler(mbpds));
}
}
}//processBodyPart
/**
* Returns the name of a parameter by extracting it
* from the content-disposition header line.
*
* @param disposition the content-disposition header line as
* String.
*
* @return the name of the parameter as String.
*
* @throws IOException if the header line is malformed.
*/
private String extractName(String disposition)
throws IOException {
int end = 0;
int start = -1;
start = disposition.indexOf("name=\"");
end = disposition.indexOf("\"", start + 7); //offset is to skip name=\"
if (start == -1 || end == -1) {
throw new IOException("Mime header malformed.");
}
return disposition.substring(start + 6, end);
}//extractName
/**
* Returns the filename of an attachment by extracting it
* from the content-disposition header line.
*
* @param disposition the content-disposition header line as
* String.
*
* @return the filename of the attachment as String.
*
* @throws IOException if the header line is malformed.
*/
private String extractFileName(String disposition)
throws IOException {
int end = 0;
int start = -1;
start = disposition.indexOf("filename=\"");
end = disposition.indexOf("\"", start + 10); //offset is to skip filename=\"
if (start == -1 || end == -1) {
throw new IOException("Mime header malformed.");
}
return disposition.substring(start + 10, end);
}//extractFileName
/**
* Returns the value of a parameter by extracting it
* from the InputStream that represents the content
* of the (parameter) part.
*
* @param in InputStream that reads from the content
* of the (parameter) part.
*
* @return the value of the parameter as String.
*
* @throws IOException if reading from the stream fails.
*/
private String extractValue(InputStream in)
throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i = 0;
while ((i = in.read()) != -1) {
out.write(i);
}
out.flush();
out.close();
in.close();
//JwmaKernel.getReference().debugLog().write("Retrieved value="+out.toString());
//apply a little bit of magic when returning
return out.toString("iso-8859-1");
}//extractValue
/**
* Adds a parameter and mapped value to the parameters collection.
* If the parameter already exists, it adds another value to
* an already existing parameter by extending the array of strings.
*
* @param name the name of the parameter as String.
* @param value the value of the parameter as String.
*/
private void addParameter(String name, String value) {
String values[];
//JwmaKernel.getReference().debugLog().write("Adding "+name+"="+value);
if (m_Params.containsKey(name)) {
String oldValues[] = (String[]) m_Params.get(name);
values = new String[oldValues.length + 1];
for (int i = 0; i < oldValues.length; i++) {
values[i] = oldValues[i];
}
values[oldValues.length] = value;
} else {
values = new String[1];
values[0] = value;
}
m_Params.put(name, values);
}//addParameter
}//FormdataMultipart