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