/**
  * Verify the email is signed by the given certificate.
  *
  * @param signedData
  * @param mailMsg
  * @return
  * @throws CMSException
  * @throws OperatorCreationException
  * @throws CertificateException
  */
 @SuppressWarnings({"rawtypes"})
 protected boolean isValid(CMSSignedData signedData, MailMessage mailMsg)
     throws OperatorCreationException, CMSException, CertificateException {
   boolean verify = false;
   SignerInformationStore signerStore = signedData.getSignerInfos();
   Iterator<SignerInformation> it = signerStore.getSigners().iterator();
   while (it.hasNext()) {
     SignerInformation signer = it.next();
     org.bouncycastle.util.Store store = signedData.getCertificates();
     @SuppressWarnings("unchecked")
     Collection certCollection = store.getMatches(signer.getSID());
     Iterator certIt = certCollection.iterator();
     X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
     X509Certificate certificate =
         new JcaX509CertificateConverter().setProvider(PROVIDER_NAME).getCertificate(certHolder);
     verify =
         signer.verify(
             new JcaSimpleSignerInfoVerifierBuilder()
                 .setProvider(PROVIDER_NAME)
                 .build(certificate));
     mailMsg.setHasSignature(true);
     mailMsg.setSignaturePassed(verify);
     mailMsg.setNameOfPrincipal(certificate.getSubjectDN().getName());
   }
   return verify;
 }
  /**
   * Get message header for MailMessage.
   *
   * @param msg
   * @param mailMsg
   * @throws MessagingException
   */
  protected void processHeader(Message msg, MailMessage mailMsg) throws MessagingException {
    String xAutoResponseSuppressHeaderName = "X-Auto-Response-Suppress";
    String xAutoReplyHeaderName = "X-Autoreply";
    String xAutoRespondHeaderName = "X-Autorespond";
    String xAutoSubmittedHeaderName = "auto-submitted";

    String xAutoResponseSuppressVal =
        Arrays.toString(msg.getHeader(xAutoResponseSuppressHeaderName));
    String xAutoReplyVal = Arrays.toString(msg.getHeader(xAutoReplyHeaderName));
    String xAutoRespondVal = Arrays.toString(msg.getHeader(xAutoRespondHeaderName));
    String xAutoSubmittedVal = Arrays.toString(msg.getHeader(xAutoSubmittedHeaderName));
    String contentType = msg.getContentType();

    // If any of those are present in an email, then that email is an auto-reply.
    String[] autoReplyArray = {
      xAutoResponseSuppressVal, xAutoReplyVal, xAutoRespondVal, xAutoSubmittedVal
    };
    mailMsg.setAutoReply(autoReplyArray);
    mailMsg.setContentType(contentType);
  }
  /**
   * Set some email's basic information to MailMessage .
   *
   * @param mime
   * @param mailMsg
   * @throws MessagingException
   * @throws ParseException
   */
  private void setMailBasicInfoForMailMsg(MimeMessage mime, MailMessage mailMsg)
      throws MessagingException, ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
    String msgId = mime.getMessageID();
    Address[] from = mime.getFrom();
    Address[] to = mime.getRecipients(RecipientType.TO);
    Address[] cc = mime.getRecipients(RecipientType.CC);
    Address[] bcc = mime.getRecipients(RecipientType.BCC);
    String subject = mime.getSubject();
    Date sendDate = mime.getSentDate();
    String receivedUTCDate = sdf.format(this.resolveReceivedDate(mime));

    mailMsg.setMsgId(msgId);
    mailMsg.setFrom(this.convertToMailAddress(from));
    mailMsg.setTo(this.convertToMailAddress(to));
    mailMsg.setCc(this.convertToMailAddress(cc));
    mailMsg.setBcc(this.convertToMailAddress(bcc));
    mailMsg.setSubject(subject);
    if (sendDate != null) {
      mailMsg.setSendDate(sdf.format(sendDate));
    }
    mailMsg.setReceivedDate(receivedUTCDate);
  }
  /**
   * Process the MimeMessage for signed mail with/without attachment.
   *
   * @param mime
   * @param mailMsg
   * @return
   * @throws MessagingException
   * @throws IOException
   * @throws ParseException
   */
  private String processSignedMail(MimeMessage mime, MailMessage mailMsg)
      throws IOException, MessagingException, ParseException {
    this.setMailBasicInfoForMailMsg(mime, mailMsg);

    String txtBody = null;
    String htmlBody = null;
    // Get the content of the messsage, it's an Multipart object like a package including all the
    // email text and attachment.
    Multipart multi1 = (Multipart) mime.getContent();
    // process each part in order.
    for (int i = 0, n = multi1.getCount(); i < n; i++) {
      // unpack, get each part of Multipart, part 0 may email text and part 1 may attachment. Or it
      // is another embedded Multipart.
      Part part2 = multi1.getBodyPart(i);
      // determine Part is email text or Multipart.
      if (part2.getContent() instanceof Multipart) {
        Multipart multi2 = (Multipart) part2.getContent();
        // First, verify the quantity and size of attachments.
        boolean isValidMailMsg = this.isValidMailMsg(multi2);
        if (isValidMailMsg) {
          // process the content in multi2.
          for (int j = 0; j < multi2.getCount(); j++) {
            Part part3 = multi2.getBodyPart(j);
            if (part3.isMimeType("text/plain")
                && !Part.ATTACHMENT.equalsIgnoreCase(part3.getDisposition())) {
              txtBody = part3.getContent().toString();
            } else if (part3.isMimeType("text/html")
                && !Part.ATTACHMENT.equalsIgnoreCase(part3.getDisposition())) {
              htmlBody = part3.getContent().toString();
            } else if (part3.isMimeType("multipart/alternative")) {
              // generally if the content type multipart/alternative, it is email text.
              if (part3.getContent() instanceof Multipart) {
                Multipart multi3 = (Multipart) part3.getContent();
                for (int k = 0; k < multi3.getCount(); k++) {
                  Part part4 = multi3.getBodyPart(k);
                  if (part4.isMimeType("text/plain")
                      && !Part.ATTACHMENT.equalsIgnoreCase(part4.getDisposition())) {
                    txtBody = part4.getContent().toString();
                  } else if (part4.isMimeType("text/html")
                      && !Part.ATTACHMENT.equalsIgnoreCase(part4.getDisposition())) {
                    htmlBody = part4.getContent().toString();
                  }
                }
              }
            } else if (part3.isMimeType("multipart/related")) {
              if (part3.getContent() instanceof Multipart) {
                Multipart multi3 = (Multipart) part3.getContent();
                for (int m = 0; m < multi3.getCount(); m++) {
                  Part part4 = multi3.getBodyPart(m);
                  if (part4.isMimeType("multipart/alternative")) {
                    if (part4.getContent() instanceof Multipart) {
                      Multipart multi4 = (Multipart) part4.getContent();
                      for (int p = 0; p < multi4.getCount(); p++) {
                        Part part5 = multi4.getBodyPart(p);
                        if (part5.isMimeType("text/plain")
                            && !Part.ATTACHMENT.equalsIgnoreCase(part5.getDisposition())) {
                          txtBody = part5.getContent().toString();
                        } else if (part5.isMimeType("text/html")
                            && !Part.ATTACHMENT.equalsIgnoreCase(part5.getDisposition())) {
                          htmlBody = part5.getContent().toString();
                        }
                      }
                    }
                  } else {
                    // This is an embedded picture, set it as an attachment.
                    mailMsg.setHasAttachments(true);
                  }
                }
              }
            } else {
              String disposition = part3.getDisposition();
              if (disposition != null && Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
                mailMsg.setHasAttachments(true);
              }
            }
          }
        }
      } else {
        // This is a certificate file, set it as an attachment
        String disposition = part2.getDisposition();
        if (disposition != null && Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
          mailMsg.setHasAttachments(true);
        }
      }
    }
    if (!isNull(txtBody)) {
      mailMsg.setBody(txtBody);
    } else {
      mailMsg.setBody(htmlBody);
    }
    return JSONObject.fromObject(mailMsg).toString();
  }