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 }