📄 smtpmailsender.java
字号:
package org.gameeden.mail;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import org.gameeden.security.Base64;
import org.gameeden.util.LogManager;
/**
* SMTP邮件发送系统。
*
*
* 发件人和收件人的正确格式如下:
*
* 例1: "Sol"<sol@gameeden.org>
* 例2: Sol<sol@gameeden.org>
* 例3: <sol@gameeden.org>
* 例4: sol@gameeden.org
*
* @author Sol
* @since 1.5
*/
public final class SmtpMailSender
{
/**
* 发送成功的常量。
*/
public final static boolean SUCCESSFUL=true;
/**
* 发送失败的常量。
*/
public final static boolean FAILED=false;
private final static int PORT=25;//服务器端口(SMTP服务器和邮件接收服务器的端口均为25)
private final static int RETRY=3;//当连接SMTP服务器失败后尝试重新连接的次数(仅用于发送ESMTP邮件)
private final static int INTERVAL=1000;//当连接SMTP服务器失败后重新连接的时间间隔(仅用于发送ESMTP邮件)
private final static int TIMEOUT=10000;//网络连接的超时时间
private final static String BOUNDARY;//MIME分格符
private final static String CHARSET;//虚拟机的默认编码
private final static Pattern PATTERN;//用于效验邮箱地址的正确性
private static InitialDirContext dirContext;//用于查询DNS记录
private final ArrayList<LogManager> logManager;//日志管理器
private boolean isEsmtp;//发送类型
private String smtp;//SMTP服务器地址(仅用于发送ESMTP邮件)
private String user;//用户名(仅用于发送ESMTP邮件)
private String password;//密码(仅用于发送ESMTP邮件)
private String sender;//发件人名字
private String senderAddress;//发件人的E-Mail地址
static
{
BOUNDARY="Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu";
CHARSET=Charset.defaultCharset().displayName();
PATTERN=Pattern.compile(".+@[^.@]+(\\.[^.@]+)+$");//此处放弃了传统匹配方式,这是为了兼容非英文域名的电子邮箱
Hashtable<String,String> hashtable=new Hashtable<String,String>();
hashtable.put("java.naming.factory.initial","com.sun.jndi.dns.DnsContextFactory");
try
{
dirContext=new InitialDirContext(hashtable);
}
catch(NamingException e)
{
}
}
private SmtpMailSender(String from)
{
if(from==null)
{
throw new IllegalArgumentException("参数from不能为null。");
}
int leftSign=(from=from.trim()).charAt(from.length()-1)=='>'?from.lastIndexOf('<'):-1;
senderAddress=leftSign>-1?from.substring(leftSign+1,from.length()-1).trim():from;
if(!PATTERN.matcher(senderAddress).find())
{
throw new IllegalArgumentException("参数from不正确。");
}
sender=leftSign>-1?from.substring(0,leftSign).trim():null;
logManager=new ArrayList<LogManager>();
isEsmtp=false;
if(sender!=null)
{
if(sender.length()==0)
{
sender=null;
}
else if(sender.charAt(0)=='"'&&sender.charAt(sender.length()-1)=='"')
{
sender=sender.substring(1,sender.length()-1).trim();
}
}
}
private SmtpMailSender(String address,String from,String user,String password)
{
this(from);
isEsmtp=true;
this.smtp=address;
this.user=Base64.encode(user.getBytes());
this.password=Base64.encode(password.getBytes());
}
/**
* 创建SMTP邮件发送系统实例。
* @param from 发件人
* @return SMTP邮件发送系统的实例
* @throws IllegalArgumentException 如果参数from为null或格式不正确
*/
public static SmtpMailSender createSmtpMailSender(String from) throws IllegalArgumentException
{
return new SmtpMailSender(from);
}
/**
* 创建ESMTP邮件发送系统实例。
* @param smtp SMTP服务器地址
* @param from 发件人
* @param user 用户名
* @param password 密码
* @return SMTP邮件发送系统的实例
* @throws IllegalArgumentException 如果参数from为null或格式不正确
*/
public static SmtpMailSender createESmtpMailSender(String smtp,String from,String user,String password) throws IllegalArgumentException
{
return new SmtpMailSender(smtp,from,user,password);
}
/**
* 发送邮件。
* @param to 收件人
* @param subject 主题
* @param content 正文
* @param attachments 附件
* @param isHtml 使用网页形式发送
* @param isUrgent 紧急邮件
* @return 是否发送成功
* @throws IllegalArgumentException 如果参数to为null或格式不正确
*/
public boolean sendMail(String to,String subject,String content,File[] attachments,boolean isHtml,boolean isUrgent) throws IllegalArgumentException
{
if(to==null)
{
throw new IllegalArgumentException("参数to不能为null。");
}
int leftSign=(to=to.trim()).charAt(to.length()-1)=='>'?to.lastIndexOf('<'):-1;
String addresseeAddress=leftSign>-1?to.substring(leftSign+1,to.length()-1).trim():to;//收件人的E-Mail地址
if(!PATTERN.matcher(addresseeAddress).find())
{
throw new IllegalArgumentException("参数to不正确。");
}
String addressee=leftSign>-1?to.substring(0,leftSign).trim():null;//收件人名字
boolean needBoundary=attachments!=null&&attachments.length>0;
Socket socket=null;
InputStream in=null;
OutputStream out=null;
byte[] data;
try
{
if(addressee!=null)
{
if(addressee.length()==0)
{
addressee=null;
}
else if(addressee.charAt(0)=='"'&&addressee.charAt(addressee.length()-1)=='"')
{
addressee=addressee.substring(1,addressee.length()-1).trim();
}
}
if(isEsmtp)
{
for(int k=1;;k++)
{
try
{
log("连接: 主机:\""+smtp+"\" 端口:\""+PORT+"\"");
socket=new Socket(smtp,PORT);
break;
}
catch(IOException e)
{
log("错误: 连接失败"+k+"次");
if(k==RETRY)
{
return FAILED;
}
try
{
Thread.sleep(INTERVAL);
}
catch(InterruptedException ie)
{
}
}
}
in=socket.getInputStream();
out=socket.getOutputStream();
if(response(in)!=220)
{
return FAILED;
}
}
else
{
log("状态: 创建邮件接收服务器列表");
String[] address=parseDomain(parseUrl(addresseeAddress));
if(address==null)
{
return FAILED;
}
for(int k=0;k<address.length;k++)
{
try
{
log("连接: 主机:\""+address[k]+"\" 端口:\""+PORT+"\"");
socket=new Socket(address[k],PORT);
in=socket.getInputStream();
out=socket.getOutputStream();
if(response(in)!=220)
{
return FAILED;
}
break;
}
catch(IOException e)
{
log("错误: 连接失败");
}
}
}
if(in==null||out==null)
{
return FAILED;
}
socket.setSoTimeout(TIMEOUT);
sendString("HELO "+parseUrl(senderAddress),out);
sendNewline(out);
if(response(in)!=250)
{
return FAILED;
}
if(isEsmtp)
{
sendString("AUTH LOGIN",out);
sendNewline(out);
if(response(in)!=334)
{
return FAILED;
}
sendString(user,out);
sendNewline(out);
if(response(in)!=334)
{
return FAILED;
}
sendString(password,out);
sendNewline(out);
if(response(in)!=235)
{
return FAILED;
}
}
sendString("MAIL FROM: <"+senderAddress+">",out);
sendNewline(out);
if(response(in)!=250)
{
return FAILED;
}
sendString("RCPT TO: <"+addresseeAddress+">",out);
sendNewline(out);
if(response(in)!=250)
{
return FAILED;
}
sendString("DATA",out);
sendNewline(out);
if(response(in)!=354)
{
return FAILED;
}
sendString("From: "+(sender==null?senderAddress:getBase64String(sender)+" <"+senderAddress+">"),out);
sendNewline(out);
sendString("To: "+(addressee==null?addresseeAddress:getBase64String(addressee)+" <"+addresseeAddress+">"),out);
sendNewline(out);
sendString("Subject: "+getBase64String(subject),out);
sendNewline(out);
sendString("Date: "+new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z (z)",Locale.US).format(new Date()),out);
sendNewline(out);
sendString("MIME-Version: 1.0",out);
sendNewline(out);
if(needBoundary)
{
sendString("Content-Type: multipart/mixed; BOUNDARY=\""+BOUNDARY+"\"",out);
sendNewline(out);
}
else
{
if(isHtml)
{
sendString("Content-Type: text/html; charset=\""+CHARSET+"\"",out);
sendNewline(out);
}
else
{
sendString("Content-Type: text/plain; charset=\""+CHARSET+"\"",out);
sendNewline(out);
}
}
sendString("Content-Transfer-Encoding: base64",out);
sendNewline(out);
if(isUrgent)
{
sendString("X-Priority: 1",out);
sendNewline(out);
}
else
{
sendString("X-Priority: 3",out);
sendNewline(out);
}
sendString("X-Mailer: BlackFox Mail[Copyright(C) 2007 Sol]",out);
sendNewline(out);
log("发送: ");
sendNewline(out);
if(needBoundary)
{
sendString("--"+BOUNDARY,out);
sendNewline(out);
if(isHtml)
{
sendString("Content-Type: text/html; charset=\""+CHARSET+"\"",out);
sendNewline(out);
}
else
{
sendString("Content-Type: text/plain; charset=\""+CHARSET+"\"",out);
sendNewline(out);
}
sendString("Content-Transfer-Encoding: base64",out);
sendNewline(out);
log("发送: ");
sendNewline(out);
}
data=(content!=null?content:"").getBytes();
for(int k=0;k<data.length;k+=54)
{
sendString(Base64.encode(data,k,Math.min(data.length-k,54)),out);
sendNewline(out);
}
if(needBoundary)
{
RandomAccessFile attachment=null;
int fileIndex=0;
String fileName;
int k;
data=new byte[54];
try
{
for(;fileIndex<attachments.length;fileIndex++)
{
fileName=attachments[fileIndex].getName();
attachment=new RandomAccessFile(attachments[fileIndex],"r");
sendString("--"+BOUNDARY,out);
sendNewline(out);
sendString("Content-Type: "+MimeTypeFactory.getMimeType(fileName.indexOf(".")==-1?"*":fileName.substring(fileName.lastIndexOf(".")+1))+"; name=\""+(fileName=getBase64String(fileName))+"\"",out);
sendNewline(out);
sendString("Content-Transfer-Encoding: base64",out);
sendNewline(out);
sendString("Content-Disposition: attachment; filename=\""+fileName+"\"",out);
sendNewline(out);
log("发送: ");
sendNewline(out);
do
{
k=attachment.read(data,0,54);
if(k==-1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -