--- /dev/null
+package org.ibex.mail;
+
+import org.ibex.io.Fountain;
+import org.ibex.io.Stream;
+import java.sql.Timestamp;
+import java.sql.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+// nntpNumber (column)
+// uid (column)
+// uidvalidity
+public class SqliteMailbox extends Mailbox.Default {
+
+ private Connection conn;
+ private static final String columns =
+ " messageid_, from_,to_,date_,subject_,headers_,body_,flags_";
+
+ /**
+ * from http://www.sqlite.org/autoinc.html
+ * "If a column has the type INTEGER PRIMARY KEY AUTOINCREMENT
+ * then a slightly different ROWID selection algorithm is
+ * used. The ROWID chosen for the new row is one larger than the
+ * largest ROWID that has ever before existed in that same
+ * table. If the table has never before contained any data, then
+ * a ROWID of 1 is used. If the table has previously held a row
+ * with the largest possible ROWID, then new INSERTs are not
+ * allowed and any attempt to insert a new row will fail with an
+ * SQLITE_FULL error.
+ */
+ private static final String columns_ =
+ "uid_ INTEGER PRIMARY KEY AUTOINCREMENT, messageid_ unique,from_,to_,date_,subject_,headers_,body_,flags_";
+
+ public SqliteMailbox(String filename) {
+ try {
+ Class.forName("org.sqlite.JDBC");
+ conn = DriverManager.getConnection("jdbc:sqlite:"+filename);
+ conn.prepareStatement("create virtual table if not exists 'mail' using FTS2("+columns_+")").executeUpdate();
+ }
+ catch (SQLException e) { throw new RuntimeException(e); }
+ catch (ClassNotFoundException e) { throw new RuntimeException(e); }
+ }
+
+ public int uidNext() { throw new RuntimeException("not supported"); }
+ public Mailbox.Iterator iterator() { return new SqliteJdbcIterator(); }
+ public void insert(Message m, int flags) {
+ // FIXME: flags
+ try {
+ PreparedStatement add =
+ conn.prepareStatement("insert into 'mail' ("+columns+") values (?,?,?,?,?,?,?,?)");
+ add.setString(1, m.messageid+"");
+ add.setString(2, m.from+"");
+ add.setString(3, m.to+"");
+ add.setString(4, m.date+"");
+ add.setString(5, m.subject+"");
+ add.setString(6, streamToString(m.headers.getStream()));
+ add.setString(7, streamToString(m.getBody().getStream()));
+ add.setInt (8, flags);
+ add.executeUpdate();
+ } catch (Exception e) { throw new RuntimeException(e); }
+ }
+
+ private class SqliteJdbcIterator extends Mailbox.Default.Iterator {
+ // could be more efficient in a ton of ways
+ private ResultSet rs;
+ private int count = 1;
+ private int flags;
+ private Message m = null;
+ public SqliteJdbcIterator() {
+ try {
+ PreparedStatement query = conn.prepareStatement("select messageid_ from 'mail'");
+ rs = query.executeQuery();
+ rs.next();
+ } catch (Exception e) { throw new RuntimeException(e); }
+ }
+ public Message cur() {
+ try {
+ if (m!=null) return m;
+ rs.next();
+ PreparedStatement query = conn.prepareStatement("select headers_,body_,flags_ from 'mail' where messageid_=?");
+ query.setString(1, rs.getString(1));
+ ResultSet rs2 = query.executeQuery();
+ if (!rs.next()) return null;
+ m = Message.newMessage(Fountain.Util.concat(Fountain.Util.create(rs.getString(1)),
+ Fountain.Util.create("\r\n\r\n"),
+ Fountain.Util.create(rs.getString(2))));
+ flags = rs.getInt(3);
+ return m;
+ } catch (Exception e) { throw new RuntimeException(e); }
+ }
+ public int getFlags() { if (m==null) /* could be more efficient */ cur(); return flags; }
+ public Headers head() { return cur().headers; }
+ public boolean next() { try { m = null; count++; return rs.next(); } catch (Exception e) { throw new RuntimeException(e); } }
+ public int uid() { throw new RuntimeException("not supported"); }
+ public int imapNumber() { return count; }
+ public int nntpNumber() { throw new RuntimeException("not supported"); }
+ public void delete() { throw new RuntimeException("not supported"); }
+ }
+
+ 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();
+ }
+
+}