View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  
22  package org.apache.james.smtpserver.core.filter;
23  
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Locale;
27  import java.util.StringTokenizer;
28  
29  import org.apache.avalon.framework.logger.AbstractLogEnabled;
30  import org.apache.james.dsn.DSNStatus;
31  import org.apache.james.smtpserver.CommandHandler;
32  import org.apache.james.smtpserver.SMTPSession;
33  import org.apache.mailet.MailAddress;
34  
35  /**
36    * Handles MAIL command
37    */
38  public class MailFilterCmdHandler
39      extends AbstractLogEnabled
40      implements CommandHandler {
41  
42      private final static String MAIL_OPTION_SIZE = "SIZE";
43  
44      private final static String MESG_SIZE = "MESG_SIZE"; // The size of the message
45  
46      /**
47       * handles MAIL command
48       *
49       * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
50       */
51      public void onCommand(SMTPSession session) {
52          doMAIL(session, session.getCommandArgument());
53      }
54  
55  
56      /**
57       * @param session SMTP session object
58       * @param argument the argument passed in with the command by the SMTP client
59       */
60      private void doMAIL(SMTPSession session, String argument) {
61          String responseString = null;
62          String sender = null;
63          
64          if ((argument != null) && (argument.indexOf(":") > 0)) {
65              int colonIndex = argument.indexOf(":");
66              sender = argument.substring(colonIndex + 1);
67              argument = argument.substring(0, colonIndex);
68          }
69          if (session.getState().containsKey(SMTPSession.SENDER)) {
70              responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" Sender already specified";
71              session.writeResponse(responseString);
72              
73              // After this filter match we should not call any other handler!
74              session.setStopHandlerProcessing(true);
75              
76          } else if (!session.getConnectionState().containsKey(SMTPSession.CURRENT_HELO_MODE) && session.useHeloEhloEnforcement()) {
77              responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" Need HELO or EHLO before MAIL";
78              session.writeResponse(responseString);
79              
80              // After this filter match we should not call any other handler!
81              session.setStopHandlerProcessing(true);
82              
83          } else if (argument == null || !argument.toUpperCase(Locale.US).equals("FROM")
84                     || sender == null) {
85              responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Usage: MAIL FROM:<sender>";
86              session.writeResponse(responseString);
87          
88              // After this filter match we should not call any other handler!
89              session.setStopHandlerProcessing(true);
90              
91          } else {
92              sender = sender.trim();
93              // the next gt after the first lt ... AUTH may add more <>
94              int lastChar = sender.indexOf('>', sender.indexOf('<'));
95              // Check to see if any options are present and, if so, whether they are correctly formatted
96              // (separated from the closing angle bracket by a ' ').
97              if ((lastChar > 0) && (sender.length() > lastChar + 2) && (sender.charAt(lastChar + 1) == ' ')) {
98                  String mailOptionString = sender.substring(lastChar + 2);
99  
100                 // Remove the options from the sender
101                 sender = sender.substring(0, lastChar + 1);
102 
103                 StringTokenizer optionTokenizer = new StringTokenizer(mailOptionString, " ");
104                 while (optionTokenizer.hasMoreElements()) {
105                     String mailOption = optionTokenizer.nextToken();
106                     int equalIndex = mailOption.indexOf('=');
107                     String mailOptionName = mailOption;
108                     String mailOptionValue = "";
109                     if (equalIndex > 0) {
110                         mailOptionName = mailOption.substring(0, equalIndex).toUpperCase(Locale.US);
111                         mailOptionValue = mailOption.substring(equalIndex + 1);
112                     }
113 
114                     // Handle the SIZE extension keyword
115 
116                     if (mailOptionName.startsWith(MAIL_OPTION_SIZE)) {
117                         if (!(doMailSize(session, mailOptionValue, sender))) {
118                             return;
119                         }
120                     } else {
121                         // Unexpected option attached to the Mail command
122                         if (getLogger().isDebugEnabled()) {
123                             StringBuffer debugBuffer =
124                                 new StringBuffer(128)
125                                     .append("MAIL command had unrecognized/unexpected option ")
126                                     .append(mailOptionName)
127                                     .append(" with value ")
128                                     .append(mailOptionValue);
129                             getLogger().debug(debugBuffer.toString());
130                         }
131                     }
132                 }
133             }
134             if ( session.getConfigurationData().useAddressBracketsEnforcement() && (!sender.startsWith("<") || !sender.endsWith(">"))) {
135                 responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_SYNTAX_SENDER)+" Syntax error in MAIL command";
136                 session.writeResponse(responseString);
137                 if (getLogger().isErrorEnabled()) {
138                     StringBuffer errorBuffer =
139                         new StringBuffer(128)
140                             .append("Error parsing sender address: ")
141                             .append(sender)
142                             .append(": did not start and end with < >");
143                     getLogger().error(errorBuffer.toString());
144                 }
145                 // After this filter match we should not call any other handler!
146                 session.setStopHandlerProcessing(true);
147                 
148                 return;
149             }
150             MailAddress senderAddress = null;
151             
152             if (session.getConfigurationData().useAddressBracketsEnforcement() || (sender.startsWith("<") && sender.endsWith(">"))) {
153                 //Remove < and >
154                 sender = sender.substring(1, sender.length() - 1);
155             }
156             
157             if (sender.length() == 0) {
158                 //This is the <> case.  Let senderAddress == null
159             } else {
160                  
161                 if (sender.indexOf("@") < 0) {
162                     sender = sender + "@" + session.getConfigurationData().getMailServer().getDefaultDomain();
163                 }
164                 
165                 try {
166                     senderAddress = new MailAddress(sender);
167                 } catch (Exception pe) {
168                     responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_SYNTAX_SENDER)+" Syntax error in sender address";
169                     session.writeResponse(responseString);
170                     if (getLogger().isErrorEnabled()) {
171                         StringBuffer errorBuffer =
172                             new StringBuffer(256)
173                                     .append("Error parsing sender address: ")
174                                     .append(sender)
175                                     .append(": ")
176                                     .append(pe.getMessage());
177                         getLogger().error(errorBuffer.toString());
178                     }
179                     
180                     // After this filter match we should not call any other handler!
181                     session.setStopHandlerProcessing(true);
182                     
183                     return;
184                 }
185             }
186          
187             // Store the senderAddress in session map
188             session.getState().put(SMTPSession.SENDER, senderAddress);
189         }
190     }
191 
192     /**
193      * Handles the SIZE MAIL option.
194      *
195      * @param session SMTP session object
196      * @param mailOptionValue the option string passed in with the SIZE option
197      * @param tempSender the sender specified in this mail command (for logging purpose)
198      * @return true if further options should be processed, false otherwise
199      */
200     private boolean doMailSize(SMTPSession session, String mailOptionValue, String tempSender) {
201         int size = 0;
202         try {
203             size = Integer.parseInt(mailOptionValue);
204         } catch (NumberFormatException pe) {
205             // This is a malformed option value.  We return an error
206             String responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Syntactically incorrect value for SIZE parameter";
207             session.writeResponse(responseString);
208             getLogger().error("Rejected syntactically incorrect value for SIZE parameter.");
209             
210             // After this filter match we should not call any other handler!
211             session.setStopHandlerProcessing(true);
212             
213             return false;
214         }
215         if (getLogger().isDebugEnabled()) {
216             StringBuffer debugBuffer =
217                 new StringBuffer(128)
218                     .append("MAIL command option SIZE received with value ")
219                     .append(size)
220                     .append(".");
221                     getLogger().debug(debugBuffer.toString());
222         }
223         long maxMessageSize = session.getConfigurationData().getMaxMessageSize();
224         if ((maxMessageSize > 0) && (size > maxMessageSize)) {
225             // Let the client know that the size limit has been hit.
226             String responseString = "552 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SYSTEM_MSG_TOO_BIG)+" Message size exceeds fixed maximum message size";
227             session.writeResponse(responseString);
228             StringBuffer errorBuffer =
229                 new StringBuffer(256)
230                     .append("Rejected message from ")
231                     .append(tempSender != null ? tempSender : null)
232                     .append(" from host ")
233                     .append(session.getRemoteHost())
234                     .append(" (")
235                     .append(session.getRemoteIPAddress())
236                     .append(") of size ")
237                     .append(size)
238                     .append(" exceeding system maximum message size of ")
239                     .append(maxMessageSize)
240                     .append("based on SIZE option.");
241             getLogger().error(errorBuffer.toString());
242             
243             // After this filter match we should not call any other handler!
244             session.setStopHandlerProcessing(true);
245             
246             return false;
247         } else {
248             // put the message size in the message state so it can be used
249             // later to restrict messages for user quotas, etc.
250             session.getState().put(MESG_SIZE, new Integer(size));
251         }
252         return true;
253     }
254     
255     /**
256      * @see org.apache.james.smtpserver.CommandHandler#getImplCommands()
257      */
258     public Collection getImplCommands() {
259         Collection implCommands = new ArrayList();
260         implCommands.add("MAIL");
261         
262         return implCommands;
263     }
264 
265 }