/**
 *  Copyright 2004 Jeroen van Bergen
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.james.transport.mailets.mailets;

import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
import org.apache.mailet.MailetException;

import javax.mail.internet.MimeMessage;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.Multipart;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.*;

/**
 * <p>Scan a mail for an attachment with a name the matches a given pattern. If a match is found, the content of the
 * attachment is written to a file in a given directory, using the filename of the attachment.  If an attachment with
 * the same name is received, an existing file will be overwritten.</p>
 *
 * <p>Please note that this class uses the regular expressions that are available since Java2 1.4. This class will therefor
 * not work on earlier Java versions.</p>
 * <p>Configuration:</p>
 * <p>
 * <pre>
 * &#x3C;mailet match="All" class="org.apache.james.transport.mailets.StoreAttachment"&#x3E;
 *   &#x3C;pattern&#x3E;.*\.xls&#x3C;/pattern&#x3E; &#x3C;!-- The regular expression that must be matched --&#x3E;
 *   &#x3C;directory&#x3E;c:\temp\james_attach&#x3C;/directory&#x3E;  &#x3C;!-- The directory to save to --&#x3E;
 * &#x3C;/mailet&#x3E;
 * </pre>
 * </p>
 */
public class StoreAttachment extends GenericMailet {

  /**
   * Checks if the mandatory parameters are present, creates the directory to save the files in (if not present).
   *
   * @throws MailetException
   */
  public void init() throws MailetException {
    _patternString = getInitParameter(PATTERN_PARAMETER_NAME);
    if (_patternString == null) {
      throw new MailetException("No value for " + PATTERN_PARAMETER_NAME + " parameter was provided.");
    }
    _directoryName = getInitParameter(DIRECTORY_PARAMETER_NAME);
    if (_directoryName == null) {
      throw new MailetException("No value for " + DIRECTORY_PARAMETER_NAME + " parameter was provided.");
    }
    try {
      _regExPattern = Pattern.compile(_patternString);
    } catch (Exception e) {
      throw new MailetException("Could not compile regex [" + _patternString + "].");
    }
    try {
      File saveDirectory = null;
      saveDirectory = new File(_directoryName);
      if (! saveDirectory.exists()) {
        saveDirectory.mkdirs();
      }
    } catch (Exception e) {
      throw new MailetException("Could not create directory [" + _directoryName + "].", e);
    }
    log("StoreAttachment is initialised with regex pattern [" + _patternString +  "] and will save to directory [" + _directoryName + "]");
  }

  /**
   * Service the mail: scan it for attchments matching the pattern, store the content of a matching attachment in the
   * given directory.
   *
   * @param mail The mail to service
   * @throws MailetException Thrown when an error situation is encountered.
   */
  public void service(Mail mail) throws MailetException {
    MimeMessage message = null;
    try {
      message = mail.getMessage();
    } catch (MessagingException e) {
      throw new MailetException("Could not retrieve message from Mail object", e);
    }
    // All MIME messages with an attachment are multipart, so we do nothing if it is not mutlipart
    try {
      if (message.getContentType().startsWith("multipart")) {
        analyseMessage(message);
      }
    } catch (MessagingException e) {
      throw new MailetException("Could not retrieve contenttype of message.", e);
    } catch (Exception e) {
      throw new MailetException("Could not analyse message.", e);
    }
  }

  /**
   * returns a String describing this mailet.
   * @return A desciption of this mailet
   */
  public String getMailetInfo() {
    return "StoreAttachment";
  }

  /**
   * Checks every part in this part (if it is a Multipart) for having a filename that matches the pattern. If the name
   * matches, the content of the part is stored (using its name) in te given diretcory.
   *
   * Note: this method is recursive.
   *
   * @param part The part to analyse.
   * @throws Exception
   */
  private void analyseMessage(Part part) throws Exception {
    String fileName = null;
    fileName = part.getFileName();
    // filename or name of part can be null, so we have to be careful
    if (fileName != null) {
      if (fileNameMatches(fileName)) {
        saveAttachmentToFile(part);
      }
    }
    if (part.isMimeType("multipart/*")) {
      try {
        Multipart multipart = (Multipart) part.getContent();
        int numParts = multipart.getCount();
        for (int i = 0; i < numParts; i++) {
          analyseMessage(multipart.getBodyPart(i));
        }
      } catch (Exception e) {
        log("Could not analyse part.", e);
      }
    }
    return;
  }

  /**
   * Checks if the given name matches the pattern.
   *
   * @param name The name to check for a match.
   * @return True if a match is found, false otherwise.
   */
  private boolean fileNameMatches(String name) {
    boolean result = false;
    _regExmatcher = _regExPattern.matcher(name);
    if (_regExmatcher.matches()) {
      result = true;
    }
    return result;
  }

  /**
   * Saves the content of the part to a file in the given directoy, using the name of the part. If a file with
   * that name already exists, it will
   *
   * @param part The MIME part to save.
   * @throws Exception
   */
  private void saveAttachmentToFile(Part part) throws Exception {
    log("saving content of " + part.getFileName() + "...");
    BufferedOutputStream os = null;
    InputStream is = null;
    try {
      os = new BufferedOutputStream(new FileOutputStream(_directoryName + File.separatorChar + part.getFileName()));
      is = part.getInputStream();
      if (!(is instanceof BufferedInputStream)) {
        is = new BufferedInputStream(is);
      }
      int c;
      while ((c = is.read()) != -1) {
        os.write(c);
      }
    } catch (Exception e) {
      log("Error while saving contents of [" + part.getFileName() + "].", e);
      throw e;
    } finally {
      is.close();
      os.close();
    }
  }

  private String _patternString = null; // The regex that describes the names of the attachments that should be saved
  private String _directoryName = null; //The name of the direcotry that is used to store the attachemnts in (
  private Pattern _regExPattern = null;  // The compiled regex
  private Matcher _regExmatcher = null;  // The regex matcher for the name of the attachment

  public static final String PATTERN_PARAMETER_NAME =  "pattern";  // The name of the parameter describing the names of the attachments to be saved
  public static final String DIRECTORY_PARAMETER_NAME =  "directory"; // the name of the directory in which the attachments will be saved.
}
