⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smtptransport.java

📁 此源码是在sun站点上提供的javamail基础上改进。用来解决中文邮件或很多国际间邮件乱码问题。版权属于sun公司。不过当你开发webmail程序时做邮件展示时
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
 * 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 + -