//------------------------------------------------------------------ //                              \\\|/// //                            \\  -^-  // //                             (  @ @  ) //    +----------------------oOOo-(_)-oOOo---------------------+ // //           FREE SOFTWARE WRITEN BY NAVY, COPYLEFT (C) 2002 //                       SmtpClient Class 1.0  //               Use smtp server with user authorization //                         All rights reserved. // //                                    Oooo //    +---------------------- oooO---(   )---------------------+ //                            (   )   ) / //                             \ (   (_/ //                              \_) //------------------------------------------------------------------ 
 package encrypt; 
import java.io.*; import java.net.*; import java.util.Vector; //import org.apache.commons.logging.Log; //import org.apache.commons.logging.LogFactory; import encrypt.Base64; 
/***  * 标准SMTP发信类  * <p>  * 标准的纯JAVA的SMTP发信客户端程序,支持用户认证。  * <p>  * <p>  * @author Naven  * @see SmtpClient  ***/ 
public class SmtpClient {     //protected static final Log log = LogFactory.getLog(SmtpClient.class);          private static final String CMD_HELO            = "HELO ";     private static final String CMD_AUTH_LOGIN      = "AUTH LOGIN ";     private static final String CMD_MAIL_FROM       = "MAIL FROM: ";     private static final String CMD_RCPT_TO         = "RCPT TO: ";     private static final String CMD_DATA            = "DATA";     private static final String CMD_HELP            = "HELP";     private static final String CMD_RSET            = "RSET";     private static final String CMD_NOOP            = "NOOP";     private static final String CMD_QUIT            = "QUIT";     private static final String END_OF_MAIL         = "\r\n.\r\n"; 
    private static final String RCV_SERVOK          = "220";        // 220 服务就绪     private static final String RCV_HELO            = "250";        // 250 要求的邮件操作完成     private static final String RCV_AUTH_LOGIN      = "334";     private static final String RCV_AUTH_USER       = "334";     private static final String RCV_AUTH_PASSWD     = "334";     private static final String RCV_AUTH_OK         = "235";     private static final String RCV_MAIL_FROM       = "250";     private static final String RCV_RCPT_TO         = "250";     private static final String RCV_DATA            = "354";     private static final String RCV_SEND_END        = "250";     private static final String RCV_RSET            = "250";     private static final String RCV_NOOP            = "250";     private static final String RCV_QUIT            = "221";        // 221 服务关闭传输信道 
    private static final int SEND_BLOCK_SIZE        = 1024;         // 每次发送信件内容的块的大小          /**      * BASE64加密对象      */     //private Base64 base64 = new Base64();      
    private static final int _NOTHING_SPECIAL_STATE = 0;     private static final int _LAST_WAS_CR_STATE = 1;     private static final int _LAST_WAS_NL_STATE = 2; 
    /**      * 记录处理邮件正文数据发送的状态      */     private int _state = 0;           /**      * 用于处理邮件正文数据发送同步处理的锁定      */     private Integer lock = new Integer(0);          /**      * client socket      */     private Socket      socketSmtp = null;          /**      * socket out printwriter      */     private PrintWriter     sout = null;          /**      * socket int reader      */     private BufferedReader  sin = null;          /**      * smtp email server address      */     private String      smtpServer = null;          /**      * email from user for smtp server      */     private String      user = null;          /**      * user password      */     private String      passwd = null;          /**      * sender's email address      */     private String      sender = null;          /**      * email from user for smtp server, base64 encode      */     private String      encryptUser = null;          /**      * user password, base64 encode      */     private String      encryptPasswd = null;          /**      * client localhost      */     private String      localHost = null; 
    /**      * error message      */     private String      errorString = "NO ERROR";          
    /***      * 初始化发信类      * <p>      * @param server  SMTP服务器地址      * @param sender  SMTP发信人邮件地址      ***/     public SmtpClient(String server, String sender)     {         this(server, null, null, sender);      }          /***      * 初始化发信类      * <p>      * @param server  SMTP服务器地址      * @param user  SMTP发信人认证用户名      * @param passwd  SMTP发信人认证密码      * @param sender  SMTP发信人邮件地址      ***/     public SmtpClient(String server, String user, String passwd, String sender)     {         this.smtpServer = server;         this.user       = user;         this.passwd     = passwd;         this.sender     = sender; 
        if( this.user != null && this.passwd != null )          {             Base64 base64 = new Base64();                          // base64 encode begain             byte[] buser  = user.getBytes();             byte[] bpasswd= passwd.getBytes();                  base64.startEncode();             base64.encode(buser, buser.length);             base64.endEncode();             this.encryptUser = new String(base64.getEncodedResult());                  base64.startEncode();             base64.encode(bpasswd, bpasswd.length);             base64.endEncode();             this.encryptPasswd = new String(base64.getEncodedResult());         }     } 
    /***      * 获取处理的错误信息      * <p>      * @return  错误信息      ***/     public String getError() {         return errorString;     } 
    /***      * 当出错时抛出错误信息      * <p>      * @param e  错误异常      ***/     private void onError(Exception e)      {         this.errorString = e.getMessage();          //log.error("onError() " + this.errorString);          //if( log.isDebugEnabled() ) {         //    log.debug("onError()", e);          //}     } 
    /***      * 检查SMTP协议通讯收到的信息是否成功,即以指定返回代号开头,SMTP协议标准。      * <p>      * @param rcvmsg  SMTP协议通讯收到的信息      * @param code  SMTP协议通讯返回代号      * @exception IOException  失败时抛出异常      ***/     private void check(String rcvmsg, String code)          throws IOException      {         if( code == null || code.length() == 0 ) return;         if( rcvmsg == null || rcvmsg.startsWith(code) == false )             throw ( new IOException(rcvmsg) );     } 
    /***      * 检查SMTP协议通讯收到的信息是否成功,即以指定返回代号数组中任意个开头,SMTP协议标准。      * <p>      * @param rcvmsg  SMTP协议通讯收到的信息      * @param codes  SMTP协议通讯返回代号数组      * @exception IOException  失败时抛出异常      ***/     private void check(String rcvmsg, String[] codes)          throws IOException      {         if( codes == null || codes.length == 0 ) return;         boolean result = false;         for( int i=0; rcvmsg != null && i < codes.length && codes[i] != null; i++ ) {             if( rcvmsg.startsWith(codes[i]) == false ) {                 result = true;                 break;             }         }         if(!result) throw ( new IOException(rcvmsg) );     } 
    /***      * 往SMTP服务器写邮件正文数据的一个字节,并处理数据中“\r\n.”需转换成“\r\n..”的情况。      * <p>      * @param ch  写入的一个字节      * @exception IOException  失败时抛出异常      ***/     private void write(int ch)          throws IOException     {         synchronized (lock)         {             switch (ch)             {             case '\r':                 _state = _LAST_WAS_CR_STATE;                 sout.write('\r');                 return ;             case '\n':                 if (_state != _LAST_WAS_CR_STATE)                     sout.write('\r');                 sout.write('\n');                 _state = _LAST_WAS_NL_STATE;                 return ;             case '.':                 // Double the dot at the beginning of a line                 if (_state == _LAST_WAS_NL_STATE)                      sout.write('.');                 // Fall through             default:                 _state = _NOTHING_SPECIAL_STATE;                 sout.write(ch);                 return ;             }         }     } 
    /***      * 往SMTP服务器写邮件正文数据的一段数据,并处理数据中“\r\n.”需转换成“\r\n..”的情况。      * <p>      * @param buffer  写入的数据缓冲      * @param offset  写入的数据缓冲的偏移      * @param length  写入的数据缓冲的长度      * @exception IOException  失败时抛出异常      ***/     private void write(char[] buffer, int offset, int length)          throws IOException     {         synchronized (lock)         {             while (length-- > 0)                 write(buffer[offset++]);         }     } 
    /***      * 往SMTP服务器写邮件正文数据的一段数据,并处理数据中“\r\n.”需转换成“\r\n..”的情况。      * <p>      * @param buffer  写入的数据缓冲      * @exception IOException  失败时抛出异常      ***/     private void write(char[] buffer)          throws IOException     {         write(buffer, 0, buffer.length);     } 
    /***      * 往SMTP服务器写邮件正文数据的一段数据,并处理数据中“\r\n.”需转换成“\r\n..”的情况。      * <p>      * @param string  写入的数据字符串      * @exception IOException  失败时抛出异常      ***/     private void write(String string)          throws IOException      {         write(string.toCharArray());     } 
    /***      * 将SOCKET STREAM缓冲区的数据刷新,提交出去。      * <p>      * @exception IOException  失败时抛出异常      ***/     private void flush()          throws IOException     {         synchronized (lock)         {             sout.flush();         }     } 
    /***      * 往SMTP服务器写一行数据。      * <p>      * @param msg  写入的一行数据字符串      * @exception IOException  失败时抛出异常      ***/     private void sendln(String msg)          throws IOException      {         if( msg == null ) msg = "";                   sout.println(msg);         sout.flush();                  //if( log.isDebugEnabled() ) {         //    log.debug("sendln() ==>: "+msg);         //}     } 
    /***      * 往SMTP服务器写字符串数据。      * <p>      * @param msg  写入的字符串      * @exception IOException  失败时抛出异常      ***/     private void send(String msg)          throws IOException      {         if( msg == null ) msg = "";                   sout.write(msg);         sout.flush();                  //if( log.isDebugEnabled() ) {         //    log.debug("send() ==>: "+msg);         //}     } 
    /***      * 往SMTP服务器写一段大字符串数据。      * <p>      * @param text  写入的字符串数据      * @exception IOException  失败时抛出异常      ***/     private void sendtext(String text)          throws IOException      {         if( text == null ) text = "";                   if( text.length() > SEND_BLOCK_SIZE ) {             int i = 0;             while( i <= text.length() ) {                 if( (i + SEND_BLOCK_SIZE) < text.length() )                     write(text.substring(i, (i+SEND_BLOCK_SIZE)));                 else                     write(text.substring(i));                 flush();                 i = i + SEND_BLOCK_SIZE;             }                          //if( log.isDebugEnabled() ) {             //    log.debug("sendtext() ==>: <Email Mesg> "+text.length()+" chars");             //}         }         else {             write(text);              flush();                           //if( log.isDebugEnabled() ) {             //    log.debug("sendtext() ==>: "+text);             //}         }     } 
    /***      * 从SMTP服务器接收一行字符串数据。      * <p>      * @return  读取的字符串数据      * @exception IOException  失败时抛出异常      ***/     private String receive()          throws IOException      {          String rcvmsg = sin.readLine();         //if( log.isDebugEnabled() ) {         //    log.debug("receive() <==: " + rcvmsg);         //}         return rcvmsg;     } 
    /***      * 从SMTP服务器接收一行字符串数据,并判断是否是成功的返回值。      * <p>      * @param code  正确的SMTP协议代码      * @return  读取的字符串数据      * @exception IOException  失败时抛出异常      ***/     private String receive(String code)          throws IOException      {          String rcvmsg = receive();         check(rcvmsg, code);         return rcvmsg;     }          /***      * 从SMTP服务器接收一行字符串数据,并判断是否是成功的返回值数组的一个。      * <p>      * @param codes  正确的SMTP协议代码数组      * @return  读取的字符串数据      * @exception IOException  失败时抛出异常      ***/     private String receive(String[] codes)          throws IOException      {          String rcvmsg = receive();         check(rcvmsg, codes);         return rcvmsg;     }          /***      * 连接SMTP服务器并发送用户名和密码认证。      * <p>      * @return  返回成功失败结果      * @exception IOException  失败时抛出异常      ***/     public boolean connect()      {         // connect to smtp server and autherize         try{             // get localhost name             localHost = InetAddress.getLocalHost().getHostName();              //if( log.isDebugEnabled() ) {             //    log.debug("connect() localhost: " + localHost);             //} 
            // connect to smtp server             socketSmtp = new Socket(smtpServer, 25);             sout       = new PrintWriter(new OutputStreamWriter(socketSmtp.getOutputStream()));             sin        = new BufferedReader(new InputStreamReader(socketSmtp.getInputStream()));             receive(RCV_SERVOK); 
            // hello             sendln(CMD_HELO + localHost);             receive(RCV_HELO);                          if( encryptUser != null && encryptPasswd != null )              {                 // auth login                 sendln(CMD_AUTH_LOGIN);                 receive(RCV_AUTH_LOGIN);                      // base64 encode end                 sendln(encryptUser);                 receive(RCV_AUTH_USER);                 sendln(encryptPasswd);                 receive(RCV_AUTH_OK);             }         }         catch(IOException e) {             onError(e);             closeall();             return false;         }                  return true;     } 
    /***      * 连接SMTP服务器并发送邮件。      * <p>      * @param to  收件人邮件地址      * @param msg  邮件数据      * @return  返回成功失败结果      * @exception IOException  失败时抛出异常      ***/     public boolean sendMail(String to, String msg) {             return sendMail(to, msg, null);     }          /***      * 连接SMTP服务器并发送邮件。      * <p>      * @param to  收件人邮件地址      * @param msg  邮件数据      * @param cc  CC收件人邮件地址      * @return  返回成功失败结果      * @exception IOException  失败时抛出异常      ***/     public boolean sendMail(String to, String msg, Vector cc)      {         if( socketSmtp == null || sout == null || sin == null ) {             closeall();             if( !connect() ) return false;         } 
        boolean retval = false;         int count = 0;         // try send for 3 times if error         while( retval == false && count < 3 ) {           try {             // mail from             sendln(CMD_MAIL_FROM + sender);             receive(RCV_MAIL_FROM);                          // send to             sendln(CMD_RCPT_TO + to);             receive(RCV_RCPT_TO);                          // perform cc             int ccSize = 0;             if(cc != null && (ccSize = cc.size()) > 0){                 for(int i = 0; i < ccSize; i ++){                                         sendln(CMD_RCPT_TO + (String)cc.elementAt(i));                     receive(RCV_RCPT_TO);                 }             }             // end cc                          // begain send mail data             sendln(CMD_DATA);             receive(RCV_DATA);             sendtext(msg);             sendln(END_OF_MAIL);             receive(RCV_SEND_END);                          // send success             //receive(); // I dont know why 263.net.cn need receve again             retval = true;           }           catch(IOException e) {             onError(e);             retval = false;             count ++;             try{                 // reset and send again                 sendln(CMD_RSET);                 receive(RCV_RSET);             }             catch(Exception e2) {                 //log.error("sendMail()", e2);                  break;             }           }         } 
        return retval;     } 
    /***      * 关闭与SMTP服务器连接。      * <p>      ***/     private void closeall()      {         try {              if( sout != null ) {                 sout.close(); sout = null;              }             if( sin != null ) {                 sin.close(); sin = null;              }             if( socketSmtp != null ) {                 socketSmtp.close(); socketSmtp = null;              }         }         catch(IOException e) {             //log.error("closeall()", e);          }     } 
    /***      * 关闭与SMTP服务器连接并释放资源。      * <p>      ***/     public void release()      {         close();                   this.socketSmtp     = null;    // client socket         this.sout           = null;    // socket out printstream         this.sin            = null;    // socket int reader         this.smtpServer     = null;    // smtp email server address         this.user           = null;    // email from user for smtp server         this.passwd         = null;    // user password         this.sender         = null;    // sender's email address         this.encryptUser    = null;    // base64 encode         this.encryptPasswd  = null;    // base64 encode         this.localHost      = null;    // client localhost 
        this.errorString    = "NO ERROR";     } 
    /***      * 发送QUIT命令并关闭与SMTP服务器连接。      * <p>      ***/     public boolean close()      {         boolean retval = true;                  if( sout != null && sin != null ) {             try {                  // send finish quit                 sendln(CMD_QUIT);                 //receive();                 retval = true;             }             catch(IOException e) {                 retval = false;                 //log.error("close()", e);              }         }         closeall();                  return retval;     } 
    public String toString() {         return getClass().getName() +                  " Server: " + smtpServer + " User: " + user +                  " Passwd: " + passwd + " Sender: " + sender;     }          }///:~
   
 
  |