📄 smtptransport.java
字号:
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* @(#)SMTPTransport.java 1.89 07/07/03
*/
package com.sun.mail.smtp;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.*;
import com.sun.mail.util.*;
/**
* This class implements the Transport abstract class using SMTP for
* message submission and transport. <p>
*
* See the <a href="package-summary.html">com.sun.mail.smtp</a> package
* documentation for further information on the SMTP protocol provider. <p>
*
* 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:
* <pre>
* protected OutputStream data() throws MessagingException {
* if (supportsExtension("XACCOUNTING"))
* issueCommand("XACT", 25);
* return super.data();
* }
* </pre>
*
* @author Max Spivak
* @author Bill Shannon
* @author Dean Gibson (DIGEST-MD5 authentication)
* @version 1.89, 07/07/03
*
* @see javax.mail.event.ConnectionEvent
* @see javax.mail.event.TransportEvent
*/
public class SMTPTransport extends Transport {
private String name = "smtp"; // Name of this protocol
private int defaultPort = 25; // default SMTP port
private boolean isSSL = false; // use SSL?
// Following fields valid only during the sendMessage method.
private MimeMessage message; // Message to be sent
private Address[] addresses; // Addresses to which to send the msg
// Valid sent, valid unsent and invalid addresses
private Address[] validSentAddr, validUnsentAddr, invalidAddr;
// Did we send the message even though some addresses were invalid?
private boolean sendPartiallyFailed = false;
// If so, here's an exception we need to throw
private MessagingException exception;
// stream where message data is written
private SMTPOutputStream dataStream;
// Map of SMTP service extensions supported by server, if EHLO used.
private Hashtable extMap;
private boolean quitWait = false; // true if we should wait
private String saslRealm = UNKNOWN;
private boolean reportSuccess; // throw an exception even on success
private boolean useStartTLS; // use STARTTLS command
private boolean useRset; // use RSET instead of NOOP
private PrintStream out; // debug output stream
private String localHostName; // our own host name
private String lastServerResponse; // last SMTP response
private int lastReturnCode; // last SMTP return code
/** Headers that should not be included when sending */
private static final String[] ignoreList = { "Bcc", "Content-Length" };
private static final byte[] CRLF = { (byte)'\r', (byte)'\n' };
private static final String UNKNOWN = "UNKNOWN"; // place holder
/**
* Constructor that takes a Session object and a URLName
* that represents a specific SMTP server.
*/
public SMTPTransport(Session session, URLName urlname) {
this(session, urlname, "smtp", 25, false);
}
/**
* Constructor used by this class and by SMTPSSLTransport subclass.
*/
protected SMTPTransport(Session session, URLName urlname,
String name, int defaultPort, boolean isSSL) {
super(session, urlname);
if (urlname != null)
name = urlname.getProtocol();
this.name = name;
this.defaultPort = defaultPort;
this.isSSL = isSSL;
out = session.getDebugOut();
// setting mail.smtp.quitwait to false causes us to not wait for the
// response from the QUIT command
String s = session.getProperty("mail." + name + ".quitwait");
quitWait = s == null || s.equalsIgnoreCase("true");
// mail.smtp.reportsuccess causes us to throw an exception on success
s = session.getProperty("mail." + name + ".reportsuccess");
reportSuccess = s != null && s.equalsIgnoreCase("true");
// mail.smtp.starttls.enable enables use of STARTTLS command
s = session.getProperty("mail." + name + ".starttls.enable");
useStartTLS = s != null && s.equalsIgnoreCase("true");
// mail.smtp.userset causes us to use RSET instead of NOOP
// for isConnected
s = session.getProperty("mail." + name + ".userset");
useRset = s != null && s.equalsIgnoreCase("true");
}
/**
* 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.
*/
public synchronized String getLocalHost() {
try {
// 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");
if (localHostName == null || localHostName.length() <= 0) {
InetAddress localHost = InetAddress.getLocalHost();
localHostName = localHost.getHostName();
// 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) {
}
return localHostName;
}
/**
* Set the name of the local host, for use in the EHLO and HELO commands.
*
* @since JavaMail 1.3.1
*/
public synchronized void setLocalHost(String localhost) {
localHostName = localhost;
}
/**
* 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
*/
public synchronized void connect(Socket socket) throws MessagingException {
serverSocket = socket;
super.connect();
}
/**
* 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
*/
public synchronized String getSASLRealm() {
if (saslRealm == UNKNOWN) {
saslRealm = session.getProperty("mail." + name + ".sasl.realm");
if (saslRealm == null) // try old name
saslRealm = session.getProperty("mail." + name + ".saslrealm");
}
return 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
*/
public synchronized void setSASLRealm(String saslRealm) {
this.saslRealm = saslRealm;
}
/**
* Should we report even successful sends by throwing an exception?
* If so, a <code>SendFailedException</code> 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
*/
public synchronized boolean getReportSuccess() {
return 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
*/
public synchronized void setReportSuccess(boolean reportSuccess) {
this.reportSuccess = reportSuccess;
}
/**
* 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
*/
public synchronized boolean getStartTLS() {
return useStartTLS;
}
/**
* Set whether the STARTTLS command should be used.
*
* @param useStartTLS should we use the STARTTLS command?
*
* @since JavaMail 1.3.2
*/
public synchronized void setStartTLS(boolean useStartTLS) {
this.useStartTLS = useStartTLS;
}
/**
* 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
*/
public synchronized boolean getUseRset() {
return 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
*/
public synchronized void setUseRset(boolean useRset) {
this.useRset = useRset;
}
/**
* 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
*/
public synchronized String getLastServerResponse() {
return lastServerResponse;
}
/**
* 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
*/
public synchronized int getLastReturnCode() {
return lastReturnCode;
}
private DigestMD5 md5support;
private synchronized DigestMD5 getMD5() {
if (md5support == null)
md5support = new DigestMD5(debug ? out : null);
return md5support;
}
/**
* Performs the actual protocol-specific connection attempt.
* Will attempt to connect to "localhost" if the host was null. <p>
*
* 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
*/
protected boolean protocolConnect(String host, int port, String user,
String passwd) throws MessagingException {
// setting mail.smtp.ehlo to false disables attempts to use EHLO
String ehloStr = session.getProperty("mail." + name + ".ehlo");
boolean useEhlo = ehloStr == null || !ehloStr.equalsIgnoreCase("false");
// setting mail.smtp.auth to true enables attempts to use AUTH
String authStr = session.getProperty("mail." + name + ".auth");
boolean useAuth = authStr != null && authStr.equalsIgnoreCase("true");
DigestMD5 md5;
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) {
String portstring = session.getProperty("mail." + name + ".port");
if (portstring != null) {
port = Integer.parseInt(portstring);
} else {
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 && 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());
}
if ((useAuth || (user != null && passwd != null)) &&
(supportsExtension("AUTH") || supportsExtension("AUTH=LOGIN"))) {
if (debug) {
out.println("DEBUG SMTP: Attempt to authenticate");
if (!supportsAuthentication("LOGIN") &&
supportsExtension("AUTH=LOGIN"))
out.println("DEBUG SMTP: use AUTH=LOGIN hack");
}
// if authentication fails, close connection and return false
if (supportsAuthentication("LOGIN") ||
supportsExtension("AUTH=LOGIN")) {
// XXX - could use "initial response" capability
int resp = simpleCommand("AUTH LOGIN");
/*
* A 530 response indicates that the server wants us to
* issue a STARTTLS command first. Do that and try again.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -