Email Java

/***
 * 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