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.smtp.processor;
017
018 import java.io.IOException;
019 import java.util.StringTokenizer;
020
021 import javax.security.auth.login.LoginException;
022
023 import com.hs.mail.container.server.socket.TcpTransport;
024 import com.hs.mail.imap.user.UserManager;
025 import com.hs.mail.smtp.SmtpException;
026 import com.hs.mail.smtp.SmtpSession;
027 import com.hs.mail.util.BASE64;
028
029 /**
030 * Handler for AUTH command.
031 *
032 * @author Won Chul Doh
033 * @since Jun 27, 2010
034 *
035 */
036 public class AuthProcessor extends AbstractSmtpProcessor {
037
038 private static final String AUTH_TYPE_PLAIN = "PLAIN";
039
040 private static final String AUTH_TYPE_LOGIN = "LOGIN";
041
042 @Override
043 protected void doProcess(SmtpSession session, TcpTransport trans,
044 StringTokenizer st) throws SmtpException {
045 try {
046 doAuth(session, trans, st);
047 } catch (SmtpException e) {
048 // re-throw this exception
049 throw e;
050 } catch (Exception e) {
051 // Instead of throwing exception just end the session
052 trans.endSession();
053 }
054 }
055
056 /**
057 * Handles client authentication to the SMTP server.
058 */
059 private void doAuth(SmtpSession session, TcpTransport trans,
060 StringTokenizer st) throws Exception {
061 if (session.getAuthID() > 0) {
062 throw new SmtpException(SmtpException.ALREADY_AUTHENTICATED);
063 } else if (st.countTokens() < 1) {
064 throw new SmtpException(SmtpException.INVALID_COMMAND_PARAM);
065 }
066 String authType = nextToken(st);
067 if (AUTH_TYPE_PLAIN.equalsIgnoreCase(authType)) {
068 doPlainAuth(session, trans, st);
069 } else if (AUTH_TYPE_LOGIN.equalsIgnoreCase(authType)) {
070 doLoginAuth(session, trans, st);
071 } else {
072 throw new SmtpException(SmtpException.AUTHTYPE_NOT_SUPPORTED + ": "
073 + authType);
074 }
075 }
076
077 /**
078 * Handle the Plain AUTH SASL exchange. According to RFC 2595 the client
079 * must send: [authorize-id] \0 authenticate-id \0 password.
080 */
081 private void doPlainAuth(SmtpSession session, TcpTransport trans,
082 StringTokenizer st) throws IOException {
083 String userpass = null, user = null, pass = null;
084 if (st.hasMoreTokens()) {
085 userpass = nextToken(st);
086 } else {
087 session.writeResponse("334 OK. Continue authentication");
088 userpass = trans.readLine();
089 }
090 try {
091 userpass = new String(BASE64.decode(userpass));
092 StringTokenizer args = new StringTokenizer(userpass, "\0");
093 if (args.countTokens() == 3) {
094 // RFC 2595 says that "the client may leave the authorization
095 // identity empty to indicate that it is the same as the
096 // authentication identity.
097 // So skip the authorization identity
098 args.nextToken();
099 }
100 user = args.nextToken(); // Authentication identity
101 pass = args.nextToken(); // Password
102 } catch (Exception e) {
103 // Ignore - this exception will be dealt with in the if clause below
104 }
105 // Authenticate user
106 authenticate(session, trans, user, pass);
107 }
108
109 /**
110 * Handle the Login AUTH SASL exchange.
111 */
112 private void doLoginAuth(SmtpSession session, TcpTransport trans,
113 StringTokenizer st) throws IOException {
114 String user = null, pass = null;
115 if (st.hasMoreTokens()) {
116 user = nextToken(st);
117 } else {
118 session.writeResponse("334 VXNlcm5hbWU6"); // base64 encoded "Username:"
119 user = trans.readLine();
120 }
121 try {
122 user = new String(BASE64.decode(user));
123 } catch (Exception e) {
124 // Ignore - this exception will be dealt with in the if clause below
125 user = null;
126 }
127 session.writeResponse("334 UGFzc3dvcmQ6"); // base64 encoded "Password:"
128 pass = trans.readLine();
129 try {
130 pass = new String(BASE64.decode(pass));
131 } catch (Exception e) {
132 // Ignore - this exception will be dealt with in the if clause below
133 pass = null;
134 }
135 // Authenticate user
136 authenticate(session, trans, user, pass);
137 }
138
139 private void authenticate(SmtpSession session, TcpTransport trans,
140 String user, String pass) {
141 if ((user == null) || (pass == null)) {
142 throw new SmtpException(SmtpException.CANNOT_DECODE_PARAM);
143 } else {
144 try {
145 UserManager manager = getUserManager();
146 long authID = manager.login(user, pass);
147 session.setAuthID(authID);
148 session.writeResponse("235 2.7.0 Authentication Successful");
149 } catch (LoginException e) {
150 throw new SmtpException(SmtpException.AUTH_FAILED);
151 }
152 }
153 }
154
155 }