/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.edi.as2.module; import hk.hku.cecid.edi.as2.dao.AS2DAOHandler; import hk.hku.cecid.edi.as2.dao.MessageDVO; import hk.hku.cecid.edi.as2.dao.PartnershipDVO; import hk.hku.cecid.edi.as2.dao.RawRepositoryDVO; import hk.hku.cecid.edi.as2.dao.RepositoryDVO; import hk.hku.cecid.edi.as2.pkg.AS2Header; import hk.hku.cecid.edi.as2.pkg.AS2Message; import hk.hku.cecid.edi.as2.pkg.DispositionNotificationOption; import hk.hku.cecid.edi.as2.util.AS2Util; import hk.hku.cecid.piazza.commons.activation.ByteArrayDataSource; import hk.hku.cecid.piazza.commons.activation.InputStreamDataSource; import hk.hku.cecid.piazza.commons.io.IOHandler; import hk.hku.cecid.piazza.commons.module.SystemComponent; import hk.hku.cecid.piazza.commons.security.KeyStoreManager; import hk.hku.cecid.piazza.commons.security.SMimeException; import hk.hku.cecid.piazza.commons.security.SMimeMessage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Iterator; import java.util.Properties; import java.util.Set; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.internet.MimeBodyPart; public class OutgoingMessageProcessor extends SystemComponent { private Properties payloadTypes; @Override protected void init() throws Exception { super.init(); String[] types = getProperties().getProperties("/as2/content_type/type"); Properties props = new Properties(); for(String type :types){ String[] tokens = type.split(";"); props.setProperty(tokens[0], tokens[1]); } payloadTypes = props; } public AS2Message resendAsNew(String primalMessageId) throws Exception { AS2DAOHandler daoHandler = new AS2DAOHandler(getDAOFactory()); // Checking precondition if (primalMessageId == null || 0 == primalMessageId.length()) { throw new Exception("Null / Empty primal message ID"); } MessageDVO primalMsgDVO = daoHandler.findMessageDVO(primalMessageId, MessageDVO.MSGBOX_OUT); String primalStatus = primalMsgDVO.getStatus(); if (MessageDVO.STATUS_PENDING.equals(primalStatus) || MessageDVO.STATUS_PROCESSING.equals(primalStatus)) { throw new Exception("Message can only be resent as new when its status is not Pending or Processing"); } if (primalMsgDVO.isReceipt()) { throw new Exception("Receipt cannot be resent as new"); } String partnershipId = primalMsgDVO.getPartnershipId(); if (null == partnershipId) { throw new Exception("Null partnership ID"); } PartnershipDVO partnershipDVO = daoHandler.findPartnership(primalMsgDVO.getPartnershipId()); primalMsgDVO.setHasResendAsNew("true"); // Reconstruct old as2 message RawRepositoryDVO primalRawRepoDVO = daoHandler.findRawRepositoryDVO(primalMessageId); ByteArrayInputStream messageContent = new ByteArrayInputStream(primalRawRepoDVO.getContent()); AS2Message oldAs2Message = new AS2Message(messageContent); messageContent.close(); // Construct new as2 message String[] values = oldAs2Message.getBodyPart().getHeader("Content-Disposition"); String fileName = AS2Util.getFileNameFromMIMEHeader(values); MimeBodyPart bodyPart = oldAs2Message.getBodyPart(); String contentType = bodyPart.getContentType(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOHandler.pipe(bodyPart.getInputStream(), baos); DataSource dataSource = new ByteArrayDataSource(baos.toByteArray(), contentType); DataHandler dataHandler = new DataHandler(dataSource); String type = getType(contentType); InputStreamDataSource insDS = new InputStreamDataSource(dataHandler.getInputStream(), type, fileName); AS2Message newAs2Message = storeOutgoingMessage(AS2Message.generateID(), type, partnershipDVO, insDS, primalMsgDVO); return newAs2Message; } public AS2Message storeOutgoingMessage( String messageID, String type, PartnershipDVO partnership, DataSource attachementSource) throws Exception{ return storeOutgoingMessage(messageID, type, partnership, attachementSource, null); } public AS2Message storeOutgoingMessage( String messageID, String type, PartnershipDVO partnership, DataSource attachementSource, MessageDVO primalMsgDVO) throws Exception{ AS2Message as2Message; try { as2Message = new AS2Message(); // Attach Header Value as2Message.setMessageID(messageID); as2Message.setFromPartyID(partnership.getAS2From()); as2Message.setToPartyID(partnership.getAs2To()); as2Message.setHeader(AS2Header.SUBJECT, partnership.getSubject()); // Set Content to Message as2Message.setContent(attachementSource, getPayloadContentType(attachementSource.getContentType())); if(attachementSource.getName() != null){ as2Message.getBodyPart().addHeader("Content-Disposition", "attachment; filename=" + attachementSource.getName()); } AS2DAOHandler daoHandler = new AS2DAOHandler(getDAOFactory()); RawRepositoryDVO rawRepoDVO = daoHandler.createRawRepositoryDVO(as2Message); String mic = processMessage(as2Message, partnership); persistMessage(as2Message, mic, rawRepoDVO, primalMsgDVO); return as2Message; } catch (Exception e) { throw new Exception("OutgoingPayloadProcessor error", e); } } private void persistMessage(AS2Message as2Message, String mic, RawRepositoryDVO rawRepoDVO, MessageDVO primalMsgDVO) throws Exception { AS2DAOHandler daoHandler = new AS2DAOHandler(getDAOFactory()); // Persist Message to Database getLogger().info("Persisting outbound " + as2Message); RepositoryDVO repoDVO = daoHandler.createRepositoryDVO(as2Message, false); MessageDVO msgDVO = daoHandler.createMessageDVO(as2Message, false); msgDVO.setStatus(MessageDVO.STATUS_PENDING); msgDVO.setMicValue(mic); if (null != primalMsgDVO) { msgDVO.setPrimalMessageId(primalMsgDVO.getMessageId()); } /* Capture the outgoing message */ daoHandler.createMessageStore().storeMessage(primalMsgDVO, msgDVO, repoDVO, rawRepoDVO); getLogger().debug("AS2 Message is stored on database. ["+as2Message.getMessageID()+"]"); } private String processMessage(AS2Message as2Message, PartnershipDVO partnership) throws Exception { String micAlg = null; // Add header fields for request receipt if (partnership.isReceiptRequired()) { String returnUrl = null; if (!partnership.isSyncReply()) { returnUrl = partnership.getReceiptAddress(); } if (partnership.isReceiptSignRequired()) { micAlg = partnership.getMicAlgorithm(); } as2Message.requestReceipt(returnUrl, micAlg); } KeyStoreManager keyman =(KeyStoreManager) getComponent("keystore-manager"); SMimeMessage smime = new SMimeMessage(as2Message.getBodyPart(), keyman.getX509Certificate(), keyman.getPrivateKey()); smime.setContentTransferEncoding(SMimeMessage.CONTENT_TRANSFER_ENC_BINARY); String mic = calculateMIC(smime, partnership); if (partnership.isOutboundCompressRequired()) { getLogger().info("Compressing outbound "+as2Message); smime = smime.compress(); if (partnership.isOutboundSignRequired()) { mic = calculateMIC(smime, partnership); } } if (partnership.isOutboundSignRequired()) { getLogger().info("Signing outbound "+as2Message); String alg = partnership.getSignAlgorithm(); if (alg != null && alg.equalsIgnoreCase(PartnershipDVO.ALG_SIGN_MD5)) { smime.setDigestAlgorithm(SMimeMessage.DIGEST_ALG_MD5); } else { smime.setDigestAlgorithm(SMimeMessage.DIGEST_ALG_SHA1); } smime = smime.sign(); } if (partnership.isOutboundEncryptRequired()) { getLogger().info("Encrypting outbound "+as2Message); String alg = partnership.getEncryptAlgorithm(); if (alg != null && alg.equalsIgnoreCase(PartnershipDVO.ALG_ENCRYPT_RC2)) { smime.setEncryptAlgorithm(SMimeMessage.ENCRYPT_ALG_RC2_CBC); } else { smime.setEncryptAlgorithm(SMimeMessage.ENCRYPT_ALG_DES_EDE3_CBC); } smime = smime.encrypt(partnership.getEncryptX509Certificate()); } as2Message.setBodyPart(smime.getBodyPart()); return mic; } public String calculateMIC(SMimeMessage smime, PartnershipDVO partnership) throws SMimeException { String mic = null; if (partnership.isReceiptSignRequired()) { boolean isSMime = partnership.isOutboundCompressRequired() || partnership.isOutboundSignRequired() || partnership.isOutboundEncryptRequired(); String micAlg = partnership.getMicAlgorithm(); if (micAlg !=null && micAlg.equalsIgnoreCase(PartnershipDVO.ALG_MIC_MD5)) { mic = smime.digest(SMimeMessage.DIGEST_ALG_MD5, isSMime); micAlg = DispositionNotificationOption.SIGNED_RECEIPT_MICALG_MD5; } else { mic = smime.digest(SMimeMessage.DIGEST_ALG_SHA1, isSMime); micAlg = DispositionNotificationOption.SIGNED_RECEIPT_MICALG_SHA1; } mic = mic + ", " + micAlg; } return mic; } //Return the content-type mapping that defined on OutgoingMessageProcessor Component Parameters private String getPayloadContentType(String type){ if(type == null){ return "application/octet-stream"; }else{ String t = payloadTypes.getProperty(type); if (t == null) { return"application/octet-stream"; } else { return t.toString(); } } } // The inverse function of getPayloadContentType private String getType(String contentType) { if ("application/octet-stream".equals(contentType)) { return null; } else { Set<?> keySet = payloadTypes.keySet(); Iterator<?> iter = keySet.iterator(); while (iter.hasNext()) { String iterType = (String) iter.next(); String iterContentType = (String) payloadTypes.get(iterType); if (contentType.equals(iterContentType)) { return iterType; } } return null; } } }