package org.subethamail.smtp.client; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.subethamail.smtp.util.Base64; /** * PlainAuthenticator implements the SASL PLAIN mechanism which authenticates * the client using a name - password combination. * * @see <a href="http://tools.ietf.org/html/rfc4616">RFC 4616: The PLAIN Simple * Authentication and Security Layer (SASL) Mechanism</a> */ public class PlainAuthenticator implements Authenticator { private final String user; private final String password; private final SmartClient smartClient; public PlainAuthenticator(SmartClient smartClient, String user, String password) { this.smartClient = smartClient; this.user = user; this.password = password; } @Override public void authenticate() throws SMTPException, AuthenticationNotSupportedException, IOException { checkAuthPlainSupport(); String initialClientResponse = constructInitialClientResponse(); smartClient.sendAndCheck("AUTH PLAIN " + initialClientResponse); } /** * Checks if the server supports this mechanism. * * @throws AuthenticationNotSupportedException * if the server does not support this mechanism or * authentication at all. */ private void checkAuthPlainSupport() throws AuthenticationNotSupportedException { String mechanismsString = smartClient.getExtensions().get("AUTH"); if (mechanismsString == null) { throw new AuthenticationNotSupportedException( "Cannot authenticate, because the AUTH extension is " + "not supported by the server. Maybe the server expects " + "TLS first"); } Set<String> mechanisms = parseMechanismsList(mechanismsString); if (!mechanisms.contains("PLAIN")) { throw new AuthenticationNotSupportedException( "Cannot authenticate, because the PLAIN mechanism is " + "not supported by the server. Maybe the server expects " + "TLS first"); } } /** * Parses the EHLO parameter list of the SMTP AUTH extension keyword. * * @return the set of SASL mechanism names */ private Set<String> parseMechanismsList(String authParameters) { String[] mechanisms = authParameters.split(" "); return new HashSet<String>(Arrays.asList(mechanisms)); } /** * Creates the base64 encoded SASL PLAIN initial response. */ private String constructInitialClientResponse() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(512); out.write(0); out.write(user.getBytes("UTF-8")); out.write(0); out.write(password.getBytes("UTF-8")); return Base64.encodeToString(out.toByteArray(), false); } }