Package org.jasen.util

Source Code of org.jasen.util.MimeUtils

/*
* @(#)MimeUtils.java  31/10/2004
*
* Copyright (c) 2004, 2005  jASEN.org
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   1. Redistributions of source code must retain the above copyright notice,
*      this list of conditions and the following disclaimer.
*
*   2. Redistributions in binary form must reproduce the above copyright
*      notice, this list of conditions and the following disclaimer in
*      the documentation and/or other materials provided with the distribution.
*
*   3. The names of the authors may not be used to endorse or promote products
*      derived from this software without specific prior written permission.
*
*   4. Any modification or additions to the software must be contributed back
*      to the project.
*
*   5. Any investigation or reverse engineering of source code or binary to
*      enable emails to bypass the filters, and hence inflict spam and or viruses
*      onto users who use or do not use jASEN could subject the perpetrator to
*      criminal and or civil liability.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JASEN.ORG,
* OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.jasen.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import javax.mail.Address;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.AddressException;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.ParseException;

import org.jasen.error.DNSException;
import org.jasen.error.ErrorHandlerBroker;
import org.jasen.error.JasenParseException;
import org.jasen.error.ParseErrorType;
import org.jasen.interfaces.DNSResolver;
import org.jasen.interfaces.InetAddressResolver;
import org.jasen.interfaces.ReceivedHeaderParser;
import org.jasen.interfaces.ReceivedHeaderParserData;
import org.jasen.io.ByteToCharUTF7Converter;
import org.jasen.net.MXRecord;


/**
* <P>
*   General Mime utilities.
* </P>
* @author Jason Polites
*/
public class MimeUtils
{

    /**
     * Undetermined (test not yet performed)
     */
    public static final int FORGERY_UNDETERMINED = -1;

    /**
     * Confirmed forgery
     */
    public static final int FORGERY_CONFIRMED = 1;

    /**
     * Confirmed authentic
     */
    public static final int FORGERY_REJECTED = 0;

    /**
     * Forgery status could not be determined with absolute certainty
     */
    public static final int FORGERY_UNKNOWN = 2;

    public static String[] ATTACHMENT_DISPOSITIONS = {MimeMessage.ATTACHMENT, MimeMessage.INLINE};

  public static Header[] getAllHeaders(MimeMessage message) throws MessagingException {

      Header[] headers = null;
    Enumeration e = message.getAllHeaders();

    // We have to use a Vector here because we can't get the size
    Vector vHeaders = new Vector();

    while (e.hasMoreElements()) {
      vHeaders.add(e.nextElement());
      headers = (Header[]) vHeaders.toArray(new Header[vHeaders.size()]);
    }
    return headers;
  }

  public static boolean isValidAddress(String address) {
    String regex = "^[a-zA-Z][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";
    return address.matches(regex);
  }

  /**
   * Verifies the given sender address against the information in the last (most recent) received header line
   * <BR><BR>
   * Specifically, this does the following:
   * <BR><BR>
   * get domain of sender<BR>
   * get IP address of last (most recent) MTA<BR>
   * get hostname of last (most recent) MTA<BR>
   * if (MTA IP Address resolves to MTA hostname) then<BR>
   * use MTA hostname for MX IP records<BR>
   * else<BR>
   * use MTA IP Address<BR>
   *
   * get MX records for sender domain<BR>
   * if(at least 1 MX record IP matches MTA IP) then valid<BR>
   *
   * @param parser The parser to use on the header line
   * @param receivedHeaderLine The last (most recent) received header line
   * @param senderAddress The From address (NOT the envelope address)
   * @return True if the sender is valid, false otherwise
   * @throws JasenParseException
   * @throws UnknownHostException
   * @throws DNSException
   */
  public static boolean verifySenderAddress(DNSResolver dresolver, InetAddressResolver iresolver, ReceivedHeaderParser parser, String receivedHeaderLine, String senderAddress) throws JasenParseException, UnknownHostException, DNSException {

    ReceivedHeaderParserData data = parser.parse(receivedHeaderLine, iresolver);
   
    if(data == null) {
        throw new JasenParseException("Unable to parse header.", ParseErrorType.PARSE_ERROR);
    }

    // Use the InetAddress cache
    InetAddress receiverAddress = iresolver.getByName(data.getSenderIPAddress());
    String senderDomain = getDomainFromAddress(senderAddress);

    String receiverDomain = receiverAddress.getHostName();

    // Get the MX records for the sender domain
    MXRecord[] senderMXRecords = DNSUtils.getMXRecords(dresolver, senderDomain);

    if (senderMXRecords != null && senderMXRecords.length > 0) {

      // Get the root domain for both the receiver and sender
      if (!DNSUtils.isIPv4Address(receiverDomain)) {

        String senderRootDomain = DNSUtils.getRootDomain(data.getSenderHostName());
        String receiverRootDomain = DNSUtils.getRootDomain(receiverDomain);

        if (receiverRootDomain.equalsIgnoreCase(senderRootDomain)) {

          // Get the MX records for the receiver domain
          MXRecord[] receiverMXRecords = DNSUtils.getMXRecords(dresolver, receiverRootDomain);

          // The sender address is valid if one IP matches
          MXRecord senderMX = null;
          MXRecord receiverMX = null;

          if (senderMXRecords != null && senderMXRecords.length > 0) {
            if (receiverMXRecords != null && receiverMXRecords.length > 0) {
              for (int i = 0; i < senderMXRecords.length; i++) {
                senderMX = senderMXRecords[i];

                for (int j = 0; j < receiverMXRecords.length; j++) {
                  receiverMX = receiverMXRecords[j];

                  if (senderMX.getAddress().getHostAddress().equalsIgnoreCase(receiverMX.getAddress().getHostAddress())) {
                    return true;
                  }
                }
              }
            }
            else {
              return false;
            }
          }
          else {
            return false;
          }
        }
        else {
          // The IP does not match the host.  This may be a forgery but we will still test the MX
          // records against the IP address
          String mtaIPAddress = data.getSenderIPAddress();
          for (int i = 0; i < senderMXRecords.length; i++) {
            if (senderMXRecords[i].getAddress().getHostAddress().equals(mtaIPAddress)) {
              return true;
            }
          }
        }
      }
      else {
        // We could not resolve the receiver IP address to a domain
        return false;
      }
    }
    else {
      // No valid MX record for this sender
      return false;
    }

    return false;
  }

  /**
   * Converts an Address object to an Internet Address with strict address parsing
   * @param address
   * @return The parsed InternetAddress
   * @throws AddressException
   */
  public static InternetAddress toInternetAddress(Address address) throws AddressException {
    return toInternetAddress(address, true);
  }

  /**
   * Converts an Address object to an Internet Address
   * @param address
   * @param strict If true, address parsing is strict
   * @return The parsed InternetAddress
   * @throws AddressException
   */
  public static InternetAddress toInternetAddress(Address address, boolean strict) throws AddressException {
    if (address instanceof InternetAddress) {
      return (InternetAddress) address;
    }
    else {
      return InternetAddress.parse(address.toString(), strict)[0];
    }
  }


  /**
   * Determines if the loaded message is a forgery.
   * <br/>
   * This is done by looking at the last (most recent) received header and determining if the hostname of
   * the sending server matches the hostname provided by the header information via the DNS
   * @param resolver The resolver used to resolve InetAddresses
   * @param message The MimeMessage to test
   * @param senderAddress The envelope sender
   * @param parser The parser to use to parse the "Received" header(s)
   * @return An integer representing the determination. 0 = Not a forgery, 1 = Definately a forgery, 2 = Unsure or could not be determined
   * @throws MessagingException
   * @throws CacheException
   * @throws JasenParseException
   */
  public static int isForgery(InetAddressResolver resolver, MimeMessage message, String senderAddress, ReceivedHeaderParser parser) throws MessagingException, JasenParseException {

      // Get the header
      String[] headers = message.getHeader("Received");

      if(headers != null) {

          boolean hostNameIsIP = false;

          String header = headers[0];

          // Parse the header
          ReceivedHeaderParserData data = null;
         
          try {
                data = parser.parse(header,resolver);
               
                if(data != null) {
//                  Get the IPAddress of the sending server
                String senderIP = data.getSenderIPAddress();

                // Get the reported hostname
                String senderHost = data.getSenderHostName();

                // If the sender host is an IP address, but not the same as the "actual"
                // IP, we have a forgery
                hostNameIsIP = DNSUtils.isIPAddress(senderHost);

                if(hostNameIsIP) {
                    if(!senderHost.equals(senderIP)) {
                        return FORGERY_CONFIRMED; // Forgery
                    }
                }

                  try
                  {
                      // Now, attempt to get the real hostname
                      String realHost = resolver.getByName(senderIP).getHostName();

                      // If the host equals the ip, we couldn't resolve
                      if(realHost.endsWith(senderIP)) {

                          if(hostNameIsIP) {
                              // Probably a forgery, but we can't be sure
                              return FORGERY_UNKNOWN;
                          }
                          else
                          {
                              // Sender identified a host that does not match the DNS, forgery
                              return FORGERY_CONFIRMED;
                          }
                      }
                      else
                      {
                          // The host was returned, we need to get the "root" of this hostname
                          String rootRealHost = DNSUtils.getRootDomain(realHost);

                          // Now get the root of the reported host
                          String rootReportedHost = DNSUtils.getRootDomain(senderHost);

                          // If they match, we are ok
                          if(rootRealHost.equalsIgnoreCase(rootReportedHost)) {
                              return FORGERY_REJECTED;
                          }
                          else
                          {
                              // try the domain of the sender
                              String rootSenderHost = DNSUtils.getRootDomain(getDomainFromAddress(senderAddress));

                              if(rootRealHost.equalsIgnoreCase(rootSenderHost)) {
                                  return FORGERY_REJECTED; // ok
                              }
                              else
                              {
                                  return FORGERY_CONFIRMED; // forgery
                              }
                          }
                      }
                  }
                  catch (UnknownHostException e)
                  {
                      // No host.. must be forged
                      return FORGERY_CONFIRMED;
                  }                       
                }
                else {
                    // Couldn't parse
                    return FORGERY_UNKNOWN;
                }
            }
          catch (JasenParseException e) {
                // Can't parse the header
              return FORGERY_UNKNOWN;
            }
      }
      else
      {
          // no headers, can't be sure
          return FORGERY_UNKNOWN;
      }
  }

  /**
   * Returns the domain component of an email address
   * @param emailAddress
   * @return The domain ofthe address (everything after the @)
   */
  public static String getDomainFromAddress(String emailAddress) {

    if (emailAddress != null) {
      return emailAddress.substring(emailAddress.indexOf("@") + 1, emailAddress.length());
    }
    else {
      return null;
    }

  }

    public static boolean isAttachment(String disposition) {
   
      if(disposition != null) {
        return (Arrays.binarySearch(ATTACHMENT_DISPOSITIONS, disposition) > -1);
      }
      else
      {
        return false;
      }
    }

    public static void getParts(List parts, Part p) throws MessagingException, IOException {
      getParts(parts, p, null, null);
    }

    /**
     * Gets the parts from the MimeMessage<BR>
     * <BR>
     * Use null disposition to ignore<BR>
     * @param parts A new list to hold the parts
     * @param p The current Part
     * @param contentType If specified, returns only parts matching the given content type.  Use null to ignore
     * @param disposition If specified, returns only parts matching the given disposition.  Use null to ignore
     */
    public static void getParts(List parts, Part p, String contentType, String disposition) throws MessagingException, IOException {
   
      Object content = getPartContent (p);
   
      String currentDisposition = p.getDisposition();
      String currentContentType = p.getContentType();
   
      // now we need to check if the part was a multipart...
      if (content instanceof Multipart) {
        Multipart mp = (Multipart) content;
   
        // Now we need to delve into the parts
   
        // This call to getCount seems to throw ParseExceptions occasionally
        //  so we will catch that exception here so we don't lose the rest of the email...
        int count = 0;
   
        try
            {
                count = mp.getCount();
            }
            catch (Exception e)
            {
                // We weren't able to determine the number of parts in the multipart.
                // This shouldn't ever really happen, and is usually caused when
                // the MIME message is incorrectly formatted.
                // For now we are just going to record, but ignore the error
                ErrorHandlerBroker.getInstance().getErrorHandler().handleException(e);
            }
   
        for (int i = 0; i < count; i++) {
          getParts(parts, mp.getBodyPart(i), contentType, disposition);
        }
   
      }
      else
      {
        // check to the contentType
   
        if (contentType == null) {
   
          // we don't need to check
          if (disposition == null) {
            // no need to check
            // add the part
            parts.add(p);
            //index++;
          }
          else if (currentDisposition != null && currentDisposition.startsWith(disposition)) {
            // add the part
            parts.add(p);
          }
        }
        else if (p.isMimeType(contentType)) {
          // Check the disposition
   
          if (disposition == null) {
            // no need to check
            // add the part
            parts.add(p);
          }
          else if (currentDisposition != null && currentDisposition.startsWith(disposition)) {
            // add the part
            parts.add(p);
          }
        }
      }
    }

    /**
     * Gets the parts which match any of the dispositions
     * @param parts A list of parts
     * @param contentType The content type required
     * @param dispositions The content dispositions required
     * @return All the parts in the message which match the given content type and disposition
     */
    public static List getMultiplePartsFromList(List parts, String contentType, String[] dispositions) throws IOException, MessagingException {
   
      List matchedParts = null;
   
      for (int i = 0; i < dispositions.length; i++) {
        if (matchedParts == null) {
          matchedParts = getPartsFromList(parts, contentType, dispositions[i]);
        }
        else {
          matchedParts.addAll(getPartsFromList(parts, contentType, dispositions[i]));
        }
      }
   
      return matchedParts;
   
    }

    /**
     * Gets a list of all parts which are themselves MimeMessages
     * @param parts
     * @return
     */
    public static List getSubMessagePartsFromList(List parts) throws IOException, MessagingException {
      List subMessages = null;
   
      if(parts != null) {
   
        Iterator m = parts.iterator();
        Part part = null;
        Object content = null;
   
        while(m.hasNext()) {
          part = (Part) m.next();
          content = getPartContent(part);
   
   
          if(content instanceof MimeMessage) {
            if(subMessages == null) {
              subMessages = new LinkedList();
            }
   
            subMessages.add(content);
          }
        }
      }
   
      return subMessages;
    }

    /**
     * Gets all parts which are unknown.  An unknown part is one which has no disposition, and is not text or html
     * @param parts
     * @return
     */
    public static List getUnknownPartsFromList(List parts) throws IOException, MessagingException {
   
      List unknownParts = null;
   
      if(parts != null) {
   
        Iterator m = parts.iterator();
        Part part = null;
   
        while(m.hasNext()) {
          part = (Part) m.next();
   
          if(!(getPartContent(partinstanceof MimeMessage)) {
            if(part.getDisposition() == null && !part.isMimeType(MimeType.TEXT_PLAIN) && !part.isMimeType(MimeType.TEXT_HTML)) {
              // its unknown
              if(unknownParts == null) {
                unknownParts = new LinkedList();
              }
   
              unknownParts.add(part);
            }
          }
        }
      }
   
      return unknownParts;
   
    }

    /**
     * Gets all parts which can be considered an attachment.  This includes sub messages and unknown parts
     * @param parts
     * @return A List of Part objects
     */
    public static List getAllAttachmentParts(List parts) throws IOException, MessagingException {
   
      List attachments = null;
   
      if(parts != null) {
   
        Iterator m = parts.iterator();
        Part part = null;
   
        while(m.hasNext()) {
          part = (Part) m.next();
   
          if(getPartContent(part) instanceof MimeMessage) {
   
            // Its a sub message
            if(attachments == null) {
              attachments = new LinkedList();
            }
   
            attachments.add(part);
          }
          else if(part.getDisposition() == null && !part.isMimeType(MimeType.TEXT_PLAIN) && !part.isMimeType(MimeType.TEXT_HTML)) {
            // its unknown
            if(attachments == null) {
              attachments = new LinkedList();
            }
   
            attachments.add(part);
          }
          else if(part.getDisposition() != null && (
              part.getDisposition().startsWith(MimeMessage.INLINE) ||
              part.getDisposition().startsWith(MimeMessage.ATTACHMENT))) {
   
            if(attachments == null) {
              attachments = new LinkedList();
            }
   
            // It's an attachment
            attachments.add(part);
          }
        }
      }
   
      return attachments;
    }

    public static Part getFirstPartFromList(List parts, String contentType, String disposition) throws MessagingException, IOException {
        List list = getPartsFromList(parts, contentType, disposition);
        if(list != null && list.size() > 0) {
            return (Part)list.iterator().next();
        }
        else
        {
            return null;
        }
    }

    public static List getPartsFromList(List parts, String contentType, String disposition) throws MessagingException, IOException {
   
      LinkedList matchedParts = new LinkedList();
      Part p = null;
      Object content = null;
      String currentDisposition = null;
      String currentContentType = null;
   
      for (int i = 0; i < parts.size(); i++) {
   
        p = (Part) parts.get(i);
   
        content = getPartContent (p);
        //content = p.getContent();
        currentDisposition = p.getDisposition();
        currentContentType = p.getContentType();
   
        if (contentType == null) {
   
          // we don't need to check
          if (disposition == null) {
            // no need to check
            matchedParts.add(p);
          }
          else if (currentDisposition != null && currentDisposition.startsWith(disposition)) {
            matchedParts.add(p);
          }
        }
        else if (p.isMimeType(contentType)) {
          // Check the disposition
   
          if (disposition == null) {
            // no need to check
            matchedParts.add(p);
          }
          else if (currentDisposition != null && currentDisposition.startsWith(disposition)) {
            matchedParts.add(p);
          }
        }
      }
   
      return matchedParts;
    }

    public static Object getPartContent(Part part) throws IOException, MessagingException {
        Object content = null;
   
        if(part != null) {
   
            try
            {
                String strContentType = part.getContentType();
              ContentType contentType = null;
              String specifiedCharset = null;
              try
                {
                  contentType = new ContentType(strContentType);
                  specifiedCharset = contentType.getParameter("charset");
                }
                catch (ParseException ignore)
                {
                    // Ignore
                }
   
          if(specifiedCharset != null) {
   
            specifiedCharset = specifiedCharset.toLowerCase();
   
            if (specifiedCharset.indexOf("utf-7") > -1) {
              InputStream in = part.getInputStream();
              ByteArrayOutputStream out = new ByteArrayOutputStream();
       
              IOUtils.pipe(in, out, 1024);
   
              ByteToCharUTF7Converter btc = new ByteToCharUTF7Converter();
   
              byte[] bytes = out.toByteArray();
   
              char[] chars = new char[bytes.length];
   
              btc.convert(bytes, 0, bytes.length,  chars, 0, chars.length);
   
              content = new String(chars);
            }
            else
            {
              content = part.getContent();
            }
          }
          else
          {
            content = part.getContent();
          }
            }
            catch (IOException e)
            {
                ErrorHandlerBroker.getInstance().getErrorHandler().handleException(e);
            }
        }
   
      return content;
    }
}
TOP

Related Classes of org.jasen.util.MimeUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.