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  
27  import java.util.Locale;
28  import java.util.StringTokenizer;
29  
30  import org.apache.avalon.framework.logger.AbstractLogEnabled;
31  import org.apache.james.dsn.DSNStatus;
32  import org.apache.james.smtpserver.CommandHandler;
33  import org.apache.james.smtpserver.SMTPSession;
34  import org.apache.mailet.MailAddress;
35  
36  /**
37    * Handles RCPT command
38    */
39  public class RcptFilterCmdHandler extends AbstractLogEnabled implements
40          CommandHandler {
41  
42  
43      /**
44       * handles RCPT command
45       *
46       * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
47      **/
48      public void onCommand(SMTPSession session) {
49          doRCPT(session, session.getCommandArgument());
50      }
51  
52  
53      /**
54       * @param session SMTP session object
55       * @param argument the argument passed in with the command by the SMTP client
56       */
57      private void doRCPT(SMTPSession session, String argument) {
58          String responseString = null;
59          
60          String recipient = null;
61          if ((argument != null) && (argument.indexOf(":") > 0)) {
62              int colonIndex = argument.indexOf(":");
63              recipient = argument.substring(colonIndex + 1);
64              argument = argument.substring(0, colonIndex);
65          }
66          if (!session.getState().containsKey(SMTPSession.SENDER)) {
67              responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" Need MAIL before RCPT";
68              session.writeResponse(responseString);
69              
70              // After this filter match we should not call any other handler!
71              session.setStopHandlerProcessing(true);
72              
73          } else if (argument == null || !argument.toUpperCase(Locale.US).equals("TO")
74                     || recipient == null) {
75              responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_SYNTAX)+" Usage: RCPT TO:<recipient>";
76              session.writeResponse(responseString);
77              
78              // After this filter match we should not call any other handler!
79              session.setStopHandlerProcessing(true);
80              
81          } else {
82              Collection rcptColl = (Collection) session.getState().get(SMTPSession.RCPT_LIST);
83              if (rcptColl == null) {
84                  rcptColl = new ArrayList();
85              }
86              recipient = recipient.trim();
87              int lastChar = recipient.lastIndexOf('>');
88              // Check to see if any options are present and, if so, whether they are correctly formatted
89              // (separated from the closing angle bracket by a ' ').
90              String rcptOptionString = null;
91              if ((lastChar > 0) && (recipient.length() > lastChar + 2) && (recipient.charAt(lastChar + 1) == ' ')) {
92                  rcptOptionString = recipient.substring(lastChar + 2);
93  
94                  // Remove the options from the recipient
95                  recipient = recipient.substring(0, lastChar + 1);
96              }
97              if (session.getConfigurationData().useAddressBracketsEnforcement() && (!recipient.startsWith("<") || !recipient.endsWith(">"))) {
98                  responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_SYNTAX)+" Syntax error in parameters or arguments";
99                  session.writeResponse(responseString);
100                 if (getLogger().isErrorEnabled()) {
101                     StringBuffer errorBuffer =
102                         new StringBuffer(192)
103                                 .append("Error parsing recipient address: ")
104                                 .append("Address did not start and end with < >")
105                                 .append(getContext(session,null,recipient));
106                     getLogger().error(errorBuffer.toString());
107                 }
108                 
109                 // After this filter match we should not call any other handler!
110                 session.setStopHandlerProcessing(true);
111                 
112                 return;
113             }
114             MailAddress recipientAddress = null;
115             //Remove < and >
116             if (session.getConfigurationData().useAddressBracketsEnforcement() || (recipient.startsWith("<") && recipient.endsWith(">"))) {
117                 recipient = recipient.substring(1, recipient.length() - 1);
118             }
119             
120             if (recipient.indexOf("@") < 0) {
121                 // set the default domain
122                 recipient = recipient + "@" + session.getConfigurationData().getMailServer().getDefaultDomain();
123             }
124             
125             try {
126                 recipientAddress = new MailAddress(recipient);
127             } catch (Exception pe) {
128                 /*
129                  * from RFC2822;
130                  * 553 Requested action not taken: mailbox name not allowed
131                  *     (e.g., mailbox syntax incorrect)
132                  */
133                 responseString = "553 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_SYNTAX)+" Syntax error in recipient address";
134                 session.writeResponse(responseString);
135 
136                 if (getLogger().isErrorEnabled()) {
137                     StringBuffer errorBuffer =
138                         new StringBuffer(192)
139                                 .append("Error parsing recipient address: ")
140                                 .append(getContext(session,recipientAddress,recipient))
141                                 .append(pe.getMessage());
142                     getLogger().error(errorBuffer.toString());
143                 }
144                 
145                 // After this filter match we should not call any other handler!
146                 session.setStopHandlerProcessing(true);
147                 
148                 return;
149             }
150 
151             
152 
153             if (session.isAuthRequired() && !session.isRelayingAllowed()) {
154                 // Make sure the mail is being sent locally if not
155                 // authenticated else reject.
156                 if (session.getUser() == null) {
157                     String toDomain = recipientAddress.getHost();
158                     if (!session.getConfigurationData().getMailServer().isLocalServer(toDomain)) {
159                         responseString = "530 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_AUTH)+" Authentication Required";
160                         session.writeResponse(responseString);
161                         StringBuffer sb = new StringBuffer(128);
162                         sb.append("Rejected message - authentication is required for mail request");
163                         sb.append(getContext(session,recipientAddress,recipient));
164                         getLogger().error(sb.toString());
165                         
166                         // After this filter match we should not call any other handler!
167                         session.setStopHandlerProcessing(true);
168                         
169                         return;
170                     }
171                 } else {
172                     // Identity verification checking
173                     if (session.getConfigurationData().isVerifyIdentity()) {
174                         String authUser = (session.getUser()).toLowerCase(Locale.US);
175                         MailAddress senderAddress = (MailAddress) session.getState().get(SMTPSession.SENDER);
176 
177                         if ((senderAddress == null) || (!authUser.equals(senderAddress.getUser())) ||
178                             (!session.getConfigurationData().getMailServer().isLocalServer(senderAddress.getHost()))) {
179                             responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_AUTH)+" Incorrect Authentication for Specified Email Address";
180                             session.writeResponse(responseString);
181                             if (getLogger().isErrorEnabled()) {
182                                 StringBuffer errorBuffer =
183                                     new StringBuffer(128)
184                                         .append("User ")
185                                         .append(authUser)
186                                         .append(" authenticated, however tried sending email as ")
187                                         .append(senderAddress)
188                                         .append(getContext(session,recipientAddress,recipient));
189                                 getLogger().error(errorBuffer.toString());
190                             }
191                             
192                             // After this filter match we should not call any other handler!
193                             session.setStopHandlerProcessing(true);
194                             
195                             return;
196                         }
197                     }
198                 }
199             } else if (!session.isRelayingAllowed()) {
200                 String toDomain = recipientAddress.getHost();
201                 if (!session.getConfigurationData().getMailServer().isLocalServer(toDomain)) {
202                     responseString = "550 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_AUTH)+" Requested action not taken: relaying denied";
203                     session.writeResponse(responseString);
204                     StringBuffer errorBuffer = new StringBuffer(128)
205                         .append("Rejected message - ")
206                         .append(session.getRemoteIPAddress())
207                         .append(" not authorized to relay to ")
208                         .append(toDomain)
209                         .append(getContext(session,recipientAddress,recipient));
210                     getLogger().error(errorBuffer.toString());
211                     
212                     // After this filter match we should not call any other handler!
213                     session.setStopHandlerProcessing(true);
214                     
215                     return;
216                 }
217             }
218             if (rcptOptionString != null) {
219 
220               StringTokenizer optionTokenizer = new StringTokenizer(rcptOptionString, " ");
221               while (optionTokenizer.hasMoreElements()) {
222                   String rcptOption = optionTokenizer.nextToken();
223                   int equalIndex = rcptOption.indexOf('=');
224                   String rcptOptionName = rcptOption;
225                   String rcptOptionValue = "";
226                   if (equalIndex > 0) {
227                       rcptOptionName = rcptOption.substring(0, equalIndex).toUpperCase(Locale.US);
228                       rcptOptionValue = rcptOption.substring(equalIndex + 1);
229                   }
230                   // Unexpected option attached to the RCPT command
231                   if (getLogger().isDebugEnabled()) {
232                       StringBuffer debugBuffer =
233                           new StringBuffer(128)
234                               .append("RCPT command had unrecognized/unexpected option ")
235                               .append(rcptOptionName)
236                               .append(" with value ")
237                               .append(rcptOptionValue)
238                               .append(getContext(session,recipientAddress,recipient));
239                       getLogger().debug(debugBuffer.toString());
240                   }
241                   
242                   // After this filter match we should not call any other handler!
243                   session.setStopHandlerProcessing(true);
244                   
245               }
246               optionTokenizer = null;
247             }
248     
249             session.getState().put(SMTPSession.CURRENT_RECIPIENT,recipientAddress);
250         }
251     }
252 
253 
254     private String getContext(SMTPSession session, MailAddress recipientAddress, String recipient){
255         StringBuffer sb = new StringBuffer(128);
256         if(null!=recipientAddress) {
257             sb.append(" [to:" + (recipientAddress).toInternetAddress().getAddress() + "]");
258         } else if(null!=recipient) {
259             sb.append(" [to:" + recipient + "]");
260         }
261         if (null!=session.getState().get(SMTPSession.SENDER)) {
262             sb.append(" [from:" + ((MailAddress)session.getState().get(SMTPSession.SENDER)).toInternetAddress().getAddress() + "]");
263         }
264         return sb.toString();
265     } 
266     
267     /**
268      * @see org.apache.james.smtpserver.CommandHandler#getImplCommands()
269      */
270     public Collection getImplCommands() {
271         Collection implCommands = new ArrayList();
272         implCommands.add("RCPT");
273         
274         return implCommands;
275     }
276     
277 }