FileDocCategorySizeDatePackage
SMTPTransport.javaAPI DocJavaMail 1.4.365877Tue Nov 17 10:38:12 GMT 2009com.sun.mail.smtp

SMTPTransport

public class SMTPTransport extends Transport
This class implements the Transport abstract class using SMTP for message submission and transport.

See the com.sun.mail.smtp package documentation for further information on the SMTP protocol provider.

This class includes many protected methods that allow a subclass to extend this class and add support for non-standard SMTP commands. The {@link #issueCommand} and {@link #sendCommand} methods can be used to send simple SMTP commands. Other methods such as the {@link #mailFrom} and {@link #data} methods can be overridden to insert new commands before or after the corresponding SMTP commands. For example, a subclass could do this to send the XACT command before sending the DATA command:

protected OutputStream data() throws MessagingException {
if (supportsExtension("XACCOUNTING"))
issueCommand("XACT", 25);
return super.data();
}
author
Max Spivak
author
Bill Shannon
author
Dean Gibson (DIGEST-MD5 authentication)
author
Luís Serralheiro (NTLM authentication)
see
javax.mail.event.ConnectionEvent
see
javax.mail.event.TransportEvent

Fields Summary
private String
name
private int
defaultPort
private boolean
isSSL
private String
host
private MimeMessage
message
private Address[]
addresses
private Address[]
validSentAddr
private Address[]
validUnsentAddr
private Address[]
invalidAddr
private boolean
sendPartiallyFailed
private MessagingException
exception
private SMTPOutputStream
dataStream
private Hashtable
extMap
private Map
authenticators
private String
defaultAuthenticationMechanisms
private boolean
quitWait
private String
saslRealm
private String
ntlmDomain
private boolean
reportSuccess
private boolean
useStartTLS
private boolean
requireStartTLS
private boolean
useRset
private boolean
noopStrict
private PrintStream
out
private String
localHostName
private String
lastServerResponse
private int
lastReturnCode
private boolean
notificationDone
private static final String[]
ignoreList
Headers that should not be included when sending
private static final byte[]
CRLF
private static final String
UNKNOWN
private BufferedInputStream
serverInput
private LineInputStream
lineInputStream
private OutputStream
serverOutput
private Socket
serverSocket
private static char[]
hexchar
Constructors Summary
public SMTPTransport(Session session, URLName urlname)
Constructor that takes a Session object and a URLName that represents a specific SMTP server.

	// place holder

                        
         
	this(session, urlname, "smtp", false);
    
protected SMTPTransport(Session session, URLName urlname, String name, boolean isSSL)
Constructor used by this class and by SMTPSSLTransport subclass.

	super(session, urlname);
	if (urlname != null)
	    name = urlname.getProtocol();
	this.name = name;
	if (!isSSL)
	    isSSL = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".ssl.enable", false);
	if (isSSL)
	    this.defaultPort = 465;
	else
	    this.defaultPort = 25;
	this.isSSL = isSSL;

	out = session.getDebugOut();

	// setting mail.smtp.quitwait to false causes us to not wait for the
	// response from the QUIT command
	quitWait = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".quitwait", true);

	// mail.smtp.reportsuccess causes us to throw an exception on success
	reportSuccess = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".reportsuccess", false);

	// mail.smtp.starttls.enable enables use of STARTTLS command
	useStartTLS = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".starttls.enable", false);

	// mail.smtp.starttls.required requires use of STARTTLS command
	requireStartTLS = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".starttls.required", false);

	// mail.smtp.userset causes us to use RSET instead of NOOP
	// for isConnected
	useRset = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".userset", false);

	// mail.smtp.noop.strict requires 250 response to indicate success
	noopStrict = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".noop.strict", true);

	// created here, because they're inner classes that reference "this"
	Authenticator[] a = new Authenticator[] {
	    new LoginAuthenticator(),
	    new PlainAuthenticator(),
	    new DigestMD5Authenticator(),
	    new NtlmAuthenticator()
	};
	StringBuffer sb = new StringBuffer();
	for (int i = 0; i < a.length; i++) {
	    authenticators.put(a[i].getMechanism(), a[i]);
	    sb.append(a[i].getMechanism()).append(' ");
	}
	defaultAuthenticationMechanisms = sb.toString();
    
Methods Summary
private voidaddressesFailed()
The send failed, fix the address arrays to report the failure correctly.

	if (validSentAddr != null) {
	    if (validUnsentAddr != null) {
		Address newa[] =
		    new Address[validSentAddr.length + validUnsentAddr.length];
		System.arraycopy(validSentAddr, 0,
			newa, 0, validSentAddr.length);
		System.arraycopy(validUnsentAddr, 0,
			newa, validSentAddr.length, validUnsentAddr.length);
		validSentAddr = null;
		validUnsentAddr = newa;
	    } else {
		validUnsentAddr = validSentAddr;
		validSentAddr = null;
	    }
	}
    
protected voidcheckConnected()
Check if we're in the connected state. Don't bother checking whether the server is still alive, that will be detected later.

exception
IllegalStateException if not connected
since
JavaMail 1.4.1

	if (!super.isConnected())
	    throw new IllegalStateException("Not connected");
    
public synchronized voidclose()
Close the Transport and terminate the connection to the server.

	if (!super.isConnected()) // Already closed.
	    return;
	try {
	    if (serverSocket != null) {
		sendCommand("QUIT");
		if (quitWait) {
		    int resp = readServerResponse();
		    if (resp != 221 && resp != -1 && debug)
			out.println("DEBUG SMTP: QUIT failed with " + resp);
		}
	    }
	} finally {
	    closeConnection();
	}
    
private voidcloseConnection()

	try {
	    if (serverSocket != null)
		serverSocket.close();
	} catch (IOException ioex) {	    // shouldn't happen
	    throw new MessagingException("Server Close Failed", ioex);
	} finally {
	    serverSocket = null;
	    serverOutput = null;
	    serverInput = null;
	    lineInputStream = null;
	    if (super.isConnected())	// only notify if already connected
		super.close();
	}
    
public synchronized voidconnect(java.net.Socket socket)
Start the SMTP protocol on the given socket, which was already connected by the caller. Useful for implementing the SMTP ATRN command (RFC 2645) where an existing connection is used when the server reverses roles and becomes the client.

since
JavaMail 1.3.3

	serverSocket = socket;
	super.connect();
    
private booleanconvertTo8Bit(javax.mail.internet.MimePart part)
If the Part is a text part and has a Content-Transfer-Encoding of "quoted-printable" or "base64", and it obeys the rules for "8bit" encoding, change the encoding to "8bit". If the part is a multipart, recursively process all its parts.

return
true if any changes were made XXX - This is really quite a hack.

	boolean changed = false;
	try {
	    if (part.isMimeType("text/*")) {
		String enc = part.getEncoding();
		if (enc != null && (enc.equalsIgnoreCase("quoted-printable") ||
		    enc.equalsIgnoreCase("base64"))) {
		    InputStream is = null;
		    try {
			is = part.getInputStream();
			if (is8Bit(is)) {
			    /*
			     * If the message was created using an InputStream
			     * then we have to extract the content as an object
			     * and set it back as an object so that the content
			     * will be re-encoded.
			     *
			     * If the message was not created using an
			     * InputStream, the following should have no effect.
			     */
			    part.setContent(part.getContent(),
					    part.getContentType());
			    part.setHeader("Content-Transfer-Encoding", "8bit");
			    changed = true;
			}
		    } finally {
			if (is != null) {
			    try {
				is.close();
			    } catch (IOException ex2) {
				// ignore it
			    }
			}
		    }
		}
	    } else if (part.isMimeType("multipart/*")) {
		MimeMultipart mp = (MimeMultipart)part.getContent();
		int count = mp.getCount();
		for (int i = 0; i < count; i++) {
		    if (convertTo8Bit((MimePart)mp.getBodyPart(i)))
			changed = true;
		}
	    }
	} catch (IOException ioex) {
	    // any exception causes us to give up
	} catch (MessagingException mex) {
	    // any exception causes us to give up
	}
	return changed;
    
protected java.io.OutputStreamdata()
Send the DATA command to the SMTP host and return an OutputStream to which the data is to be written.

since
JavaMail 1.4.1

	assert Thread.holdsLock(this);
	issueSendCommand("DATA", 354);
	dataStream = new SMTPOutputStream(serverOutput);
	return dataStream;
    
protected booleanehlo(java.lang.String domain)
Issue the EHLO command. Collect the returned list of service extensions.

param
domain our domain
return
true if command succeeds
since
JavaMail 1.4.1

	String cmd;
	if (domain != null)
	    cmd = "EHLO " + domain;
	else
	    cmd = "EHLO";
	sendCommand(cmd);
	int resp = readServerResponse();
	if (resp == 250) {
	    // extract the supported service extensions
	    BufferedReader rd =
		new BufferedReader(new StringReader(lastServerResponse));
	    String line;
	    extMap = new Hashtable();
	    try {
		boolean first = true;
		while ((line = rd.readLine()) != null) {
		    if (first) {	// skip first line which is the greeting
			first = false;
			continue;
		    }
		    if (line.length() < 5)
			continue;		// shouldn't happen
		    line = line.substring(4);	// skip response code
		    int i = line.indexOf(' ");
		    String arg = "";
		    if (i > 0) {
			arg = line.substring(i + 1);
			line = line.substring(0, i);
		    }
		    if (debug)
			out.println("DEBUG SMTP: Found extension \"" +
					    line + "\", arg \"" + arg + "\"");
		    extMap.put(line.toUpperCase(Locale.ENGLISH), arg);
		}
	    } catch (IOException ex) { }	// can't happen
	}
	return resp == 250;
    
private voidexpandGroups()
Expand any group addresses.

	Vector groups = null;
	for (int i = 0; i < addresses.length; i++) {
	    InternetAddress a = (InternetAddress)addresses[i];
	    if (a.isGroup()) {
		if (groups == null) {
		    // first group, catch up with where we are
		    groups = new Vector();
		    for (int k = 0; k < i; k++)
			groups.addElement(addresses[k]);
		}
		// parse it and add each individual address
		try {
		    InternetAddress[] ia = a.getGroup(true);
		    if (ia != null) {
			for (int j = 0; j < ia.length; j++)
			    groups.addElement(ia[j]);
		    } else
			groups.addElement(a);
		} catch (ParseException pex) {
		    // parse failed, add the whole thing
		    groups.addElement(a);
		}
	    } else {
		// if we've started accumulating a list, add this to it
		if (groups != null)
		    groups.addElement(a);
	    }
	}

	// if we have a new list, convert it back to an array
	if (groups != null) {
	    InternetAddress[] newa = new InternetAddress[groups.size()];
	    groups.copyInto(newa);
	    addresses = newa;
	}
    
protected voidfinalize()

	super.finalize();
	try {
	    closeConnection();
	} catch (MessagingException mex) { }	// ignore it
    
protected voidfinishData()
Terminate the sent data.

since
JavaMail 1.4.1

	assert Thread.holdsLock(this);
	dataStream.ensureAtBOL();
	issueSendCommand(".", 250);
    
public java.lang.StringgetExtensionParameter(java.lang.String ext)
Return the parameter the server provided for the specified service extension, or null if the extension isn't supported.

param
ext the service extension name
return
the extension parameter
since
JavaMail 1.3.2

	return extMap == null ? null :
			(String)extMap.get(ext.toUpperCase(Locale.ENGLISH));
    
public synchronized intgetLastReturnCode()
Return the return code from the last response we got from the server.

return
return code from last response from server
since
JavaMail 1.4.1

	return lastReturnCode;
    
public synchronized java.lang.StringgetLastServerResponse()
Return the last response we got from the server. A failed send is often followed by an RSET command, but the response from the RSET command is not saved. Instead, this returns the response from the command before the RSET command.

return
last response from server
since
JavaMail 1.3.2

	return lastServerResponse;
    
public synchronized java.lang.StringgetLocalHost()
Get the name of the local host, for use in the EHLO and HELO commands. The property mail.smtp.localhost overrides mail.smtp.localaddress, which overrides what InetAddress would tell us.

	// get our hostname and cache it for future use
	if (localHostName == null || localHostName.length() <= 0)
	    localHostName =
		    session.getProperty("mail." + name + ".localhost");
	if (localHostName == null || localHostName.length() <= 0)
	    localHostName =
		    session.getProperty("mail." + name + ".localaddress");
	try {
	    if (localHostName == null || localHostName.length() <= 0) {
		InetAddress localHost = InetAddress.getLocalHost();
		localHostName = localHost.getCanonicalHostName();
		// if we can't get our name, use local address literal
		if (localHostName == null)
		    // XXX - not correct for IPv6
		    localHostName = "[" + localHost.getHostAddress() + "]";
	    }
	} catch (UnknownHostException uhex) {
	}

	// last chance, try to get our address from our socket
	if (localHostName == null || localHostName.length() <= 0) {
	    if (serverSocket != null && serverSocket.isBound()) {
		InetAddress localHost = serverSocket.getLocalAddress();
		localHostName = localHost.getCanonicalHostName();
		// if we can't get our name, use local address literal
		if (localHostName == null)
		    // XXX - not correct for IPv6
		    localHostName = "[" + localHost.getHostAddress() + "]";
	    }
	}
	return localHostName;
    
public synchronized java.lang.StringgetNTLMDomain()
Gets the NTLM domain to be used for NTLM authentication.

return
the name of the domain to use for NTLM authentication.
since
JavaMail 1.4.3

	if (ntlmDomain == UNKNOWN) {
	    ntlmDomain =
		session.getProperty("mail." + name + ".auth.ntlm.domain");
	}
	return ntlmDomain;
    
public synchronized booleangetNoopStrict()
Is the NOOP command required to return a response code of 250 to indicate success?

return
true if NOOP must return 250
since
JavaMail 1.4.3

	return noopStrict;
    
public synchronized booleangetReportSuccess()
Should we report even successful sends by throwing an exception? If so, a SendFailedException will always be thrown and an {@link com.sun.mail.smtp.SMTPAddressSucceededException SMTPAddressSucceededException} will be included in the exception chain for each successful address, along with the usual {@link com.sun.mail.smtp.SMTPAddressFailedException SMTPAddressFailedException} for each unsuccessful address.

return
true if an exception will be thrown on successful sends.
since
JavaMail 1.3.2

	return reportSuccess;
    
public synchronized booleangetRequireStartTLS()
Should we require the STARTTLS command to secure the connection?

return
true if the STARTTLS command will be required
since
JavaMail 1.4.2

	return requireStartTLS;
    
public synchronized java.lang.StringgetSASLRealm()
Gets the SASL realm to be used for DIGEST-MD5 authentication.

return
the name of the realm to use for SASL authentication.
since
JavaMail 1.3.1

	if (saslRealm == UNKNOWN) {
	    saslRealm = session.getProperty("mail." + name + ".sasl.realm");
	    if (saslRealm == null)	// try old name
		saslRealm = session.getProperty("mail." + name + ".saslrealm");
	}
	return saslRealm;
    
public synchronized booleangetStartTLS()
Should we use the STARTTLS command to secure the connection if the server supports it?

return
true if the STARTTLS command will be used
since
JavaMail 1.3.2

	return useStartTLS;
    
public synchronized booleangetUseRset()
Should we use the RSET command instead of the NOOP command in the @{link #isConnected isConnected} method?

return
true if RSET will be used
since
JavaMail 1.4

	return useRset;
    
protected voidhelo(java.lang.String domain)
Issue the HELO command.

param
domain our domain
since
JavaMail 1.4.1

	if (domain != null)
	    issueCommand("HELO " + domain, 250);
	else
	    issueCommand("HELO", 250);
    
private voidinitStreams()

	PrintStream out = session.getDebugOut();
	boolean debug = session.getDebug();
	boolean quote = PropUtil.getBooleanSessionProperty(session,
					"mail.debug.quote", false);

	TraceInputStream traceInput =
	    new TraceInputStream(serverSocket.getInputStream(), out);
	traceInput.setTrace(debug);
	traceInput.setQuote(quote);

	TraceOutputStream traceOutput =
	    new TraceOutputStream(serverSocket.getOutputStream(), out);
	traceOutput.setTrace(debug);
	traceOutput.setQuote(quote);

	serverOutput =
	    new BufferedOutputStream(traceOutput);
	serverInput =
	    new BufferedInputStream(traceInput);
	lineInputStream = new LineInputStream(serverInput);
    
private booleanis8Bit(java.io.InputStream is)
Check whether the data in the given InputStream follows the rules for 8bit text. Lines have to be 998 characters or less and no NULs are allowed. CR and LF must occur in pairs but we don't check that because we assume this is text and we convert all CR/LF combinations into canonical CRLF later.

	int b;
	int linelen = 0;
	boolean need8bit = false;
	try {
	    while ((b = is.read()) >= 0) {
		b &= 0xff;
		if (b == '\r" || b == '\n")
		    linelen = 0;
		else if (b == 0)
		    return false;
		else {
		    linelen++;
		    if (linelen > 998)	// 1000 - CRLF
			return false;
		}
		if (b > 0x7f)
		    need8bit = true;
	    }
	} catch (IOException ex) {
	    return false;
	}
	if (debug && need8bit)
	    out.println("DEBUG SMTP: found an 8bit part");
	return need8bit;
    
public synchronized booleanisConnected()
Check whether the transport is connected. Override superclass method, to actually ping our server connection.

	if (!super.isConnected())
	    // if we haven't been connected at all, don't bother with NOOP
	    return false;

	try {
	    // sendmail may respond slowly to NOOP after many requests
	    // so if mail.smtp.userset is set we use RSET instead of NOOP.
	    if (useRset)
		sendCommand("RSET");
	    else
		sendCommand("NOOP");
	    int resp = readServerResponse();

	    /*
	     * NOOP should return 250 on success, however, SIMS 3.2 returns
	     * 200, so we work around it.
	     *
	     * Hotmail didn't used to implement the NOOP command at all so
	     * assume any kind of response means we're still connected.
	     * That is, any response except 421, which means the server
	     * is shutting down the connection.
	     *
	     * Some versions of Exchange return 451 instead of 421 when
	     * timing out a connection.
	     *
	     * Argh!
	     *
	     * If mail.smtp.noop.strict is set to false, be tolerant of
	     * servers that return the wrong response code for success.
	     */
	    if (resp >= 0 && (noopStrict ? resp == 250 : resp != 421)) {
		return true;
	    } else {
		try {
		    closeConnection();
		} catch (MessagingException mex) { }	// ignore it
		return false;
	    }
	} catch (Exception ex) {
	    try {
		closeConnection();
	    } catch (MessagingException mex) { }	// ignore it
	    return false;
	}
    
private booleanisNotLastLine(java.lang.String line)

        return line != null && line.length() >= 4 && line.charAt(3) == '-";
    
public synchronized voidissueCommand(java.lang.String cmd, int expect)
Send the command to the server. If the expected response code is not received, throw a MessagingException.

param
cmd the command to send
param
expect the expected response code (-1 means don't care)
since
JavaMail 1.4.1

	sendCommand(cmd);

	// if server responded with an unexpected return code,
	// throw the exception, notifying the client of the response
	int resp = readServerResponse();
	if (expect != -1 && resp != expect)
	    throw new MessagingException(lastServerResponse);
    
private voidissueSendCommand(java.lang.String cmd, int expect)
Issue a command that's part of sending a message.

	sendCommand(cmd);

	// if server responded with an unexpected return code,
	// throw the exception, notifying the client of the response
	int ret;
	if ((ret = readServerResponse()) != expect) {
	    // assume message was not sent to anyone,
	    // combine valid sent & unsent addresses
	    int vsl = validSentAddr == null ? 0 : validSentAddr.length;
	    int vul = validUnsentAddr == null ? 0 : validUnsentAddr.length;
	    Address[] valid = new Address[vsl + vul];
	    if (vsl > 0)
		System.arraycopy(validSentAddr, 0, valid, 0, vsl);
	    if (vul > 0)
		System.arraycopy(validUnsentAddr, 0, valid, vsl, vul);
	    validSentAddr = null;
	    validUnsentAddr = valid;
	    if (debug)
		out.println("DEBUG SMTP: got response code " + ret +
		    ", with response: " + lastServerResponse);
	    String _lsr = lastServerResponse; // else rset will nuke it
	    int _lrc = lastReturnCode;
	    if (serverSocket != null)	// hasn't already been closed
		issueCommand("RSET", -1);
	    lastServerResponse = _lsr;	// restore, for get
	    lastReturnCode = _lrc;
	    throw new SMTPSendFailedException(cmd, ret, lastServerResponse,
			exception, validSentAddr, validUnsentAddr, invalidAddr);
	}
    
protected voidmailFrom()
Issue the MAIL FROM: command to start sending a message.

Gets the sender's address in the following order:

  1. SMTPMessage.getEnvelopeFrom()
  2. mail.smtp.from property
  3. From: header in the message
  4. System username using the InternetAddress.getLocalAddress() method

since
JavaMail 1.4.1

	String from = null;
	if (message instanceof SMTPMessage)
	    from = ((SMTPMessage)message).getEnvelopeFrom();
	if (from == null || from.length() <= 0)
	    from = session.getProperty("mail." + name + ".from");
	if (from == null || from.length() <= 0) {
	    Address[] fa;
	    Address me;
	    if (message != null && (fa = message.getFrom()) != null &&
		    fa.length > 0)
		me = fa[0];
	    else
		me = InternetAddress.getLocalAddress(session);

	    if (me != null)
		from = ((InternetAddress)me).getAddress();
	    else
		throw new MessagingException(
					"can't determine local email address");
	}

	String cmd = "MAIL FROM:" + normalizeAddress(from);

	// request delivery status notification?
	if (supportsExtension("DSN")) {
	    String ret = null;
	    if (message instanceof SMTPMessage)
		ret = ((SMTPMessage)message).getDSNRet();
	    if (ret == null)
		ret = session.getProperty("mail." + name + ".dsn.ret");
	    // XXX - check for legal syntax?
	    if (ret != null)
		cmd += " RET=" + ret;
	}

	/*
	 * If an RFC 2554 submitter has been specified, and the server
	 * supports the AUTH extension, include the AUTH= element on
	 * the MAIL FROM command.
	 */
	if (supportsExtension("AUTH")) {
	    String submitter = null;
	    if (message instanceof SMTPMessage)
		submitter = ((SMTPMessage)message).getSubmitter();
	    if (submitter == null)
		submitter = session.getProperty("mail." + name + ".submitter");
	    // XXX - check for legal syntax?
	    if (submitter != null) {
		try {
		    String s = xtext(submitter);
		    cmd += " AUTH=" + s;
		} catch (IllegalArgumentException ex) {
		    if (debug)
			out.println("DEBUG SMTP: ignoring invalid submitter: " +
			    submitter + ", Exception: " + ex);
		}
	    }
	}

	/*
	 * Have any extensions to the MAIL command been specified?
	 */
	String ext = null;
	if (message instanceof SMTPMessage)
	    ext = ((SMTPMessage)message).getMailExtension();
	if (ext == null)
	    ext = session.getProperty("mail." + name + ".mailextension");
	if (ext != null && ext.length() > 0)
	    cmd += " " + ext;

	issueSendCommand(cmd, 250);
    
private java.lang.StringnormalizeAddress(java.lang.String addr)

	if ((!addr.startsWith("<")) && (!addr.endsWith(">")))
	    return "<" + addr + ">";
	else
	    return addr;
    
protected voidnotifyTransportListeners(int type, javax.mail.Address[] validSent, javax.mail.Address[] validUnsent, javax.mail.Address[] invalid, Message msg)
Notify all TransportListeners. Keep track of whether notification has been done so as to only notify once per send.

since
JavaMail 1.4.2


	if (!notificationDone) {
	    super.notifyTransportListeners(type, validSent, validUnsent,
		invalid, msg);
	    notificationDone = true;
	}
    
private voidopenServer(java.lang.String host, int port)
Connect to host on port and start the SMTP protocol.


        if (debug)
	    out.println("DEBUG SMTP: trying to connect to host \"" + host +
				"\", port " + port + ", isSSL " + isSSL);

	try {
	    Properties props = session.getProperties();

	    serverSocket = SocketFetcher.getSocket(host, port,
		props, "mail." + name, isSSL);

	    // socket factory may've chosen a different port,
	    // update it for the debug messages that follow
	    port = serverSocket.getPort();
	    // save host name for startTLS
	    this.host = host;

	    initStreams();

	    int r = -1;
	    if ((r = readServerResponse()) != 220) {
		serverSocket.close();
		serverSocket = null;
		serverOutput = null;
		serverInput = null;
		lineInputStream = null;
		if (debug)
		    out.println("DEBUG SMTP: could not connect to host \"" +
				    host + "\", port: " + port +
				    ", response: " + r + "\n");
		throw new MessagingException(
			"Could not connect to SMTP host: " + host +
				    ", port: " + port +
				    ", response: " + r);
	    } else {
		if (debug)
		    out.println("DEBUG SMTP: connected to host \"" +
				       host + "\", port: " + port + "\n");
	    }
	} catch (UnknownHostException uhex) {
	    throw new MessagingException("Unknown SMTP host: " + host, uhex);
	} catch (IOException ioe) {
	    throw new MessagingException("Could not connect to SMTP host: " +
				    host + ", port: " + port, ioe);
	}
    
private voidopenServer()
Start the protocol to the server on serverSocket, assumed to be provided and connected by the caller.

	int port = -1;
	host = "UNKNOWN";
	try {
	    port = serverSocket.getPort();
	    host = serverSocket.getInetAddress().getHostName();
	    if (debug)
		out.println("DEBUG SMTP: starting protocol to host \"" +
					host + "\", port " + port);

	    initStreams();

	    int r = -1;
	    if ((r = readServerResponse()) != 220) {
		serverSocket.close();
		serverSocket = null;
		serverOutput = null;
		serverInput = null;
		lineInputStream = null;
		if (debug)
		    out.println("DEBUG SMTP: got bad greeting from host \"" +
				    host + "\", port: " + port +
				    ", response: " + r + "\n");
		throw new MessagingException(
			"Got bad greeting from SMTP host: " + host +
				    ", port: " + port +
				    ", response: " + r);
	    } else {
		if (debug)
		    out.println("DEBUG SMTP: protocol started to host \"" +
				       host + "\", port: " + port + "\n");
	    }
	} catch (IOException ioe) {
	    throw new MessagingException(
				    "Could not start protocol to SMTP host: " +
				    host + ", port: " + port, ioe);
	}
    
protected booleanprotocolConnect(java.lang.String host, int port, java.lang.String user, java.lang.String passwd)
Performs the actual protocol-specific connection attempt. Will attempt to connect to "localhost" if the host was null.

Unless mail.smtp.ehlo is set to false, we'll try to identify ourselves using the ESMTP command EHLO. If mail.smtp.auth is set to true, we insist on having a username and password, and will try to authenticate ourselves if the server supports the AUTH extension (RFC 2554).

param
host the name of the host to connect to
param
port the port to use (-1 means use default port)
param
user the name of the user to login as
param
passwd the user's password
return
true if connection successful, false if authentication failed
exception
MessagingException for non-authentication failures

	// setting mail.smtp.ehlo to false disables attempts to use EHLO
	boolean useEhlo =  PropUtil.getBooleanSessionProperty(session,
					"mail." + name + ".ehlo", true);
	// setting mail.smtp.auth to true enables attempts to use AUTH
	boolean useAuth = PropUtil.getBooleanSessionProperty(session,
					"mail." + name + ".auth", false);
	// setting mail.smtp.auth.mechanisms controls which mechanisms will
	// be used, and in what order they'll be considered.  only the first
	// match is used.
	String mechs = session.getProperty("mail." + name + ".auth.mechanisms");
	if (mechs == null)
	    mechs = defaultAuthenticationMechanisms;

	if (debug)
	    out.println("DEBUG SMTP: useEhlo " + useEhlo +
				", useAuth " + useAuth);

	/*
	 * If mail.smtp.auth is set, make sure we have a valid username
	 * and password, even if we might not end up using it (e.g.,
	 * because the server doesn't support ESMTP or doesn't support
	 * the AUTH extension).
	 */
	if (useAuth && (user == null || passwd == null))
	    return false;

	/*
	 * If port is not specified, set it to value of mail.smtp.port
         * property if it exists, otherwise default to 25.
	 */
        if (port == -1)
	    port = PropUtil.getIntSessionProperty(session,
					"mail." + name + ".port", -1);
        if (port == -1)
	    port = defaultPort;

	if (host == null || host.length() == 0)
	    host = "localhost";

	boolean succeed = false;

	if (serverSocket != null)
	    openServer();	// only happens from connect(socket)
	else
	    openServer(host, port);

	if (useEhlo)
	    succeed = ehlo(getLocalHost());
	if (!succeed)
	    helo(getLocalHost());

	if (useStartTLS || requireStartTLS) {
	    if (supportsExtension("STARTTLS")) {
		startTLS();
		/*
		 * Have to issue another EHLO to update list of extensions
		 * supported, especially authentication mechanisms.
		 * Don't know if this could ever fail, but we ignore failure.
		 */
		ehlo(getLocalHost());
	    } else if (requireStartTLS) {
		if (debug)
		    out.println(
			"DEBUG SMTP: STARTTLS required but not supported");
		try {
		    closeConnection();
		} catch (MessagingException mex) { /* ignore it */ }
		throw new MessagingException(
		    "STARTTLS is required but host does not support STARTTLS");
	    }
	}

	if ((useAuth || (user != null && passwd != null)) &&
	      (supportsExtension("AUTH") || supportsExtension("AUTH=LOGIN"))) {
	    if (debug) {
		out.println("DEBUG SMTP: Attempt to authenticate");
		out.println("DEBUG SMTP: check mechanisms: " + mechs);
	    }

	    /*
	     * Loop through the list of mechanisms supplied by the user
	     * (or defaulted) and try each in turn.  If the server supports
	     * the mechanism and we have an authenticator for the mechanism,
	     * use it.
	     */
	    StringTokenizer st = new StringTokenizer(mechs);
	    while (st.hasMoreTokens()) {
		String m = st.nextToken().toUpperCase(Locale.ENGLISH);
		if (!supportsAuthentication(m)) {
		    if (debug)
			out.println("DEBUG SMTP: mechanism " + m +
						" not supported by server");
		    continue;
		}
		Authenticator a = (Authenticator)authenticators.get(m);
		if (a == null) {
		    if (debug)
			out.println("DEBUG SMTP: " +
			    "no authenticator for mechanism " + m);
		    continue;
		}
		// only first supported mechanism is used
		return a.authenticate(host, user, passwd);
	    }

	    // if no authentication mechanism found, close connection and fail
	    try {
		closeConnection();
	    } catch (MessagingException mex) { /* ignore it */ }
	    throw new AuthenticationFailedException(
		"No authentication mechansims supported by both " +
		"server and client");
	}

	// we connected correctly
	return true;
    
protected voidrcptTo()
Sends each address to the SMTP host using the RCPT TO: command and copies the address either into the validSentAddr or invalidAddr arrays. Sets the sendFailed flag to true if any addresses failed.

since
JavaMail 1.4.1

	Vector valid = new Vector();
	Vector validUnsent = new Vector();
	Vector invalid = new Vector();
	int retCode = -1;
	MessagingException mex = null;
	boolean sendFailed = false;
	MessagingException sfex = null;
	validSentAddr = validUnsentAddr = invalidAddr = null;
	boolean sendPartial = false;
	if (message instanceof SMTPMessage)
	    sendPartial = ((SMTPMessage)message).getSendPartial();
	if (!sendPartial)
	    sendPartial = PropUtil.getBooleanSessionProperty(session,
					"mail." + name + ".sendpartial", false);
	if (debug && sendPartial)
	    out.println("DEBUG SMTP: sendPartial set");

	boolean dsn = false;
	String notify = null;
	if (supportsExtension("DSN")) {
	    if (message instanceof SMTPMessage)
		notify = ((SMTPMessage)message).getDSNNotify();
	    if (notify == null)
		notify = session.getProperty("mail." + name + ".dsn.notify");
	    // XXX - check for legal syntax?
	    if (notify != null)
		dsn = true;
	}

	// try the addresses one at a time
	for (int i = 0; i < addresses.length; i++) {

	    sfex = null;
	    InternetAddress ia = (InternetAddress)addresses[i];
	    String cmd = "RCPT TO:" + normalizeAddress(ia.getAddress());
	    if (dsn)
		cmd += " NOTIFY=" + notify;
	    // send the addresses to the SMTP server
	    sendCommand(cmd);
	    // check the server's response for address validity
	    retCode = readServerResponse();
	    switch (retCode) {
	    case 250: case 251:
		valid.addElement(ia);
		if (!reportSuccess)
		    break;

		// user wants exception even when successful, including
		// details of the return code

		// create and chain the exception
		sfex = new SMTPAddressSucceededException(ia, cmd, retCode,
							lastServerResponse);
		if (mex == null)
		    mex = sfex;
		else
		    mex.setNextException(sfex);
		break;

	    case 550: case 553: case 503: case 551: case 501:
		// given address is invalid
		if (!sendPartial)
		    sendFailed = true;
		invalid.addElement(ia);
		// create and chain the exception
		sfex = new SMTPAddressFailedException(ia, cmd, retCode,
							lastServerResponse);
		if (mex == null)
		    mex = sfex;
		else
		    mex.setNextException(sfex);
		break;

	    case 552: case 450: case 451: case 452:
		// given address is valid
		if (!sendPartial)
		    sendFailed = true;
		validUnsent.addElement(ia);
		// create and chain the exception
		sfex = new SMTPAddressFailedException(ia, cmd, retCode,
							lastServerResponse);
		if (mex == null)
		    mex = sfex;
		else
		    mex.setNextException(sfex);
		break;

	    default:
		// handle remaining 4xy & 5xy codes
		if (retCode >= 400 && retCode <= 499) {
		    // assume address is valid, although we don't really know
		    validUnsent.addElement(ia);
		} else if (retCode >= 500 && retCode <= 599) {
		    // assume address is invalid, although we don't really know
		    invalid.addElement(ia);
		} else {
		    // completely unexpected response, just give up
		    if (debug)
			out.println("DEBUG SMTP: got response code " + retCode +
			    ", with response: " + lastServerResponse);
		    String _lsr = lastServerResponse; // else rset will nuke it
		    int _lrc = lastReturnCode;
		    if (serverSocket != null)	// hasn't already been closed
			issueCommand("RSET", -1);
		    lastServerResponse = _lsr;	// restore, for get
		    lastReturnCode = _lrc;
		    throw new SMTPAddressFailedException(ia, cmd, retCode,
								_lsr);
		}
		if (!sendPartial)
		    sendFailed = true;
		// create and chain the exception
		sfex = new SMTPAddressFailedException(ia, cmd, retCode,
							lastServerResponse);
		if (mex == null)
		    mex = sfex;
		else
		    mex.setNextException(sfex);
		break;
	    }
	}

	// if we're willing to send to a partial list, and we found no
	// valid addresses, that's complete failure
	if (sendPartial && valid.size() == 0)
	    sendFailed = true;

	// copy the vectors into appropriate arrays
	if (sendFailed) {
	    // copy invalid addrs
	    invalidAddr = new Address[invalid.size()];
	    invalid.copyInto(invalidAddr);

	    // copy all valid addresses to validUnsent, since something failed
	    validUnsentAddr = new Address[valid.size() + validUnsent.size()];
	    int i = 0;
	    for (int j = 0; j < valid.size(); j++)
		validUnsentAddr[i++] = (Address)valid.elementAt(j);
	    for (int j = 0; j < validUnsent.size(); j++)
		validUnsentAddr[i++] = (Address)validUnsent.elementAt(j);
	} else if (reportSuccess || (sendPartial &&
			(invalid.size() > 0 || validUnsent.size() > 0))) {
	    // we'll go on to send the message, but after sending we'll
	    // throw an exception with this exception nested
	    sendPartiallyFailed = true;
	    exception = mex;

	    // copy invalid addrs
	    invalidAddr = new Address[invalid.size()];
	    invalid.copyInto(invalidAddr);

	    // copy valid unsent addresses to validUnsent
	    validUnsentAddr = new Address[validUnsent.size()];
	    validUnsent.copyInto(validUnsentAddr);

	    // copy valid addresses to validSent
	    validSentAddr = new Address[valid.size()];
	    valid.copyInto(validSentAddr);
	} else {        // all addresses pass
	    validSentAddr = addresses;
	}


	// print out the debug info
	if (debug) {
	    if (validSentAddr != null && validSentAddr.length > 0) {
		out.println("DEBUG SMTP: Verified Addresses");
		for (int l = 0; l < validSentAddr.length; l++) {
		    out.println("DEBUG SMTP:   " + validSentAddr[l]);
		}
	    }
	    if (validUnsentAddr != null && validUnsentAddr.length > 0) {
		out.println("DEBUG SMTP: Valid Unsent Addresses");
		for (int j = 0; j < validUnsentAddr.length; j++) {
		    out.println("DEBUG SMTP:   " + validUnsentAddr[j]);
		}
	    }
	    if (invalidAddr != null && invalidAddr.length > 0) {
		out.println("DEBUG SMTP: Invalid Addresses");
		for (int k = 0; k < invalidAddr.length; k++) {
		    out.println("DEBUG SMTP:   " + invalidAddr[k]);
		}
	    }
	}

	// throw the exception, fire TransportEvent.MESSAGE_NOT_DELIVERED event
	if (sendFailed) {
	    if (debug)
		out.println("DEBUG SMTP: Sending failed " +
				   "because of invalid destination addresses");
	    notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
				     validSentAddr, validUnsentAddr,
				     invalidAddr, this.message);

	    // reset the connection so more sends are allowed
	    String lsr = lastServerResponse;	// save, for get
	    int lrc = lastReturnCode;
	    try {
		if (serverSocket != null)
		    issueCommand("RSET", -1);
	    } catch (MessagingException ex) {
		// if can't reset, best to close the connection
		try {
		    close();
		} catch (MessagingException ex2) {
		    // thrown by close()--ignore, will close() later anyway
		    if (debug)
			ex2.printStackTrace(out);
		}
	    } finally {
		lastServerResponse = lsr;	// restore
		lastReturnCode = lrc;
	    }

	    throw new SendFailedException("Invalid Addresses", mex,
					  validSentAddr,
					  validUnsentAddr, invalidAddr);
	}
    
protected intreadServerResponse()
Reads server reponse returning the returnCode as the number. Returns -1 on failure. Sets lastServerResponse and lastReturnCode.

return
server response code
since
JavaMail 1.4.1

	assert Thread.holdsLock(this);
        String serverResponse = "";
        int returnCode = 0;
	StringBuffer buf = new StringBuffer(100);

	// read the server response line(s) and add them to the buffer
	// that stores the response
        try {
	    String line = null;

	    do {
		line = lineInputStream.readLine();
		if (line == null) {
		    serverResponse = buf.toString();
		    if (serverResponse.length() == 0)
			serverResponse = "[EOF]";
		    lastServerResponse = serverResponse;
		    lastReturnCode = -1;
		    if (debug)
			out.println("DEBUG SMTP: EOF: " + serverResponse);
		    return -1;
		}
		buf.append(line);
		buf.append("\n");
	    } while (isNotLastLine(line));

            serverResponse = buf.toString();
        } catch (IOException ioex) {
	    if (debug)
		out.println("DEBUG SMTP: exception reading response: " + ioex);
            //ioex.printStackTrace(out);
	    lastServerResponse = "";
	    lastReturnCode = 0;
	    throw new MessagingException("Exception reading response", ioex);
            //returnCode = -1;
        }

	// print debug info
        //if (debug)
            //out.println("DEBUG SMTP RCVD: " + serverResponse);

	// parse out the return code
        if (serverResponse != null && serverResponse.length() >= 3) {
            try {
                returnCode = Integer.parseInt(serverResponse.substring(0, 3));
            } catch (NumberFormatException nfe) {
		try {
		    close();
		} catch (MessagingException mex) {
		    // thrown by close()--ignore, will close() later anyway
		    if (debug)
			mex.printStackTrace(out);
		}
		returnCode = -1;
            } catch (StringIndexOutOfBoundsException ex) {
		//if (debug) ex.printStackTrace(out);
		try {
		    close();
		} catch (MessagingException mex) {
		    // thrown by close()--ignore, will close() later anyway
		    if (debug)
			mex.printStackTrace(out);
		}
                returnCode = -1;
	    }
	} else {
	    returnCode = -1;
	}
	if (returnCode == -1 && debug)
	    out.println("DEBUG SMTP: bad server response: " + serverResponse);

        lastServerResponse = serverResponse;
	lastReturnCode = returnCode;
        return returnCode;
    
protected voidsendCommand(java.lang.String cmd)
Sends command cmd to the server terminating it with CRLF.

since
JavaMail 1.4.1

	sendCommand(ASCIIUtility.getBytes(cmd));
    
private voidsendCommand(byte[] cmdBytes)

	assert Thread.holdsLock(this);
	//if (debug)
	    //out.println("DEBUG SMTP SENT: " + new String(cmdBytes, 0));

        try {
	    serverOutput.write(cmdBytes);
	    serverOutput.write(CRLF);
	    serverOutput.flush();
	} catch (IOException ex) {
	    throw new MessagingException("Can't send command to SMTP host", ex);
	}
    
public synchronized voidsendMessage(Message message, javax.mail.Address[] addresses)
Send the Message to the specified list of addresses.

If all the addresses succeed the SMTP check using the RCPT TO: command, we attempt to send the message. A TransportEvent of type MESSAGE_DELIVERED is fired indicating the successful submission of a message to the SMTP host.

If some of the addresses fail the SMTP check, and the mail.stmp.sendpartial property is not set, sending is aborted. The TransportEvent of type MESSAGE_NOT_DELIVERED is fired containing the valid and invalid addresses. The SendFailedException is also thrown.

If some of the addresses fail the SMTP check, and the mail.stmp.sendpartial property is set to true, the message is sent. The TransportEvent of type MESSAGE_PARTIALLY_DELIVERED is fired containing the valid and invalid addresses. The SMTPSendFailedException is also thrown.

MessagingException is thrown if the message can't write out an RFC822-compliant stream using its writeTo method.

param
message The MimeMessage to be sent
param
addresses List of addresses to send this message to
see
javax.mail.event.TransportEvent
exception
SMTPSendFailedException if the send failed because of an SMTP command error
exception
SendFailedException if the send failed because of invalid addresses.
exception
MessagingException if the connection is dead or not in the connected state or if the message is not a MimeMessage.


	sendMessageStart(message != null ? message.getSubject() : "");
	checkConnected();

	// check if the message is a valid MIME/RFC822 message and that
	// it has all valid InternetAddresses; fail if not
        if (!(message instanceof MimeMessage)) {
	    if (debug)
		out.println("DEBUG SMTP: Can only send RFC822 msgs");
	    throw new MessagingException("SMTP can only send RFC822 messages");
	}
	for (int i = 0; i < addresses.length; i++) {
	    if (!(addresses[i] instanceof InternetAddress)) {
		throw new MessagingException(addresses[i] +
					     " is not an InternetAddress");
	    }
	}
	if (addresses.length == 0)
	    throw new SendFailedException("No recipient addresses");

	this.message = (MimeMessage)message;
	this.addresses = addresses;
	validUnsentAddr = addresses;	// until we know better
	expandGroups();

	boolean use8bit = false;
	if (message instanceof SMTPMessage)
	    use8bit = ((SMTPMessage)message).getAllow8bitMIME();
	if (!use8bit)
	    use8bit = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".allow8bitmime", false);
	if (debug)
	    out.println("DEBUG SMTP: use8bit " + use8bit);
	if (use8bit && supportsExtension("8BITMIME")) {
	    if (convertTo8Bit(this.message)) {
		// in case we made any changes, save those changes
		// XXX - this will change the Message-ID
		try {
		    this.message.saveChanges();
		} catch (MessagingException mex) {
		    // ignore it
		}
	    }
	}

	try {
	    mailFrom();
	    rcptTo();
	    this.message.writeTo(data(), ignoreList);
	    finishData();
	    if (sendPartiallyFailed) {
		// throw the exception,
		// fire TransportEvent.MESSAGE_PARTIALLY_DELIVERED event
		if (debug)
		    out.println("DEBUG SMTP: Sending partially failed " +
			"because of invalid destination addresses");
		notifyTransportListeners(
			TransportEvent.MESSAGE_PARTIALLY_DELIVERED,
			validSentAddr, validUnsentAddr, invalidAddr,
			this.message);

		throw new SMTPSendFailedException(".", lastReturnCode,
				lastServerResponse, exception,
				validSentAddr, validUnsentAddr, invalidAddr);
	    }
	    notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED,
				     validSentAddr, validUnsentAddr,
				     invalidAddr, this.message);
	} catch (MessagingException mex) {
	    if (debug)
		mex.printStackTrace(out);
	    addressesFailed();
	    notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
				     validSentAddr, validUnsentAddr,
				     invalidAddr, this.message);

	    throw mex;
	} catch (IOException ex) {
	    if (debug)
		ex.printStackTrace(out);
	    // if we catch an IOException, it means that we want
	    // to drop the connection so that the message isn't sent
	    try {
		closeConnection();
	    } catch (MessagingException mex) { /* ignore it */ }
	    addressesFailed();
	    notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
				     validSentAddr, validUnsentAddr,
				     invalidAddr, this.message);

	    throw new MessagingException("IOException while sending message",
					 ex);
	} finally {
	    // no reason to keep this data around
	    validSentAddr = validUnsentAddr = invalidAddr = null;
	    this.addresses = null;
	    this.message = null;
	    this.exception = null;
	    sendPartiallyFailed = false;
	    notificationDone = false;	// reset for next send
	}
	sendMessageEnd();
    
private voidsendMessageEnd()

 
private voidsendMessageStart(java.lang.String subject)

 
public synchronized voidsetLocalHost(java.lang.String localhost)
Set the name of the local host, for use in the EHLO and HELO commands.

since
JavaMail 1.3.1

	localHostName = localhost;
    
public synchronized voidsetNTLMDomain(java.lang.String ntlmDomain)
Sets the NTLM domain to be used for NTLM authentication.

param
ntlmDomain the name of the domain to use for NTLM authentication.
since
JavaMail 1.4.3

	this.ntlmDomain = ntlmDomain;
    
public synchronized voidsetNoopStrict(boolean noopStrict)
Set whether the NOOP command is required to return a response code of 250 to indicate success.

param
noopStrict is NOOP required to return 250?
since
JavaMail 1.4.3

	this.noopStrict = noopStrict;
    
public synchronized voidsetReportSuccess(boolean reportSuccess)
Set whether successful sends should be reported by throwing an exception.

param
reportSuccess should we throw an exception on success?
since
JavaMail 1.3.2

	this.reportSuccess = reportSuccess;
    
public synchronized voidsetRequireStartTLS(boolean requireStartTLS)
Set whether the STARTTLS command should be required.

param
requireStartTLS should we require the STARTTLS command?
since
JavaMail 1.4.2

	this.requireStartTLS = requireStartTLS;
    
public synchronized voidsetSASLRealm(java.lang.String saslRealm)
Sets the SASL realm to be used for DIGEST-MD5 authentication.

param
saslRealm the name of the realm to use for SASL authentication.
since
JavaMail 1.3.1

	this.saslRealm = saslRealm;
    
public synchronized voidsetStartTLS(boolean useStartTLS)
Set whether the STARTTLS command should be used.

param
useStartTLS should we use the STARTTLS command?
since
JavaMail 1.3.2

	this.useStartTLS = useStartTLS;
    
public synchronized voidsetUseRset(boolean useRset)
Set whether the RSET command should be used instead of the NOOP command in the @{link #isConnected isConnected} method.

param
useRset should we use the RSET command?
since
JavaMail 1.4

	this.useRset = useRset;
    
public synchronized intsimpleCommand(java.lang.String cmd)
Send the command to the server and return the response code from the server.

since
JavaMail 1.4.1

	sendCommand(cmd);
	return readServerResponse();
    
protected intsimpleCommand(byte[] cmd)
Send the command to the server and return the response code from the server.

since
JavaMail 1.4.1

	assert Thread.holdsLock(this);
	sendCommand(cmd);
	return readServerResponse();
    
protected voidstartTLS()
Issue the STARTTLS command and switch the socket to TLS mode if it succeeds.

since
JavaMail 1.4.1

	issueCommand("STARTTLS", 220);
	// it worked, now switch the socket into TLS mode
	try {
	    serverSocket = SocketFetcher.startTLS(serverSocket, host,
				session.getProperties(), "mail." + name);
	    initStreams();
	} catch (IOException ioex) {
	    closeConnection();
	    throw new MessagingException("Could not convert socket to TLS",
								ioex);
	}
    
protected booleansupportsAuthentication(java.lang.String auth)
Does the server we're connected to support the specified authentication mechanism? Uses the extension information returned by the server from the EHLO command.

param
auth the authentication mechanism
return
true if the authentication mechanism is supported
since
JavaMail 1.4.1

	assert Thread.holdsLock(this);
	if (extMap == null)
	    return false;
	String a = (String)extMap.get("AUTH");
	if (a == null)
	    return false;
	StringTokenizer st = new StringTokenizer(a);
	while (st.hasMoreTokens()) {
	    String tok = st.nextToken();
	    if (tok.equalsIgnoreCase(auth))
		return true;
	}
	// hack for buggy servers that advertise capability incorrectly
	if (auth.equalsIgnoreCase("LOGIN") && supportsExtension("AUTH=LOGIN")) {
	    out.println("DEBUG SMTP: use AUTH=LOGIN hack");
	    return true;
	}
	return false;
    
public booleansupportsExtension(java.lang.String ext)
Return true if the SMTP server supports the specified service extension. Extensions are reported as results of the EHLO command when connecting to the server. See RFC 1869 and other RFCs that define specific extensions.

param
ext the service extension name
return
true if the extension is supported
since
JavaMail 1.3.2

	return extMap != null &&
			extMap.get(ext.toUpperCase(Locale.ENGLISH)) != null;
    
protected static java.lang.Stringxtext(java.lang.String s)
Convert a string to RFC 1891 xtext format.

xtext = *( xchar / hexchar )

xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive,
except for "+" and "=".

; "hexchar"s are intended to encode octets that cannot appear
; as ASCII characters within an esmtp-value.

hexchar = ASCII "+" immediately followed by two upper case
hexadecimal digits

since
JavaMail 1.4.1


                                                                                                         
         
	StringBuffer sb = null;
	for (int i = 0; i < s.length(); i++) {
	    char c = s.charAt(i);
	    if (c >= 128)	// not ASCII
		throw new IllegalArgumentException(
		    "Non-ASCII character in SMTP submitter: " + s);
	    if (c < '!" || c > '~" || c == '+" || c == '=") {
		if (sb == null) {
		    sb = new StringBuffer(s.length() + 4);
		    sb.append(s.substring(0, i));
		}
		sb.append('+");
		sb.append(hexchar[(((int)c)& 0xf0) >> 4]);
		sb.append(hexchar[((int)c)& 0x0f]);
	    } else {
		if (sb != null)
		    sb.append(c);
	    }
	}
	return sb != null ? sb.toString() : s;