001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020 package org.apache.james.protocols.smtp.core;
021
022 import java.util.ArrayList;
023 import java.util.Collection;
024 import java.util.Locale;
025 import java.util.StringTokenizer;
026
027 import org.apache.james.protocols.api.handler.CommandHandler;
028 import org.apache.james.protocols.smtp.SMTPResponse;
029 import org.apache.james.protocols.smtp.SMTPRetCode;
030 import org.apache.james.protocols.smtp.SMTPSession;
031 import org.apache.james.protocols.smtp.dsn.DSNStatus;
032 import org.apache.james.protocols.smtp.hook.HookResult;
033 import org.apache.james.protocols.smtp.hook.RcptHook;
034 import org.apache.mailet.MailAddress;
035
036 /**
037 * Handles RCPT command
038 */
039 public class RcptCmdHandler extends AbstractHookableCmdHandler<RcptHook> implements
040 CommandHandler<SMTPSession> {
041
042 public static final String CURRENT_RECIPIENT = "CURRENT_RECIPIENT"; // Current recipient
043
044
045
046 /**
047 * Handler method called upon receipt of a RCPT command. Reads recipient.
048 * Does some connection validation.
049 *
050 *
051 * @param session
052 * SMTP session object
053 * @param command
054 * command passed
055 * @param parameters
056 * parameters passed in with the command by the SMTP client
057 */
058 @SuppressWarnings("unchecked")
059 protected SMTPResponse doCoreCmd(SMTPSession session, String command,
060 String parameters) {
061 Collection<MailAddress> rcptColl = (Collection<MailAddress>) session.getState().get(
062 SMTPSession.RCPT_LIST);
063 if (rcptColl == null) {
064 rcptColl = new ArrayList<MailAddress>();
065 }
066 MailAddress recipientAddress = (MailAddress) session.getState().get(
067 CURRENT_RECIPIENT);
068 rcptColl.add(recipientAddress);
069 session.getState().put(SMTPSession.RCPT_LIST, rcptColl);
070 StringBuilder response = new StringBuilder();
071 response
072 .append(
073 DSNStatus.getStatus(DSNStatus.SUCCESS,
074 DSNStatus.ADDRESS_VALID))
075 .append(" Recipient <").append(recipientAddress).append("> OK");
076 return new SMTPResponse(SMTPRetCode.MAIL_OK, response);
077
078 }
079
080 /**
081 * @param session
082 * SMTP session object
083 * @param argument
084 * the argument passed in with the command by the SMTP client
085 */
086 protected SMTPResponse doFilterChecks(SMTPSession session, String command,
087 String argument) {
088 String recipient = null;
089 if ((argument != null) && (argument.indexOf(":") > 0)) {
090 int colonIndex = argument.indexOf(":");
091 recipient = argument.substring(colonIndex + 1);
092 argument = argument.substring(0, colonIndex);
093 }
094 if (!session.getState().containsKey(SMTPSession.SENDER)) {
095 return new SMTPResponse(SMTPRetCode.BAD_SEQUENCE, DSNStatus
096 .getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_OTHER)
097 + " Need MAIL before RCPT");
098 } else if (argument == null
099 || !argument.toUpperCase(Locale.US).equals("TO")
100 || recipient == null) {
101 return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,
102 DSNStatus.getStatus(DSNStatus.PERMANENT,
103 DSNStatus.DELIVERY_SYNTAX)
104 + " Usage: RCPT TO:<recipient>");
105 }
106
107 recipient = recipient.trim();
108 int lastChar = recipient.lastIndexOf('>');
109 // Check to see if any options are present and, if so, whether they
110 // are correctly formatted
111 // (separated from the closing angle bracket by a ' ').
112 String rcptOptionString = null;
113 if ((lastChar > 0) && (recipient.length() > lastChar + 2)
114 && (recipient.charAt(lastChar + 1) == ' ')) {
115 rcptOptionString = recipient.substring(lastChar + 2);
116
117 // Remove the options from the recipient
118 recipient = recipient.substring(0, lastChar + 1);
119 }
120 if (session.useAddressBracketsEnforcement()
121 && (!recipient.startsWith("<") || !recipient.endsWith(">"))) {
122 if (session.getLogger().isInfoEnabled()) {
123 StringBuilder errorBuffer = new StringBuilder(192).append(
124 "Error parsing recipient address: ").append(
125 "Address did not start and end with < >").append(
126 getContext(session, null, recipient));
127 session.getLogger().info(errorBuffer.toString());
128 }
129 return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,
130 DSNStatus.getStatus(DSNStatus.PERMANENT,
131 DSNStatus.DELIVERY_SYNTAX)
132 + " Syntax error in parameters or arguments");
133 }
134 MailAddress recipientAddress = null;
135 // Remove < and >
136 if (session.useAddressBracketsEnforcement()
137 || (recipient.startsWith("<") && recipient.endsWith(">"))) {
138 recipient = recipient.substring(1, recipient.length() - 1);
139 }
140
141 if (recipient.indexOf("@") < 0) {
142 // set the default domain
143 recipient = recipient
144 + "@"
145 + getDefaultDomain();
146 }
147
148 try {
149 recipientAddress = new MailAddress(recipient);
150 } catch (Exception pe) {
151 if (session.getLogger().isInfoEnabled()) {
152 StringBuilder errorBuffer = new StringBuilder(192).append(
153 "Error parsing recipient address: ").append(
154 getContext(session, recipientAddress, recipient))
155 .append(pe.getMessage());
156 session.getLogger().info(errorBuffer.toString());
157 }
158 /*
159 * from RFC2822; 553 Requested action not taken: mailbox name
160 * not allowed (e.g., mailbox syntax incorrect)
161 */
162 return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_MAILBOX,
163 DSNStatus.getStatus(DSNStatus.PERMANENT,
164 DSNStatus.ADDRESS_SYNTAX)
165 + " Syntax error in recipient address");
166 }
167
168 if (rcptOptionString != null) {
169
170 StringTokenizer optionTokenizer = new StringTokenizer(
171 rcptOptionString, " ");
172 while (optionTokenizer.hasMoreElements()) {
173 String rcptOption = optionTokenizer.nextToken();
174 int equalIndex = rcptOption.indexOf('=');
175 String rcptOptionName = rcptOption;
176 String rcptOptionValue = "";
177 if (equalIndex > 0) {
178 rcptOptionName = rcptOption.substring(0, equalIndex)
179 .toUpperCase(Locale.US);
180 rcptOptionValue = rcptOption.substring(equalIndex + 1);
181 }
182 // Unexpected option attached to the RCPT command
183 if (session.getLogger().isDebugEnabled()) {
184 StringBuilder debugBuffer = new StringBuilder(128)
185 .append(
186 "RCPT command had unrecognized/unexpected option ")
187 .append(rcptOptionName).append(" with value ")
188 .append(rcptOptionValue).append(
189 getContext(session, recipientAddress,
190 recipient));
191 session.getLogger().debug(debugBuffer.toString());
192 }
193
194 return new SMTPResponse(
195 SMTPRetCode.PARAMETER_NOT_IMPLEMENTED,
196 "Unrecognized or unsupported option: "
197 + rcptOptionName);
198 }
199 optionTokenizer = null;
200 }
201
202 session.getState().put(CURRENT_RECIPIENT,recipientAddress);
203
204 return null;
205 }
206
207 private String getContext(SMTPSession session,
208 MailAddress recipientAddress, String recipient) {
209 StringBuilder sb = new StringBuilder(128);
210 if (null != recipientAddress) {
211 sb
212 .append(" [to:"
213 + (recipientAddress).toInternetAddress()
214 .getAddress() + "]");
215 } else if (null != recipient) {
216 sb.append(" [to:" + recipient + "]");
217 }
218 if (null != session.getState().get(SMTPSession.SENDER)) {
219 sb
220 .append(" [from:"
221 + ((MailAddress) session.getState().get(
222 SMTPSession.SENDER)).toInternetAddress()
223 .getAddress() + "]");
224 }
225 return sb.toString();
226 }
227
228 /**
229 * @see org.apache.james.protocols.api.handler.CommandHandler#getImplCommands()
230 */
231 public Collection<String> getImplCommands() {
232 Collection<String> implCommands = new ArrayList<String>();
233 implCommands.add("RCPT");
234
235 return implCommands;
236 }
237
238 /**
239 * @see org.apache.james.protocols.smtp.core.AbstractHookableCmdHandler#getHookInterface()
240 */
241 protected Class<RcptHook> getHookInterface() {
242 return RcptHook.class;
243 }
244
245 /**
246 * {@inheritDoc}
247 */
248 protected HookResult callHook(RcptHook rawHook, SMTPSession session,
249 String parameters) {
250 return rawHook.doRcpt(session,
251 (MailAddress) session.getState().get(SMTPSession.SENDER),
252 (MailAddress) session.getState().get(CURRENT_RECIPIENT));
253 }
254
255 protected String getDefaultDomain() {
256 return "localhost";
257 }
258 }