X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2FWhitelist.java;h=9842320d23bdaa8f05e21f1bb67d2a11f01b290f;hb=7e5f113841114ac4b0ef469ef286bd31fd9da6cb;hp=06724fed9d1f5244bc43a44c9fecc090c1209667;hpb=d551e8ab37c017805f39ee0d089e073eb2f18e0c;p=org.ibex.mail.git diff --git a/src/org/ibex/mail/Whitelist.java b/src/org/ibex/mail/Whitelist.java index 06724fe..9842320 100644 --- a/src/org/ibex/mail/Whitelist.java +++ b/src/org/ibex/mail/Whitelist.java @@ -1,8 +1,10 @@ package org.ibex.mail; import org.ibex.io.*; +import org.ibex.net.*; import org.ibex.mail.protocol.*; import org.ibex.util.*; +import org.ibex.net.*; import java.sql.*; import java.net.*; import java.io.*; @@ -10,78 +12,59 @@ import java.util.*; import java.sql.Timestamp; import java.sql.Connection; -// now all I need is the click-through page +public class Whitelist extends SqliteDB { -// FIXME: periodic cleanup -public class Whitelist { - - private Connection conn; - - // FIXME very ugly - static { - new Thread() { public void run() { startWebServer(); } }.start(); + public Whitelist(String filename) throws SQLException { + super(filename); + SqliteTable whitelist = getTable("whitelist", "(email)"); + whitelist.createIndex("email"); + SqliteTable pending = getTable("pending", "(spamid,email,message,date)"); + pending.reap("date"); + pending.createIndex("spamid"); + pending.createIndex("email"); } - public static void startWebServer() { + + public boolean handleRequest(org.ibex.net.Connection c) { try { - ServerSocket ss = new ServerSocket(8025); - while(true) { - final Socket sock = ss.accept(); - new Thread() { - public void run() { - try { - BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); - String s = br.readLine(); - String url = s.substring(s.indexOf(' ')+1); - url = url.substring(0, url.indexOf(' ')); - while(s!=null && !s.equals("")) - s = br.readLine(); - PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); - if (!url.startsWith("/whitelist/")) { - pw.print("HTTP/1.0 404 Not FoundK\r\n"); - pw.print("Content-Type: text/plain\r\n"); - pw.print("\r\n"); - pw.println("you are lost."); - } else { - url = url.substring("/whitelist/".length()); - url = URLDecoder.decode(url); - pw.print("HTTP/1.0 200 OK\r\n"); - pw.print("Content-Type: text/plain\r\n"); - pw.print("\r\n"); - try { - SMTP.whitelist.response(url); - pw.println("Thanks! You've been added to my list of non-spammers and your message"); - pw.println("has been moved to my inbox."); - pw.println("email id " + url); - pw.println(""); - } catch (Exception e) { - e.printStackTrace(pw); - } - } - pw.flush(); - sock.close(); - } catch (Exception e) { throw new RuntimeException(e); } - } - }.start(); + Socket sock = c.getSocket(); + BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); + String s = br.readLine(); + String url = s.substring(s.indexOf(' ')+1); + url = url.substring(0, url.indexOf(' ')); + while(s!=null && !s.equals("")) + s = br.readLine(); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); + if (!url.startsWith("/whitelist/")) { + pw.print("HTTP/1.0 404 Not Found\r\n"); + pw.print("Content-Type: text/plain\r\n"); + pw.print("\r\n"); + pw.println("you are lost."); + } else { + url = url.substring("/whitelist/".length()); + url = URLDecoder.decode(url); + if (url.endsWith(".txt")) url = url.substring(0, url.length()-4); + pw.print("HTTP/1.0 200 OK\r\n"); + pw.print("Content-Type: text/plain\r\n"); + pw.print("\r\n"); + try { + SMTP.whitelist.response(url); + pw.println("Thanks! You've been added to my list of non-spammers and your message"); + pw.println("has been moved to my inbox."); + pw.println("email id " + url); + pw.println(""); + } catch (Exception e) { + e.printStackTrace(pw); + } } + pw.flush(); + sock.close(); } catch (Exception e) { throw new RuntimeException(e); } - } - - - public Whitelist(String filename) { - try { - Class.forName("org.sqlite.JDBC"); - conn = DriverManager.getConnection("jdbc:sqlite:"+filename); - conn.prepareStatement("create table if not exists "+ - "'whitelist' (email)").executeUpdate(); - conn.prepareStatement("create table if not exists "+ - "'pending' (spamid,email,message,date)").executeUpdate(); - } - catch (SQLException e) { throw new RuntimeException(e); } - catch (ClassNotFoundException e) { throw new RuntimeException(e); } + return true; } public synchronized boolean isWhitelisted(Address a) { try { + if (a==null) return false; PreparedStatement check = conn.prepareStatement("select * from 'whitelist' where email=?"); check.setString(1, a.toString(false).toLowerCase()); ResultSet rs = check.executeQuery(); @@ -104,17 +87,46 @@ public class Whitelist { ResultSet rs = query.executeQuery(); if (!rs.next()) throw new RuntimeException("could not find messageid \""+messageid+"\""); - do { - addWhitelist(Address.parse(rs.getString(1))); - Message m = Message.newMessage(new Fountain.StringFountain(rs.getString(2))); + HashSet hsm = new HashSet(); + synchronized(this) { + do { + addWhitelist(Address.parse(rs.getString(1))); + Message m = Message.newMessage(new Fountain.StringFountain(rs.getString(2))); + Address a = m.headers.get("reply-to")==null ? null : Address.parse(m.headers.get("reply-to")); + if (a!=null) addWhitelist(a); + a = m.from; + if (a!=null) addWhitelist(a); + a = m.envelopeFrom; + if (a!=null) addWhitelist(a); + hsm.add(m); + if (m.cc != null) for(Address aa : m.cc) { + if (aa!= null) addWhitelist(aa); + } + } while (rs.next()); + } + for(Message m : hsm) Target.root.accept(m); - } while (rs.next()); } catch (SQLException e) { throw new RuntimeException(e); } } - public synchronized void challenge(Message m) { + public void challenge(Message m) { try { + // FIXME: don't challenge emails with binaries in them; + // reject them outright and have the sender send an + // initial message w/o a binary. + + // FIXME: use Auto here!!! + // The challenge should refer to the message-id of the mail being challenged. + + // FIXME: watch outgoing MessageID's: if something comes + // back with an In-Reply-To mentioning a MessageID from + // the last few days, auto-whitelist them. + + // FIXME: important that "From" on the challenge matches + // RCPT TO on the original message. + Log.warn(Whitelist.class, "challenging message: " + m.summary()); + Address to = m.headers.get("reply-to")==null ? null : Address.parse(m.headers.get("reply-to")); if (to==null) to = m.from; if (to==null) to = m.envelopeFrom; @@ -129,6 +141,10 @@ public class Whitelist { "with Auto-Submitted=\""+m.headers.get("Auto-Submitted")+"\""); return; } + if (m.headers.get("List-Id") != null || m.headers.get("List-Post") != null) { + Log.warn(this, "refusing to send a challenge to a message with a List-Id or List-Post header"); + return; + } Address from = Address.parse("adam@megacz.com"); @@ -137,7 +153,7 @@ public class Whitelist { messageid = messageid.replace('%','_'); Log.warn(Whitelist.class, "got challenge for: " + messageid); - String url = "http://www.megacz.com:8025/whitelist/"+URLEncoder.encode(messageid); + String url = "http://www.megacz.com:8025/whitelist/"+URLEncoder.encode(messageid)+".txt"; String message = "Return-Path: <>" + "\r\n" + "Envelope-To: " + to + "\r\n" + @@ -186,23 +202,33 @@ public class Whitelist { " http://www.templetons.com/brad/spam/crgood.html\n"; Message challenge = Message.newMessage(new Fountain.StringFountain(message)); - if (!SMTP.Outgoing.attempt(challenge)) - throw new RuntimeException("attempted to send challenge but could not: " + m.summary()); - - PreparedStatement add = conn.prepareStatement("insert into pending values(?,?,?,?)"); - add.setString(1, messageid); - add.setString(2, to.toString(false)); - add.setString(3, streamToString(m.getStream())); - add.setTimestamp(4, new Timestamp(System.currentTimeMillis())); - add.executeUpdate(); + + boolean send = false; + synchronized(this) { + PreparedStatement query = conn.prepareStatement("select email from pending where email=?"); + query.setString(1, to.toString(false)); + ResultSet rs = query.executeQuery(); + if (rs.next()) { + Log.warn(this, "already challenged " + to.toString(false) + "; not challenging again."); + } else { + send = true; + } + } + + if (send) + if (!SMTP.Outgoing.attempt(challenge)) + throw new RuntimeException("attempted to send challenge but could not: " + m.summary()); + + synchronized(this) { + PreparedStatement add = conn.prepareStatement("insert into pending values(?,?,?,?)"); + add.setString(1, messageid); + add.setString(2, to.toString(false)); + add.setString(3, SqliteDB.streamToString(m.getStream())); + add.setTimestamp(4, new Timestamp(System.currentTimeMillis())); + add.executeUpdate(); + } } catch (Exception e) { throw new RuntimeException(e); } } - private static String streamToString(Stream stream) throws Exception { - StringBuffer b = new StringBuffer(); - for(String s = stream.readln(); s!=null; s=stream.readln()) - b.append(s+"\n"); - return b.toString(); - } }