Improved SMTP email robustness and added JUnit test for it
This commit is contained in:
parent
b109d6ae5c
commit
3d04dba4fd
3 changed files with 239 additions and 66 deletions
|
|
@ -1,11 +1,13 @@
|
|||
package zutil.net.smtp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static zutil.net.smtp.SMTPClient.NEWLINE;
|
||||
|
||||
import sun.net.smtp.SmtpClient;
|
||||
|
||||
/**
|
||||
* Simplifies sending of a email
|
||||
|
|
@ -13,16 +15,20 @@ import sun.net.smtp.SmtpClient;
|
|||
* @author Ziver
|
||||
*/
|
||||
public class Email {
|
||||
public enum ContentType{
|
||||
|
||||
public enum ContentType{
|
||||
PLAIN, HTML
|
||||
}
|
||||
private static final SimpleDateFormat dateFormatter =
|
||||
new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
|
||||
private static final Pattern PATTERN_NEWLINE = Pattern.compile("(\\r\\n|\\n)");
|
||||
|
||||
private String from;
|
||||
private String niceFrom = null;
|
||||
private String to;
|
||||
private String replyTo = null;
|
||||
private String fromAddress;
|
||||
private String fromName = null;
|
||||
private String toAddress;
|
||||
private String toName = null;
|
||||
private String replyToAddress = null;
|
||||
private Date date = null;
|
||||
private ContentType type = ContentType.PLAIN;
|
||||
private String subject;
|
||||
private String message;
|
||||
|
|
@ -33,33 +39,66 @@ public class Email {
|
|||
|
||||
|
||||
public void setFrom(String address){
|
||||
from = address;
|
||||
this.fromAddress = sanitizeParam(address);
|
||||
}
|
||||
public void setFrom(String address, String niceName){
|
||||
from = address;
|
||||
niceFrom = niceName;
|
||||
fromAddress = sanitizeParam(address);
|
||||
fromName = sanitizeParam(niceName);
|
||||
}
|
||||
public String getFromAddress(){
|
||||
return from;
|
||||
return fromAddress;
|
||||
}
|
||||
public void setReplyTo(String rpt){
|
||||
replyTo = rpt;
|
||||
public String getFromName() {
|
||||
return fromName;
|
||||
}
|
||||
|
||||
public void setReplyTo(String address){
|
||||
this.replyToAddress = sanitizeParam(address);
|
||||
}
|
||||
public void setTo(String t){
|
||||
to = t;
|
||||
public String getReplyToAddress() {
|
||||
return replyToAddress;
|
||||
}
|
||||
|
||||
public void setTo(String address){
|
||||
this.toAddress = sanitizeParam(address);
|
||||
}
|
||||
public String getTo(){
|
||||
return to;
|
||||
public void setTo(String address, String niceName){
|
||||
this.toAddress = sanitizeParam(address);
|
||||
this.toName = sanitizeParam(niceName);
|
||||
}
|
||||
public String getToAddress(){
|
||||
return toAddress;
|
||||
}
|
||||
public String getToName() {
|
||||
return toName;
|
||||
}
|
||||
|
||||
public void setDate(Date date){
|
||||
this.date = date;
|
||||
}
|
||||
public void setContentType(ContentType t){
|
||||
type = t;
|
||||
}
|
||||
public void setSubject(String s){
|
||||
subject = s;
|
||||
|
||||
public void setSubject(String subject){
|
||||
this.subject = sanitizeParam(subject);
|
||||
}
|
||||
public String getSubject(){
|
||||
return subject;
|
||||
}
|
||||
|
||||
public void setMessage(String msg){
|
||||
message = msg;
|
||||
message = msg.replaceAll("(\\r\\n|\\n)", NEWLINE);
|
||||
message = message.replaceAll(NEWLINE+"\\.", NEWLINE +"..");
|
||||
}
|
||||
public String getMessage(){
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
private String sanitizeParam(String param){
|
||||
return PATTERN_NEWLINE.matcher(param).replaceAll("");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -67,33 +106,48 @@ public class Email {
|
|||
*
|
||||
* @throws IllegalArgumentException if from address and to address has not been set
|
||||
*/
|
||||
public void write(PrintStream out) throws IOException{
|
||||
if(from == null)
|
||||
public void write(Writer out) throws IOException{
|
||||
if(fromAddress == null)
|
||||
throw new IllegalArgumentException("From value cannot be null!");
|
||||
if(to == null)
|
||||
if(toAddress == null)
|
||||
throw new IllegalArgumentException("To value cannot be null!");
|
||||
|
||||
//************ Headers
|
||||
if (niceFrom!=null)
|
||||
out.println("From: \""+niceFrom+"\" <"+from+">");
|
||||
// From
|
||||
if (fromName !=null)
|
||||
out.write("From: "+ fromName +" <"+ fromAddress +">"+ NEWLINE);
|
||||
else
|
||||
out.println("From: <"+from+">");
|
||||
if ( replyTo != null )
|
||||
out.println("Reply-To: <"+replyTo+">");
|
||||
out.println("To: " + to);
|
||||
out.println("Subject: "+subject);
|
||||
out.write("From: "+ fromAddress + NEWLINE);
|
||||
|
||||
// Reply-To
|
||||
if ( replyToAddress != null )
|
||||
out.write("Reply-To: <"+ replyToAddress +">"+ NEWLINE);
|
||||
|
||||
// To
|
||||
if (toName !=null)
|
||||
out.write("To: "+ toName +" <"+ toAddress +">"+ NEWLINE);
|
||||
else
|
||||
out.write("To: "+ toAddress + NEWLINE);
|
||||
|
||||
// Date
|
||||
out.println("Date: "+dateFormatter.format(new Date(System.currentTimeMillis())));
|
||||
if (date != null)
|
||||
out.write("Date: "+dateFormatter.format(date) + NEWLINE);
|
||||
else
|
||||
out.write("Date: "+dateFormatter.format(new Date(System.currentTimeMillis())) + NEWLINE);
|
||||
|
||||
// Content type
|
||||
switch( type ){
|
||||
case HTML:
|
||||
out.println("Content-Type: text/html;"); break;
|
||||
out.write("Content-Type: text/html;"+ NEWLINE); break;
|
||||
default:
|
||||
out.println("Content-Type: text/plain;"); break;
|
||||
out.write("Content-Type: text/plain;"+ NEWLINE); break;
|
||||
}
|
||||
out.println();
|
||||
|
||||
|
||||
// Subject
|
||||
out.write("Subject: "+(subject!=null ? subject : "") + NEWLINE);
|
||||
|
||||
out.write(NEWLINE);
|
||||
//*********** Mesasge
|
||||
out.println( message );
|
||||
out.write( message );
|
||||
}
|
||||
}
|
||||
|
|
@ -26,10 +26,7 @@ package zutil.net.smtp;
|
|||
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
|
|
@ -46,7 +43,7 @@ import java.util.logging.Logger;
|
|||
public class SMTPClient {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private static final String NEWLINE = "\r\n";
|
||||
protected static final String NEWLINE = "\r\n";
|
||||
private static final String CMD_HELO = "HELO";
|
||||
private static final String CMD_FROM = "MAIL FROM";
|
||||
private static final String CMD_TO = "RCPT TO";
|
||||
|
|
@ -59,7 +56,7 @@ public class SMTPClient {
|
|||
|
||||
private Socket socket;
|
||||
private BufferedReader in;
|
||||
private PrintStream out;
|
||||
private Writer out;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -77,7 +74,7 @@ public class SMTPClient {
|
|||
public SMTPClient(String host, int port) throws IOException {
|
||||
socket = new Socket(host, port);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
out = new OutputStreamWriter(socket.getOutputStream());
|
||||
|
||||
readCommand();
|
||||
sendCommand(CMD_HELO + " " + InetAddress.getLocalHost().getHostName());
|
||||
|
|
@ -92,26 +89,12 @@ public class SMTPClient {
|
|||
* @param msg the email body message
|
||||
*/
|
||||
public synchronized void send(String from, String to, String subj, String msg) throws IOException{
|
||||
if(from == null)
|
||||
throw new IllegalArgumentException("From value cannot be null!");
|
||||
if(to == null)
|
||||
throw new IllegalArgumentException("To value cannot be null!");
|
||||
try{
|
||||
// Pre metadata
|
||||
sendCommand(CMD_FROM + ":" + from);
|
||||
sendCommand(CMD_TO + ":" + to);
|
||||
sendCommand(CMD_DATA);
|
||||
// Message headers and body
|
||||
out.println("From: "+from);
|
||||
out.println("To: "+to);
|
||||
out.println("Subject: "+subj);
|
||||
out.println("");
|
||||
out.println(msg);
|
||||
sendCommand(CMD_DATA_END);
|
||||
reset();
|
||||
}catch(IOException e){
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
Email email = new Email();
|
||||
email.setFrom(from);
|
||||
email.setTo(to);
|
||||
email.setSubject(subj);
|
||||
email.setMessage(msg);
|
||||
send(email);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,15 +105,16 @@ public class SMTPClient {
|
|||
public synchronized void send(Email email) throws IOException{
|
||||
if(email.getFromAddress() == null)
|
||||
throw new IllegalArgumentException("From value cannot be null!");
|
||||
if(email.getTo() == null)
|
||||
if(email.getToAddress() == null)
|
||||
throw new IllegalArgumentException("To value cannot be null!");
|
||||
try{
|
||||
// Pre metadata
|
||||
sendCommand(CMD_FROM + ":" + email.getFromAddress());
|
||||
sendCommand(CMD_TO + ":" + email.getTo());
|
||||
sendCommand(CMD_TO + ":" + email.getToAddress());
|
||||
sendCommand(CMD_DATA);
|
||||
// Message headers and body
|
||||
email.write(out);
|
||||
out.write(NEWLINE);
|
||||
sendCommand(CMD_DATA_END);
|
||||
reset();
|
||||
}catch(IOException e){
|
||||
|
|
@ -148,7 +132,7 @@ public class SMTPClient {
|
|||
*/
|
||||
public synchronized int sendCommand(String cmd) throws IOException{
|
||||
logger.finest(">> "+cmd);
|
||||
out.print(cmd + NEWLINE);
|
||||
out.write(cmd + NEWLINE);
|
||||
String reply = readCommand();
|
||||
return parseReturnCode(reply);
|
||||
}
|
||||
|
|
|
|||
135
test/zutil/net/smtp/EmailTest.java
Normal file
135
test/zutil/net/smtp/EmailTest.java
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package zutil.net.smtp;
|
||||
|
||||
import org.junit.Test;
|
||||
import zutil.io.StringOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static zutil.net.smtp.SMTPClient.NEWLINE;
|
||||
|
||||
/**
|
||||
* Created by Ziver on 2017-01-19.
|
||||
*/
|
||||
public class EmailTest {
|
||||
|
||||
@Test
|
||||
public void sanitizingFrom(){
|
||||
Email email = new Email();
|
||||
|
||||
email.setFrom("aa\n@aa.aa"+NEWLINE);
|
||||
assertEquals("aa@aa.aa", email.getFromAddress());
|
||||
|
||||
email.setFrom("aa\n@aa.aa"+NEWLINE, "aa\n bb"+NEWLINE);
|
||||
assertEquals("aa@aa.aa", email.getFromAddress());
|
||||
assertEquals("aa bb", email.getFromName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanitizingReplyTo(){
|
||||
Email email = new Email();
|
||||
|
||||
email.setReplyTo("aa\n@aa.aa"+NEWLINE);
|
||||
assertEquals("aa@aa.aa", email.getReplyToAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanitizingTo(){
|
||||
Email email = new Email();
|
||||
|
||||
email.setTo("aa\n@aa.aa"+NEWLINE);
|
||||
assertEquals("aa@aa.aa", email.getToAddress());
|
||||
|
||||
email.setTo("aa\n@aa.aa"+NEWLINE, "aa\n bb"+NEWLINE);
|
||||
assertEquals("aa@aa.aa", email.getToAddress());
|
||||
assertEquals("aa bb", email.getToName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanitizingSubject(){
|
||||
Email email = new Email();
|
||||
|
||||
email.setSubject("aa\n aa aa"+NEWLINE);
|
||||
assertEquals("aa aa aa", email.getSubject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanitizingMessage(){
|
||||
Email email = new Email();
|
||||
|
||||
email.setMessage("aa\nbb"+ NEWLINE +"cc\n");
|
||||
assertEquals("aa"+ NEWLINE +"bb"+ NEWLINE +"cc"+ NEWLINE,
|
||||
email.getMessage());
|
||||
|
||||
email.setMessage("aa"+ NEWLINE +"."+ NEWLINE +"bb");
|
||||
assertEquals("aa"+ NEWLINE +".."+ NEWLINE +"bb",
|
||||
email.getMessage());
|
||||
|
||||
email.setMessage("aa\n.\nbb");
|
||||
assertEquals("aa"+ NEWLINE +".."+ NEWLINE +"bb",
|
||||
email.getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void simpleEmail() throws IOException {
|
||||
Email email = new Email();
|
||||
email.setFrom("test@example.com");
|
||||
email.setTo("to@example.com");
|
||||
email.setDate(getDate());
|
||||
email.setMessage("message");
|
||||
|
||||
assertEquals(
|
||||
"From: test@example.com" + NEWLINE +
|
||||
"To: to@example.com" + NEWLINE +
|
||||
"Date: on, 22 nov 2000 15:20:55 +0100" + NEWLINE +
|
||||
"Content-Type: text/plain;" + NEWLINE +
|
||||
"Subject: " + NEWLINE +
|
||||
NEWLINE +
|
||||
"message",
|
||||
getEmailString(email));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullEmail() throws IOException {
|
||||
Email email = new Email();
|
||||
email.setFrom("test@example.com", "Test Tester");
|
||||
email.setTo("to@example.com", "To Totter");
|
||||
email.setDate(getDate());
|
||||
email.setContentType(Email.ContentType.HTML);
|
||||
email.setReplyTo("mokey@example.org");
|
||||
email.setSubject("Title");
|
||||
email.setMessage("<html>\n<body>\n<b>message</b>\n</body>\n</html>");
|
||||
|
||||
assertEquals(
|
||||
"From: Test Tester <test@example.com>" + NEWLINE +
|
||||
"Reply-To: <mokey@example.org>" + NEWLINE +
|
||||
"To: To Totter <to@example.com>" + NEWLINE +
|
||||
"Date: on, 22 nov 2000 15:20:55 +0100" + NEWLINE +
|
||||
"Content-Type: text/html;" + NEWLINE +
|
||||
"Subject: Title" + NEWLINE +
|
||||
NEWLINE +
|
||||
"<html>"+NEWLINE+"<body>"+NEWLINE+"<b>message</b>"+NEWLINE+"</body>"+NEWLINE+"</html>",
|
||||
getEmailString(email));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private String getEmailString(Email email) throws IOException {
|
||||
StringOutputStream buff = new StringOutputStream();
|
||||
OutputStreamWriter out = new OutputStreamWriter(buff);
|
||||
email.write(out);
|
||||
out.flush();
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
private Date getDate(){
|
||||
GregorianCalendar date = new GregorianCalendar(2000,10,22, 15,20,55);
|
||||
return date.getTime();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue