📄 smtptransport.java
字号:
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 + -