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.*;
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();
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<Message> hsm = new HashSet<Message>();
+ 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"));
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" +
Message challenge = Message.newMessage(new Fountain.StringFountain(message));
- 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 {
+ 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());
- }
- 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();
+ 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();
- }
}