Java tutorial
/* * Copyright (c) 2010-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.application.djigzo.james.mailets; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.Collection; import java.util.List; import javax.mail.MessagingException; import mitm.application.djigzo.james.DjigzoMailAttributes; import mitm.application.djigzo.james.DjigzoMailAttributesImpl; import mitm.application.djigzo.service.SystemServices; import mitm.common.dlp.MimeMessageTextExtractor; import mitm.common.dlp.PolicyChecker; import mitm.common.dlp.PolicyCheckerContext; import mitm.common.dlp.PolicyPattern; import mitm.common.dlp.PolicyViolation; import mitm.common.dlp.PolicyViolationException; import mitm.common.dlp.TextNormalizer; import mitm.common.dlp.impl.PolicyCheckerContextImpl; import mitm.common.extractor.ExtractedPart; import mitm.common.extractor.TextExtractor; import mitm.common.extractor.impl.TextExtractorUtils; import mitm.common.util.SizeUtils; import mitm.common.util.StringIterator; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.text.StrBuilder; import org.apache.mailet.Mail; /** * Abstract base class for RegExpPolicyChecker mailets. * * @author Martijn Brinkers * */ public abstract class AbstractRegExpPolicyChecker extends AbstractDjigzoMailet { /* * As a sanity measure, the length of the error message stored in Mail attributes * will be limited to this length. */ private final static int MAX_POLICY_VIOLATION_ERROR_MESSAGE_LENGTH = 1024; /* * The mailet initialization parameters used by this mailet. */ private enum Parameter { WARN_PROCESSOR("warnProcessor"), MUST_ENCRYPT_PROCESSOR("mustEncryptProcessor"), QUARANTINE_PROCESSOR( "quarantineProcessor"), BLOCK_PROCESSOR("blockProcessor"), ERROR_PROCESSOR( "errorProcessor"), MAX_STRING_LENGTH( "maxStringLength"), MAX_INIT_BUFFER_SIZE("maxInitBufferSize"); private String name; private Parameter(String name) { this.name = name; } @Override public String toString() { return name; } }; /* * is used to normalize the extracted text (lowercase, remove words on skip list etc.) */ private TextNormalizer textNormalizer; /* * The policy checker pipeline */ private PolicyChecker policyChecker; /* * Used to extract the text from the email message. */ private MimeMessageTextExtractor textExtractor; /* * The next processor when the policy was violated with WARN action. */ private String warnProcessor; /* * The next processor when the policy was violated with MUST_ENCRYPT action. */ private String mustEncryptProcessor; /* * The next processor when the policy was violated with QUARANTINE action. */ private String quarantineProcessor; /* * The next processor when the policy was violated with BLOCK action. */ private String blockProcessor; /* * The next processor when an error occurred when checking the policy */ private String errorProcessor; /* * The max length of the a string returned by the string iterator */ private int maxStringLength = SizeUtils.KB * 512; /* * The max initial size of the buffer used by the string iterator */ private int maxInitBufferSize = SizeUtils.KB * 64; private String getMandatoryInitParameter(String name) { String value = getInitParameter(name); if (StringUtils.isBlank(value)) { throw new IllegalArgumentException("Init parameter " + name + " is missing."); } return value; } @Override public void initMailet() { getLogger().info("Initializing mailet: " + getMailetName()); warnProcessor = getMandatoryInitParameter(Parameter.WARN_PROCESSOR.name); mustEncryptProcessor = getMandatoryInitParameter(Parameter.MUST_ENCRYPT_PROCESSOR.name); quarantineProcessor = getMandatoryInitParameter(Parameter.QUARANTINE_PROCESSOR.name); blockProcessor = getMandatoryInitParameter(Parameter.BLOCK_PROCESSOR.name); errorProcessor = getMandatoryInitParameter(Parameter.ERROR_PROCESSOR.name); maxStringLength = getIntegerInitParameter(Parameter.MAX_STRING_LENGTH.name, maxStringLength); maxInitBufferSize = getIntegerInitParameter(Parameter.MAX_INIT_BUFFER_SIZE.name, maxInitBufferSize); textNormalizer = SystemServices.getTextNormalizer(); policyChecker = SystemServices.getPolicyCheckerPipeline(); textExtractor = SystemServices.getMimeMessageTextExtractor(); StrBuilder sb = new StrBuilder(); sb.append("warnProcessor: ").append(warnProcessor).append("; mustEncryptProcessor: ") .append(mustEncryptProcessor).append("; quarantineProcessor: ").append(quarantineProcessor) .append("; blockProcessor: ").append(blockProcessor).append("; errorProcessor: ") .append(errorProcessor).append("; maxStringLength: ").append(maxStringLength) .append("; maxInitBufferSize: ").append(maxInitBufferSize); getLogger().info(sb.toString()); } /** * Should return the PolicyPattern's which are used to check the message content with */ protected abstract Collection<PolicyPattern> getPolicyPatterns(Mail mail) throws MessagingException, IOException; private void logException(Exception e) { if (getLogger().isDebugEnabled()) { getLogger().warn("Error extracting text from email.", e); } else { getLogger().warn("Error extracting text from email. Message: " + ExceptionUtils.getRootCauseMessage(e)); } } private void handleError(Mail mail, Exception e) { logException(e); /* * Place the error message in the Mail attributes so the exact details * of the error can be retrieved by other Mailets */ DjigzoMailAttributes mailAttributes = new DjigzoMailAttributesImpl(mail); /* * Get the root cause and add make sure the error message has some sane length */ String errorMessage = StringUtils.abbreviate(ExceptionUtils.getRootCauseMessage(e), MAX_POLICY_VIOLATION_ERROR_MESSAGE_LENGTH); mailAttributes.setPolicyViolationErrorMessage(errorMessage); mail.setState(errorProcessor); } private void handlePolicyViolation(Mail mail, PolicyViolationException e) { PolicyViolation mainViolation = null; Collection<PolicyViolation> violations = e.getViolations(); if (violations != null) { /* * We will place the violations in the Mail attributes so the exact details * of the violation can be retrieved by other Mailets */ DjigzoMailAttributes mailAttributes = new DjigzoMailAttributesImpl(mail); mailAttributes.setPolicyViolations(violations); /* * Multiple policies might be violated. We need to check which policy is the * most important policy. */ for (PolicyViolation violation : violations) { if (violation == null) { continue; } if (mainViolation == null || violation.getPriority().isHigherPriorityThen(mainViolation.getPriority())) { mainViolation = violation; } } } if (mainViolation == null) { throw new IllegalStateException("No main violation found."); } String nextProcessor; switch (mainViolation.getPriority()) { case WARN: nextProcessor = warnProcessor; break; case MUST_ENCRYPT: nextProcessor = mustEncryptProcessor; break; case QUARANTINE: nextProcessor = quarantineProcessor; break; case BLOCK: nextProcessor = blockProcessor; break; default: throw new IllegalArgumentException("Unknown PolicyViolationAction: " + mainViolation.getPriority()); } if (getLogger().isDebugEnabled()) { getLogger().warn("Policy was violated. Rule: " + mainViolation); } else { getLogger().warn("Policy was violated. Rule: " + mainViolation.getRule() + ", Priority: " + mainViolation.getPriority()); } mail.setState(nextProcessor); } @Override public void serviceMail(Mail mail) { try { List<ExtractedPart> extractedParts = null; try { PolicyCheckerContext context = new PolicyCheckerContextImpl(); context.setPatterns(getPolicyPatterns(mail)); extractedParts = textExtractor.extractText(mail.getMessage()); policyChecker.init(context); if (extractedParts != null) { for (ExtractedPart part : extractedParts) { StringIterator contentIterator = new StringIterator(part.getContent(), maxStringLength, TextExtractor.ENCODING, maxInitBufferSize); /* * We need to track whether the content of a part is too big to fit * in one call. If so, partial will be true and the data top be * analyzed will contain some overlap between different partial updates * to make it possible to analyze the boundaries. */ boolean partial = false; String extractedText; while ((extractedText = contentIterator.getNext()) != null) { StringWriter normalized = new StringWriter(extractedText.length()); /* * The extracted text should be normalized before checking the policy */ textNormalizer.normalize(new StringReader(extractedText), normalized); context.setContent(normalized.toString()); context.setPartial(partial); policyChecker.update(context); partial = true; } } try { policyChecker.finish(context); } catch (PolicyViolationException e) { handlePolicyViolation(mail, e); } } } finally { TextExtractorUtils.closeQuitely(extractedParts); } } catch (Exception e) { handleError(mail, e); } } }