📄 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 + -