Methods Summary |
---|
private final java.lang.String | arrayToString(java.lang.Object[] array)Utility method for obtaining a string representation of an array of Objects.
if (array == null) {
return "null";
}
StringBuffer sb = new StringBuffer(1024);
sb.append("[");
for (int i = 0; i < array.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(array[i]);
}
sb.append("]");
return sb.toString();
|
private void | checkInitParameters(java.lang.String[] allowedArray)Checks if there are unallowed init parameters specified in the configuration file
against the String[] allowedInitParameters.
// if null then no check is requested
if (allowedArray == null) {
return;
}
Collection allowed = new HashSet();
Collection bad = new ArrayList();
for (int i = 0; i < allowedArray.length; i++) {
allowed.add(allowedArray[i]);
}
Iterator iterator = getInitParameterNames();
while (iterator.hasNext()) {
String parameter = (String) iterator.next();
if (!allowed.contains(parameter)) {
bad.add(parameter);
}
}
if (bad.size() > 0) {
throw new MessagingException("Unexpected init parameters found: "
+ arrayToString(bad.toArray()));
}
|
protected final boolean | fromAddressSameAsReverse(org.apache.mailet.Mail mail)Utility method that checks if there is at least one address in the "From:" header
same as the reverse-path.
MailAddress reversePath = mail.getSender();
if (reversePath == null) {
return false;
}
try {
InternetAddress[] fromArray = (InternetAddress[]) mail.getMessage().getFrom();
if (fromArray != null) {
for (int i = 0; i < fromArray.length; i++) {
MailAddress mailAddress = null;
try {
mailAddress = new MailAddress(fromArray[i]);
} catch (ParseException pe) {
log("Unable to parse a \"FROM\" header address: " + fromArray[i].toString() + "; ignoring.");
continue;
}
if (mailAddress.equals(reversePath)) {
return true;
}
}
}
} catch (MessagingException me) {
log("Unable to parse the \"FROM\" header; ignoring.");
}
return false;
|
protected abstract java.lang.String[] | getAllowedInitParameters()Gets the expected init parameters.
|
public java.lang.String | getExplanationText()Getter for property explanationText.
Text to be used in the SignatureExplanation.txt file.
return this.explanationText;
|
protected org.apache.james.security.KeyHolder | getKeyHolder()Getter for property keyHolder.
It is protected instead of public for security reasons.
return this.keyHolder;
|
protected final java.lang.String | getMessageHeaders(javax.mail.internet.MimeMessage message)Utility method for obtaining a string representation of the Message's headers
Enumeration heads = message.getAllHeaderLines();
StringBuffer headBuffer = new StringBuffer(1024);
while(heads.hasMoreElements()) {
headBuffer.append(heads.nextElement().toString()).append("\r\n");
}
return headBuffer.toString();
|
protected final java.lang.String | getReplacedExplanationText(java.lang.String explanationText, java.lang.String signerName, java.lang.String signerAddress, java.lang.String reversePath, java.lang.String headers)Prepares the explanation text making substitutions in the explanationText template string.
Utility method that searches for all occurrences of some pattern strings
and substitute them with the appropriate params.
String replacedExplanationText = explanationText;
replacedExplanationText = getReplacedString(replacedExplanationText, SIGNER_NAME_PATTERN, signerName);
replacedExplanationText = getReplacedString(replacedExplanationText, SIGNER_ADDRESS_PATTERN, signerAddress);
replacedExplanationText = getReplacedString(replacedExplanationText, REVERSE_PATH_PATTERN, reversePath);
replacedExplanationText = getReplacedString(replacedExplanationText, HEADERS_PATTERN, headers);
return replacedExplanationText;
|
private java.lang.String | getReplacedString(java.lang.String template, java.lang.String pattern, java.lang.String actual)Searches the template String for all occurrences of the pattern string
and creates a new String substituting them with the actual String.
if (actual != null) {
StringBuffer sb = new StringBuffer(template.length());
int fromIndex = 0;
int index;
while ((index = template.indexOf(pattern, fromIndex)) >= 0) {
sb.append(template.substring(fromIndex, index));
sb.append(actual);
fromIndex = index + pattern.length();
}
if (fromIndex < template.length()){
sb.append(template.substring(fromIndex));
}
return sb.toString();
} else {
return new String(template);
}
|
public java.lang.String | getSignerName()Getter for property signerName.
return this.signerName;
|
protected abstract javax.mail.internet.MimeBodyPart | getWrapperBodyPart(org.apache.mailet.Mail mail)Creates the {@link javax.mail.internet.MimeBodyPart} that will be signed.
For example, may attach a text file explaining the meaning of the signature,
or an XML file containing information that can be checked by other MTAs.
|
public void | init()Mailet initialization routine.
// check that all init parameters have been declared in allowedInitParameters
checkInitParameters(getAllowedInitParameters());
try {
initDebug();
if (isDebug()) {
log("Initializing");
}
initKeyHolder();
initSignerName();
initPostmasterSigns();
initRebuildFrom();
initExplanationText();
} catch (MessagingException me) {
throw me;
} catch (Exception e) {
log("Exception thrown", e);
throw new MessagingException("Exception thrown", e);
} finally {
if (isDebug()) {
StringBuffer logBuffer =
new StringBuffer(1024)
.append("Other parameters:")
.append(", signerName=").append(getSignerName())
.append(", postmasterSigns=").append(postmasterSigns)
.append(", rebuildFrom=").append(rebuildFrom)
.append(" ");
log(logBuffer.toString());
}
}
|
protected void | initDebug()Initializer for property debug.
/* ******************************************************************** */
/* ****************** Begin of setters and getters ******************** */
/* ******************************************************************** */
setDebug((getInitParameter("debug") == null) ? false : new Boolean(getInitParameter("debug")).booleanValue());
|
protected void | initExplanationText()Initializer for property explanationText.
setExplanationText(getInitParameter("explanationText"));
if (isDebug()) {
log("Explanation text:\r\n" + getExplanationText());
}
|
protected void | initKeyHolder()Initializer for property keyHolder.
String keyStoreFileName = getInitParameter("keyStoreFileName");
if (keyStoreFileName == null) {
throw new MessagingException("<keyStoreFileName> parameter missing.");
}
String keyStorePassword = getInitParameter("keyStorePassword");
if (keyStorePassword == null) {
throw new MessagingException("<keyStorePassword> parameter missing.");
}
String keyAliasPassword = getInitParameter("keyAliasPassword");
if (keyAliasPassword == null) {
keyAliasPassword = keyStorePassword;
if (isDebug()) {
log("<keyAliasPassword> parameter not specified: will default to the <keyStorePassword> parameter.");
}
}
String keyStoreType = getInitParameter("keyStoreType");
if (keyStoreType == null) {
if (isDebug()) {
log("<type> parameter not specified: will default to \"" + KeyHolder.getDefaultType() + "\".");
}
}
String keyAlias = getInitParameter("keyAlias");
if (keyAlias == null) {
if (isDebug()) {
log("<keyAlias> parameter not specified: will look for the first one in the keystore.");
}
}
if (isDebug()) {
StringBuffer logBuffer =
new StringBuffer(1024)
.append("KeyStore related parameters:")
.append(" keyStoreFileName=").append(keyStoreFileName)
.append(", keyStoreType=").append(keyStoreType)
.append(", keyAlias=").append(keyAlias)
.append(" ");
log(logBuffer.toString());
}
// Certificate preparation
setKeyHolder(new KeyHolder(keyStoreFileName, keyStorePassword, keyAlias, keyAliasPassword, keyStoreType));
if (isDebug()) {
log("Subject Distinguished Name: " + getKeyHolder().getSignerDistinguishedName());
}
if (getKeyHolder().getSignerAddress() == null) {
throw new MessagingException("Signer address missing in the certificate.");
}
|
protected void | initPostmasterSigns()Initializer for property postmasterSigns.
setPostmasterSigns((getInitParameter("postmasterSigns") == null) ? false : new Boolean(getInitParameter("postmasterSigns")).booleanValue());
|
protected void | initRebuildFrom()Initializer for property rebuildFrom.
setRebuildFrom((getInitParameter("rebuildFrom") == null) ? false : new Boolean(getInitParameter("rebuildFrom")).booleanValue());
if (isDebug()) {
if (isRebuildFrom()) {
log("Will modify the \"From:\" header.");
} else {
log("Will leave the \"From:\" header unchanged.");
}
}
|
protected void | initSignerName()Initializer for property signerName.
setSignerName(getInitParameter("signerName"));
if (getSignerName() == null) {
if (getKeyHolder() == null) {
throw new RuntimeException("initKeyHolder() must be invoked before initSignerName()");
}
setSignerName(getKeyHolder().getSignerCN());
if (isDebug()) {
log("<signerName> parameter not specified: will use the certificate signer \"CN=\" attribute.");
}
}
|
public boolean | isDebug()Getter for property debug.
return this.debug;
|
protected boolean | isOkToSign(org.apache.mailet.Mail mail)Checks if the mail can be signed.
Rules:
- The reverse-path != null (it is not a bounce).
- The sender user must have been SMTP authenticated.
- Either:
- The reverse-path is the postmaster address and {@link #isPostmasterSigns} returns true
- or the reverse-path == the authenticated user
and there is at least one "From:" address == reverse-path.
.
- The message has not already been signed (mimeType != multipart/signed
and != application/pkcs7-mime).
MailAddress reversePath = mail.getSender();
// Is it a bounce?
if (reversePath == null) {
return false;
}
String authUser = (String) mail.getAttribute("org.apache.james.SMTPAuthUser");
// was the sender user SMTP authorized?
if (authUser == null) {
return false;
}
// The sender is the postmaster?
if (getMailetContext().getPostmaster().equals(reversePath)) {
// should not sign postmaster sent messages?
if (!isPostmasterSigns()) {
return false;
}
} else {
// is the reverse-path user different from the SMTP authorized user?
if (!reversePath.getUser().equals(authUser)) {
return false;
}
// is there no "From:" address same as the reverse-path?
if (!fromAddressSameAsReverse(mail)) {
return false;
}
}
// if already signed return false
MimeMessage mimeMessage = mail.getMessage();
if (mimeMessage.isMimeType("multipart/signed")
|| mimeMessage.isMimeType("application/pkcs7-mime")) {
return false;
}
return true;
|
public boolean | isPostmasterSigns()Getter for property postmasterSigns.
If true will sign messages signed by the postmaster.
return this.postmasterSigns;
|
public boolean | isRebuildFrom()Getter for property rebuildFrom.
If true will modify the "From:" header.
The modification is as follows:
assuming that the signer mail address in the signer certificate is trusted-server@xxx.com>
and that From: "John Smith"
we will get From: "John Smith" " <trusted-server@xxx.com>.
If the "ReplyTo:" header is missing or empty it will be set to the original "From:" header.
Such modification is necessary to achieve a correct behaviour
with some mail clients (e.g. Microsoft Outlook Express).
return this.rebuildFrom;
|
public void | service(org.apache.mailet.Mail mail)Service does the hard work, and signs
try {
if (!isOkToSign(mail)) {
return;
}
MimeBodyPart wrapperBodyPart = getWrapperBodyPart(mail);
MimeMessage originalMessage = mail.getMessage();
// do it
MimeMultipart signedMimeMultipart;
if (wrapperBodyPart != null) {
signedMimeMultipart = getKeyHolder().generate(wrapperBodyPart);
} else {
signedMimeMultipart = getKeyHolder().generate(originalMessage);
}
MimeMessage newMessage = new MimeMessage(Session.getDefaultInstance(System.getProperties(),
null));
Enumeration headerEnum = originalMessage.getAllHeaderLines();
while (headerEnum.hasMoreElements()) {
newMessage.addHeaderLine((String) headerEnum.nextElement());
}
newMessage.setSender(new InternetAddress(getKeyHolder().getSignerAddress(), getSignerName()));
if (isRebuildFrom()) {
// builds a new "mixed" "From:" header
InternetAddress modifiedFromIA = new InternetAddress(getKeyHolder().getSignerAddress(), mail.getSender().toString());
newMessage.setFrom(modifiedFromIA);
// if the original "ReplyTo:" header is missing sets it to the original "From:" header
newMessage.setReplyTo(originalMessage.getReplyTo());
}
newMessage.setContent(signedMimeMultipart, signedMimeMultipart.getContentType());
String messageId = originalMessage.getMessageID();
newMessage.saveChanges();
if (messageId != null) {
newMessage.setHeader(RFC2822Headers.MESSAGE_ID, messageId);
}
mail.setMessage(newMessage);
// marks this mail as server-signed
mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNING_MAILET, this.getClass().getName());
// it is valid for us by definition (signed here by us)
mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNATURE_VALIDITY, "valid");
// saves the trusted server signer address
// warning: should be same as the mail address in the certificate, but it is not guaranteed
mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNER_ADDRESS, getKeyHolder().getSignerAddress());
if (isDebug()) {
log("Message signed, reverse-path: " + mail.getSender() + ", Id: " + messageId);
}
} catch (MessagingException me) {
log("MessagingException found - could not sign!", me);
throw me;
} catch (Exception e) {
log("Exception found", e);
throw new MessagingException("Exception thrown - could not sign!", e);
}
|
public void | setDebug(boolean debug)Setter for property debug.
this.debug = debug;
|
public void | setExplanationText(java.lang.String explanationText)Setter for property explanationText.
this.explanationText = explanationText;
|
protected void | setKeyHolder(org.apache.james.security.KeyHolder keyHolder)Setter for property keyHolder.
It is protected instead of public for security reasons.
this.keyHolder = keyHolder;
|
public void | setPostmasterSigns(boolean postmasterSigns)Setter for property postmasterSigns.
this.postmasterSigns = postmasterSigns;
|
public void | setRebuildFrom(boolean rebuildFrom)Setter for property rebuildFrom.
this.rebuildFrom = rebuildFrom;
|
public void | setSignerName(java.lang.String signerName)Setter for property signerName.
this.signerName = signerName;
|