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 }