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

📄 smtptransport.java

📁 此源码是在sun站点上提供的javamail基础上改进。用来解决中文邮件或很多国际间邮件乱码问题。版权属于sun公司。不过当你开发webmail程序时做邮件展示时
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
		if (resp == 530) {
		    startTLS();
		    resp = simpleCommand("AUTH LOGIN");
		}

		/*
		 * Wrap a BASE64Encoder around a ByteArrayOutputstream
		 * to craft b64 encoded username and password strings.
		 *
		 * Also note that unlike the B64 definition in MIME, CRLFs 
		 * should *not* be inserted during the encoding process.
		 * So I use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the
		 * bytesPerLine, which should be sufficiently large!
		 */
		try {
		    ByteArrayOutputStream bos = new ByteArrayOutputStream();
		    OutputStream b64os =
				new BASE64EncoderStream(bos, Integer.MAX_VALUE);

		    if (resp == 334) {
			// obtain b64 encoded bytes
			b64os.write(ASCIIUtility.getBytes(user));
			b64os.flush(); 	// complete the encoding

			// send username
			resp = simpleCommand(bos.toByteArray());
			bos.reset(); 	// reset buffer
		    }
		    if (resp == 334) {
			// obtain b64 encoded bytes
			b64os.write(ASCIIUtility.getBytes(passwd));
			b64os.flush(); 	// complete the encoding

			// send passwd
			resp = simpleCommand(bos.toByteArray());
			bos.reset(); 	// reset buffer
		    }
		} catch (IOException ex) {	// should never happen, ignore
		} finally {
		    if (resp != 235) {
			closeConnection();
			return false;
		    }
		}
	    } else if (supportsAuthentication("PLAIN")) {
		// XXX - could use "initial response" capability
		int resp = simpleCommand("AUTH PLAIN");
		try {
		    ByteArrayOutputStream bos = new ByteArrayOutputStream();
		    OutputStream b64os =
				new BASE64EncoderStream(bos, Integer.MAX_VALUE);
		    if (resp == 334) {
			// send "<NUL>user<NUL>passwd"
			// XXX - we don't send an authorization identity
			b64os.write(0);
			b64os.write(ASCIIUtility.getBytes(user));
			b64os.write(0);
			b64os.write(ASCIIUtility.getBytes(passwd));
			b64os.flush(); 	// complete the encoding

			// send username
			resp = simpleCommand(bos.toByteArray());
		    }
		} catch (IOException ex) {	// should never happen, ignore
		} finally {
		    if (resp != 235) {
			closeConnection();
			return false;
		    }
		}
	    } else if (supportsAuthentication("DIGEST-MD5") &&
		    (md5 = getMD5()) != null) {
		int resp = simpleCommand("AUTH DIGEST-MD5");
		try {
		    if (resp == 334) {
			byte[] b = md5.authClient(host, user, passwd,
					    getSASLRealm(), lastServerResponse);
			resp = simpleCommand(b);
			if (resp == 334) { // client authenticated by server
			    if (!md5.authServer(lastServerResponse)) {
				// server NOT authenticated by client !!!
				resp = -1;
			    } else {
				// send null response
				resp = simpleCommand(new byte[0]);
			    }
			}
		    }
		} catch (Exception ex) {	// should never happen, ignore
		    if (debug)
			out.println("DEBUG SMTP: DIGEST-MD5: " + ex);
		} finally {
		    if (resp != 235) {
			closeConnection();
			return false;
		    }
		}
	    }
	}

	// we connected correctly
	return true;
    }


    /**
     * Send the Message to the specified list of addresses.<p>
     *
     * If all the <code>addresses</code> succeed the SMTP check
     * using the <code>RCPT TO:</code> 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.<p>
     *
     * If some of the <code>addresses</code> fail the SMTP check,
     * and the <code>mail.stmp.sendpartial</code> 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. <p>
     *
     * If some of the <code>addresses</code> fail the SMTP check,
     * and the <code>mail.stmp.sendpartial</code> 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. <p>
     *
     * MessagingException is thrown if the message can't write out
     * an RFC822-compliant stream using its <code>writeTo</code> method. <p>
     *
     * @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.
     */
    public synchronized void sendMessage(Message message, Address[] addresses)
		    throws MessagingException, SendFailedException {

	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");
	    }
	}

	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) {
	    String ebStr =
		    session.getProperty("mail." + name + ".allow8bitmime");
	    use8bit = ebStr != null && ebStr.equalsIgnoreCase("true");
	}
	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);
	    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 */ }
	    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;
	}
    }

    /**
     * Close the Transport and terminate the connection to the server.
     */
    public synchronized void close() throws MessagingException {
	if (!super.isConnected()) // Already closed.
	    return;
	try {
	    if (serverSocket != null) {
		sendCommand("QUIT");
		if (quitWait) {
		    int resp = readServerResponse();
		    if (resp != 221 && resp != -1)
			out.println("DEBUG SMTP: QUIT failed with " + resp);
		}
	    }
	} finally {
	    closeConnection();
	}
    }

    private void closeConnection() throws MessagingException {
	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();
	}
    }

    /**
     * Check whether the transport is connected. Override superclass
     * method, to actually ping our server connection.
     */
    public synchronized boolean isConnected() {
	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 doesn't 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.
	    //
	    if (resp >= 0 && 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;
	}
    }

    /**
     * Expand any group addresses.
     */
    private void expandGroups() {
	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;
	}
    }

    /**
     * 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.
     */
    private boolean convertTo8Bit(MimePart part) {
	boolean changed = false;
	try {
	    if (part.isMimeType("text/*")) {
		String enc = part.getEncoding();
		if (enc != null && (enc.equalsIgnoreCase("quoted-printable") ||
		    enc.equalsIgnoreCase("base64"))) {
		    InputStream 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;
		    }
		}
	    } 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;
    }

    /**
     * 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.
     */
    private boolean is8Bit(InputStream is) {
	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;
    }

    protected void finalize() throws Throwable {
	super.finalize();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -