X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2FSMTP.java;h=f8bcf0b5f03fc298625f49f2c76d95908f66e4f8;hb=31f6bfd3b57924e8c3d506592132e91e73145462;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..f8bcf0b 100644 --- a/src/org/ibex/mail/SMTP.java +++ b/src/org/ibex/mail/SMTP.java @@ -27,6 +27,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 @@ -58,8 +61,8 @@ public class SMTP { new Outgoing().start(); } - public static void accept(Message m) throws IOException { - if (!m.envelopeTo.isLocal()) Outgoing.accept(m); + public static void enqueue(Message m) throws IOException { + if (!m.envelopeTo.isLocal()) Outgoing.enqueue(m); else Target.root.accept(m); } @@ -85,14 +88,15 @@ public class SMTP { public void handleRequest(Connection conn) throws IOException { conn.setTimeout(5 * 60 * 1000); conn.setNewline("\r\n"); - conn.println("220 " + conn.vhost + " SMTP " + this.getClass().getName()); + conn.println("220 " + conn.vhost + " ESMTP " + this.getClass().getName()); Address from = null; Vector to = new Vector(); boolean ehlo = false; String remotehost = null; + String authenticatedAs = null; for(String command = conn.readln(); ; command = conn.readln()) try { if (command == null) return; - //Log.warn("**"+conn.getRemoteAddress()+"**", command); + Log.warn("**"+conn.getRemoteAddress()+"**", command); String c = command.toUpperCase(); if (c.startsWith("HELO")) { remotehost = c.substring(5).trim(); @@ -100,7 +104,11 @@ public class SMTP { from = null; to = new Vector(); } else if (c.startsWith("EHLO")) { remotehost = c.substring(5).trim(); - conn.println("250 "+conn.vhost+" greets " + remotehost); + conn.println("250-"+conn.vhost); + //conn.println("250-AUTH"); + conn.println("250-AUTH PLAIN"); + //conn.println("250-STARTTLS"); + conn.println("250 HELP"); ehlo = true; from = null; to = new Vector(); } else if (c.startsWith("RSET")) { conn.println("250 reset ok"); from = null; to = new Vector(); @@ -109,6 +117,58 @@ public class SMTP { } else if (c.startsWith("EXPN")) { conn.println("502 EXPN not supported"); } else if (c.startsWith("NOOP")) { conn.println("250 OK"); } else if (c.startsWith("QUIT")) { conn.println("221 " + conn.vhost + " closing connection"); return; + } else if (c.startsWith("STARTTLS")) { + conn.println("220 starting TLS..."); + conn.flush(); + conn = conn.negotiateSSL(true); + from = null; to = new Vector(); + } else if (c.startsWith("AUTH")) { + if (authenticatedAs != null) { + conn.println("503 you are already authenticated; you must reconnect to reauth"); + } else { + String mechanism = command.substring(4).trim(); + String rest = ""; + if (mechanism.indexOf(' ')!=-1) { + rest = mechanism.substring(mechanism.indexOf(' ')+1).trim(); + mechanism = mechanism.substring(0, mechanism.indexOf(' ')); + } + if (mechanism.equals("PLAIN")) { + // 538 Encryption required for requested authentication mechanism? + byte[] bytes = Encode.fromBase64(rest); + String authenticateUser = null; + String authorizeUser = null; + String password = null; + int start = 0; + for(int i=0; i<=bytes.length; i++) { + if (i") ? null : new Address(command); @@ -118,13 +178,6 @@ public class SMTP { 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"); @@ -132,14 +185,20 @@ public class SMTP { } else 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); + whitelist.addWhitelist(addr); + } else if (authenticatedAs!=null) { + conn.println("250 you are authenticated as "+authenticatedAs+", so I will let you send"); + to.addElement(addr); + whitelist.addWhitelist(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"); } 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 +221,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,6 +229,8 @@ public class SMTP { // FIXME: this is leaking BCC addrs // 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()); @@ -271,11 +333,9 @@ public class SMTP { 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 +344,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(".");