Javen-Studio 咖啡小屋

http://javenstudio.org - C++ Java 分布式 搜索引擎
Naven's Research Laboratory - Thinking of Life, Imagination of Future

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  24 随笔 :: 57 文章 :: 170 评论 :: 4 Trackbacks

//------------------------------------------------------------------
//                              \\\|///
//                            \\  -^-  //
//                             (  @ @  )
//    +----------------------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;
    }
       
}///:~

posted on 2005-10-03 11:48 Javen-Studio 阅读(509) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理