001    /*
002     * Copyright 2010 the original author or authors.
003     * 
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package com.hs.mail.sieve;
017    
018    import java.io.IOException;
019    
020    import javax.mail.MessagingException;
021    import javax.mail.internet.MimeMessage;
022    
023    import org.apache.jsieve.mail.ActionFileInto;
024    import org.apache.jsieve.mail.ActionKeep;
025    import org.apache.jsieve.mail.ActionRedirect;
026    import org.apache.jsieve.mail.ActionReject;
027    
028    import com.hs.mail.imap.ImapConstants;
029    import com.hs.mail.mailet.MailetContext;
030    import com.hs.mail.smtp.message.DeliveryStatusNotifier;
031    import com.hs.mail.smtp.message.MailAddress;
032    
033    public class Actions {
034    
035        /**
036         * Constructor for Actions.
037         */
038            private Actions() {
039                    super();
040            }
041    
042        /**
043         * <p>
044         * Executes the passed ActionFileInto.
045         * </p>
046         * 
047         * <p>
048         * This implementation accepts any destination with the root of <code>INBOX</code>.
049         * </p>
050         * 
051         * <p>
052         * As the current POP3 server does not support sub-folders, the mail is
053         * stored in the INBOX for the recipient of the mail and the full intended
054         * destination added as a prefix to the message's subject.
055         * </p>
056         * 
057         * <p>
058         * When IMAP support is added to James, it will be possible to support
059         * sub-folders of <code>INBOX</code> fully.
060         * </p>
061         * 
062         * @param anAction
063         * @param aMail
064         * @param aMailetContext
065         * @throws MessagingException
066         */
067            public static void execute(ActionFileInto anAction, SieveMailAdapter aMail,
068                            MailetContext aMailetContext) throws MessagingException {
069                    boolean delivered = false;
070                    try {
071                            String destination = anAction.getDestination();
072                            aMailetContext.storeMail(aMail.getSoleRecipientID(), destination,
073                                            aMail.getMessage());
074                            delivered = true;
075                    } catch (IOException e) {
076                            throw new MessagingException(e.getMessage(), e);
077                    } finally {
078                            // Ensure the mail is always ghosted
079                    }
080                    if (delivered) {
081                    }
082            }
083    
084        /**
085         * <p>
086         * Executes the passed ActionKeep.
087         * </p>
088         * 
089         * <p>
090         * In this implementation, "keep" is equivalent to "fileinto" with a
091         * destination of "INBOX".
092         * </p>
093         * 
094         * @param anAction
095         * @param aMail
096         * @param aMailetContext
097         * @throws MessagingException
098         */
099            public static void execute(ActionKeep anAction, SieveMailAdapter aMail,
100                            MailetContext aMailetContext) throws MessagingException {
101                    ActionFileInto action = new ActionFileInto(ImapConstants.INBOX_NAME);
102                    execute(action, aMail, aMailetContext);
103            }
104    
105        /**
106         * Method execute executes the passed ActionRedirect.
107         * 
108         * @param anAction
109         * @param aMail
110         * @param aMailetContext
111         * @throws MessagingException
112         */
113            public static void execute(ActionRedirect anAction, SieveMailAdapter aMail,
114                            MailetContext aMailetContext) throws MessagingException {
115                    try {
116                            detectAndHandleLocalLooping(aMail, anAction.getAddress());
117                            aMailetContext.sendMail(aMail.getSoleRecipient(),
118                                            new String[] { anAction.getAddress() }, aMail.getMessage());
119                    } catch (IOException e) {
120                            throw new MessagingException(e.getMessage(), e);
121                    }
122            }
123    
124        /**
125         * <p>
126         * Method execute executes the passed ActionReject. It sends an RFC 2098
127         * compliant reject MDN back to the sender.
128         * </p>
129         * 
130         * @param anAction
131         * @param aMail
132         * @param aMailetContext
133         * @throws MessagingException
134         */
135            public static void execute(ActionReject anAction, SieveMailAdapter aMail,
136                            MailetContext aMailetContext) throws MessagingException {
137                    detectAndHandleLocalLooping(aMail, null);
138                    // Create the MDN part
139                    StringBuilder humanText = new StringBuilder(128);
140                    humanText.append("This message was refused by the recipient's mail filtering program.");
141                    humanText.append("\r\n");
142                    humanText.append("The reason given was:");
143                    humanText.append("\r\n");
144                    humanText.append("\r\n");
145                    humanText.append(anAction.getMessage());
146                    
147                    MimeMessage message = aMail.getMessage().getMimeMessage();
148                    DeliveryStatusNotifier.dsnNotify(aMail.getSoleRecipient(),
149                                    new MailAddress(message.getReplyTo()[0]), message,
150                                    humanText.toString());
151            }
152    
153            /**
154             * Detect and handle locally looping mail. External loop detection is left
155             * to the MTA.
156             * 
157             * @param aMail
158             * @param aMailetContext
159             * @param anAttributeSuffix
160             * @throws MessagingException
161             */
162            protected static void detectAndHandleLocalLooping(SieveMailAdapter aMail,
163                            String recipient) throws MessagingException {
164                    if (aMail.getMessage().isNotificationMessage()) {
165                            // Don't reject or redirect notification message.
166                            throw new MessagingException(
167                                            "This message is a notification message!");
168                    }
169                    try {
170                            String from = getSender(aMail);
171                            if (from != null && from.equals(recipient)) {
172                                    MessagingException ex = new MessagingException(
173                                                    "This message is looping!");
174                                    throw ex;
175                            }
176                    } catch (IOException e) {
177                            throw new MessagingException(e.getMessage(), e);
178                    }
179            }
180            
181            private static String getSender(SieveMailAdapter aMail) throws IOException {
182                    return aMail.getMessage().getMailMessage().getFrom();
183            }
184    
185    }