X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2FSMTP.java;h=039b3a7f176b66bd10c2d0127f1c1fd3f8280f96;hb=bea75eed0419e5888bda70d9cb1f3951a1a6d510;hp=5a735540cfd852e79ce7304c783ba53dad5ab14a;hpb=80d159654238da2dc2f1dd1c946bcaf2785b7ffd;p=org.ibex.mail.git diff --git a/src/org/ibex/mail/SMTP.java b/src/org/ibex/mail/SMTP.java index 5a73554..039b3a7 100644 --- a/src/org/ibex/mail/SMTP.java +++ b/src/org/ibex/mail/SMTP.java @@ -14,6 +14,8 @@ import java.text.*; import javax.naming.*; import javax.naming.directory.*; +// FIXME: inbound throttling/ratelimiting + // RFC's implemented // RFC2554: SMTP Service Extension for Authentication // - did not implement section 5, though @@ -27,6 +29,9 @@ import javax.naming.directory.*; // FIXME: loop prevention // FIXME: probably need some throttling on outbound mail +// FEATURE: public static boolean validate(Address a) +// FEATURE: rate-limiting + // FEATURE: infer messageid, date, if not present (?) // FEATURE: exponential backoff on retry time? // FEATURE: RFC2822, section 4.5.1: special "postmaster" address @@ -51,15 +56,15 @@ public class SMTP { Integer.parseInt(System.getProperty("org.ibex.mail.smtp.maxMessageSize", "-1")); private static final Mailbox spool = - FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT,false).slash("spool",true).slash("smtp",true); + FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT,false).slash("spool",true).slash("smtp",true).getMailbox(); static { for(int i=0; i") ? null : new Address(command); conn.println("250 " + from + " is syntactically correct"); + // FEATURE: perform SMTP validation on the address, reject if invalid } else if (c.startsWith("RCPT TO:")) { // some clients are broken and put RCPT first; we will tolerate this command = command.substring(8).trim(); if(command.indexOf(' ') != -1) command = command.substring(0, command.indexOf(' ')); Address addr = new Address(command); - /* - Log.warn("**"+conn.getRemoteAddress()+"**", - "addr.isLocal(): " + addr.isLocal() + "\n" + - "conn.getRemoteAddress().isLoopbackAddress(): " + conn.getRemoteAddress().isLoopbackAddress() + "\n" + - "johnw: " + (from!=null&&from.toString().indexOf("johnw")!=-1) + "\n" - ); - */ - if (addr.isLocal()) { - // FEATURE: should check the address further and give 550 if undeliverable - conn.println("250 " + addr + " is on this machine; I will deliver it"); - to.addElement(addr); - } else if (conn.getRemoteAddress().isLoopbackAddress() || (from!=null&&from.toString().indexOf("johnw")!=-1)) { + if (conn.getRemoteAddress().isLoopbackAddress() || (from!=null&&from.toString().indexOf("johnw")!=-1)) { conn.println("250 you are connected locally, so I will let you send"); to.addElement(addr); + if (!whitelist.isWhitelisted(addr)) + whitelist.addWhitelist(addr); + } else if (authenticatedAs!=null) { + conn.println("250 you are authenticated as "+authenticatedAs+", so I will let you send"); + to.addElement(addr); + if (!whitelist.isWhitelisted(addr)) + whitelist.addWhitelist(addr); + } else if (addr.isLocal()) { + if (to.size() > 3) { + conn.println("536 sorry, limit on 3 RCPT TO's per DATA"); + } else { + // FEATURE: should check the address further and give 550 if undeliverable + conn.println("250 " + addr + " is on this machine; I will deliver it"); + to.addElement(addr); + } } else { - conn.println("551 sorry, " + addr + " is not on this machine"); + conn.println("535 sorry, " + addr + " is not on this machine, you are not connected from localhost, and I will not relay without SMTP AUTH"); + Log.warn("","535 sorry, " + addr + " is not on this machine, you are not connected from localhost, and I will not relay without SMTP AUTH"); + failedRcptCount++; + if (failedRcptCount > 3) { + conn.close(); + return; + } } conn.flush(); } else if (c.startsWith("DATA")) { //if (from == null) { conn.println("503 MAIL FROM command must precede DATA"); continue; } if (to == null || to.size()==0) { conn.println("503 RCPT TO command must precede DATA"); continue; } - if (!graylist.isWhitelisted(conn.getRemoteAddress()) && !conn.getRemoteAddress().isLoopbackAddress()) { + if (!graylist.isWhitelisted(conn.getRemoteAddress()) && !conn.getRemoteAddress().isLoopbackAddress() && authenticatedAs==null) { long when = graylist.getGrayListTimestamp(conn.getRemoteAddress(), from+"", to+""); if (when == 0 || System.currentTimeMillis() - when > GRAYLIST_MAXWAIT) { graylist.setGrayListTimestamp(conn.getRemoteAddress(), from+"", to+"", System.currentTimeMillis()); @@ -162,6 +236,7 @@ public class SMTP { } conn.flush(); try { + // FIXME: deal with messages larger than memory here? StringBuffer buf = new StringBuffer(); buf.append("Received: from " + conn.getRemoteHostname() + " (" + remotehost + ")\r\n"); buf.append(" by "+conn.vhost+" ("+SMTP.class.getName()+") with "+(ehlo?"ESMTP":"SMTP") + "\r\n"); @@ -169,13 +244,15 @@ public class SMTP { // FIXME: this is leaking BCC addrs // for(int i=0; i MAX_MESSAGE_SIZE) { + if (MAX_MESSAGE_SIZE != -1 && buf.length() > MAX_MESSAGE_SIZE && (from+"").indexOf("paperless")==-1) { Log.error("**"+conn.getRemoteAddress()+"**", "sorry, this mail server only accepts messages of less than " + ByteSize.toString(MAX_MESSAGE_SIZE)); @@ -183,12 +260,10 @@ public class SMTP { ByteSize.toString(MAX_MESSAGE_SIZE)); } } - String body = buf.toString(); + String message = buf.toString(); Message m = null; - for(int i=0; i 1000 * 60 * 60 * 24 * 5) { if (!noBounces) { - accept(m.bounce("could not send for 5 days")); + enqueue(m.bounce("could not send for 5 days")); return true; } else { Log.warn(SMTP.Outgoing.class, "could not send for 5 days: " + m.summary()); @@ -257,7 +332,7 @@ public class SMTP { } for(int i=0; i 3 && s.charAt(3) == '-') s = conn.readln(); - if (s.startsWith("4")||s.startsWith("5")) throw new SMTPException(s); + //if (s.startsWith("4")||s.startsWith("5")) throw new SMTPException(s); + if (!s.startsWith("2")&&!s.startsWith("3")) throw new SMTPException(s); } private static boolean attempt(final Message m, final InetAddress mx) { boolean accepted = false; Connection conn = null; try { - Log.note("connecting to " + mx + "..."); conn = new Connection(new Socket(mx, 25), InetAddress.getLocalHost().getHostName()); conn.setNewline("\r\n"); conn.setTimeout(60 * 1000); - Log.note(" connected"); check(conn.readln(), conn); // banner try { conn.println("EHLO " + conn.vhost); @@ -284,18 +358,16 @@ public class SMTP { conn.println("HELO " + conn.vhost); check(conn.readln(), conn); } - if (m.envelopeFrom==null) { - Log.warn("", "MAIL FROM:<>"); - conn.println("MAIL FROM:<>"); check(conn.readln(), conn); - } else { - Log.warn("", "MAIL FROM:<" + m.envelopeFrom.toString()+">"); - conn.println("MAIL FROM:<" + m.envelopeFrom.toString()+">"); check(conn.readln(), conn); - } - conn.println("RCPT TO:<" + m.envelopeTo.toString()+">"); check(conn.readln(), conn); - conn.println("DATA"); check(conn.readln(), conn); - Headers head = m.headers; - head = head.remove("return-path"); - head = head.remove("bcc"); + String envelopeFrom = m.envelopeFrom==null ? "" : m.envelopeFrom.toString(); + conn.println("MAIL FROM:<" + envelopeFrom +">"); check(conn.readln(), conn); + conn.println("RCPT TO:<" + m.envelopeTo.toString()+">"); check(conn.readln(), conn); + conn.println("DATA"); check(conn.readln(), conn); + + Headers head = new Headers(m.headers, + new String[] { + "return-path", null, + "bcc", null + }); Stream stream = head.getStream(); for(String s = stream.readln(); s!=null; s=stream.readln()) { if (s.startsWith(".")) conn.print("."); @@ -320,6 +392,8 @@ public class SMTP { Log.warn(SMTP.Outgoing.class, " unable to send; error=" + e); Log.warn(SMTP.Outgoing.class, " message: " + m.summary()); Log.warn(SMTP.Outgoing.class, e); + /* + // FIXME: we should not be bouncing here! if (e.code >= 500 && e.code <= 599) { try { attempt(m.bounce("unable to deliver: " + e), true); @@ -329,13 +403,14 @@ public class SMTP { } return true; } + */ return false; } catch (Exception e) { if (accepted) return true; Log.warn(SMTP.Outgoing.class, " unable to send; error=" + e); Log.warn(SMTP.Outgoing.class, " message: " + m.summary()); Log.warn(SMTP.Outgoing.class, e); - if (conn != null) Log.warn(SMTP.Outgoing.class, conn.dumpLog()); + //if (conn != null) Log.warn(SMTP.Outgoing.class, conn.dumpLog()); return false; } finally { if (conn != null) conn.close();