/** * ESUP-Portail Helpdesk - Copyright (c) 2004-2009 University of Pardubice. */ package org.esupportail.helpdesk.services.feed.imap.messageId; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import org.esupportail.commons.exceptions.ConfigException; import org.esupportail.commons.services.logging.Logger; import org.esupportail.commons.services.logging.LoggerImpl; import org.esupportail.commons.utils.Assert; import org.esupportail.commons.utils.strings.StringUtils; import org.esupportail.helpdesk.domain.beans.Ticket; import org.springframework.beans.factory.InitializingBean; /** * A simple messageId handler. */ public class SigningMessageIdHandlerImpl extends AbstractMessageIdHandler implements InitializingBean { /** * The serialization id. */ private static final long serialVersionUID = -4788996241260919702L; /** * Regular expression patter to find ticket number. */ private static final String TICKET_ID_PATTERN = "<ticketId\\.(\\d+)\\.([0-9a-v]+)@.*>"; /** * The base used to hash. */ private static final int HASH_BASE = 32; /** * The length of hashes. */ private static final int HASH_LENGTH = 16; /** * A logger. */ private final Logger logger = new LoggerImpl(getClass()); /** * The domain of outgoing emails, used to build the MessageID headers. */ private String emailDomain; /** * Salt to sign. */ private String salt; /** * Constructor. */ public SigningMessageIdHandlerImpl() { super(); } /** * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() { Assert.hasText(emailDomain, "property emailDomain of class " + this.getClass().getName() + " can not be null"); if (salt == null) { logger.info("no salt provided, turning off reply-to functionalities!"); } } /** * @param ticketId * @return a hash. */ protected String getHash( final long ticketId) { if (salt == null) { throw new ConfigException("no salt provided, reply-to functionalities are turned off!"); } try { MessageDigest md = MessageDigest.getInstance("MD5"); md.reset(); md.update((ticketId + salt).getBytes()); //base-32 encoded hash; only 16 digits for shorter Message-Ids return new BigInteger(1, md.digest()) .toString(HASH_BASE).substring(0, HASH_LENGTH).toLowerCase(); } catch (NoSuchAlgorithmException e) { throw new MessageIdException(e); } } /** * @see org.esupportail.helpdesk.services.feed.imap.messageId.MessageIdHandler#genMessageId( * org.esupportail.helpdesk.domain.beans.Ticket) */ @Override public String genMessageId(final Ticket ticket) { if (ticket == null) { // Message-ID according to RFC 822 in form "<yyyyyyyyy@domain>", // where yyyyyyyyy is a random number return "<" + Math.abs(new Random().nextLong()) + "@" + emailDomain + ">"; } // Message-ID according to RFC 822 in form "<ticketId.xxx.yyy@domain>", // where xxx is the ticket id and and yyy is hash. long ticketId = ticket.getId(); String hash = getHash(ticketId); return "<" + "ticketId." + ticketId + "." + hash + "@" + emailDomain + ">"; } /** * @see org.esupportail.helpdesk.services.feed.imap.messageId.MessageIdHandler#getTicketIdFromMessageId( * java.lang.String) */ @Override public Long getTicketIdFromMessageId( final String messageId) throws MessageIdException { if (!messageId.matches(TICKET_ID_PATTERN)) { throw new MessageIdException( "messageId does not match pattern [ticketId.<id>.<hash>@" + emailDomain + "]"); } long ticketId; try { ticketId = Long.valueOf(messageId.replaceAll(TICKET_ID_PATTERN, "$1")); } catch (NumberFormatException e1) { throw new MessageIdException("invalid ticketId"); } String hash = messageId.replaceAll(TICKET_ID_PATTERN, "$2"); if (!getHash(ticketId).equals(hash)) { throw new MessageIdException("invalid hash"); } return ticketId; } /** * @return the salt */ protected String getSalt() { return salt; } /** * @param salt the salt */ public void setSalt(final String salt) { this.salt = StringUtils.nullIfEmpty(salt); } /** * @return the emailDomain */ protected String getEmailDomain() { return emailDomain; } /** * @param emailDomain the emailDomain to set */ public void setEmailDomain(final String emailDomain) { this.emailDomain = StringUtils.nullIfEmpty(emailDomain); } }