Author: dbkr
Date: 2006-07-28 17:37:32 +0000 (Fri, 28 Jul 2006)
New Revision: 9799
Added:
trunk/apps/Freemail/src/freemail/HashSlotManager.java
trunk/apps/Freemail/src/freemail/NIMContact.java
trunk/apps/Freemail/src/freemail/Postman.java
trunk/apps/Freemail/src/freemail/SlotManager.java
trunk/apps/Freemail/src/freemail/SlotSaveCallback.java
Removed:
trunk/apps/Freemail/src/freemail/Contact.java
Modified:
trunk/apps/Freemail/src/freemail/AccountManager.java
trunk/apps/Freemail/src/freemail/InboundContact.java
trunk/apps/Freemail/src/freemail/NIMFetcher.java
trunk/apps/Freemail/src/freemail/OutboundContact.java
trunk/apps/Freemail/src/freemail/RTSFetcher.java
trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java
trunk/apps/Freemail/src/freemail/utils/PropsFile.java
Log:
It lives! (That is: implement message polling and ACK sending). One more
protocol change planned, and some changes in the directory format, so don't
bank on not having to wipe the directory and start again *just yet*. ;)
Modified: trunk/apps/Freemail/src/freemail/AccountManager.java
===================================================================
--- trunk/apps/Freemail/src/freemail/AccountManager.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/AccountManager.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -63,7 +63,7 @@
if (!nimdir.mkdir()) throw new IOException("Failed to
create nim directory");
}
- File keyfile = new File(nimdir, Contact.KEYFILE);
+ File keyfile = new File(nimdir, NIMContact.KEYFILE);
PrintWriter pw = new PrintWriter(new FileOutputStream(keyfile));
pw.println(MessageSender.NIM_KEY_PREFIX + username + "-");
Deleted: trunk/apps/Freemail/src/freemail/Contact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/Contact.java 2006-07-27 23:59:06 UTC
(rev 9798)
+++ trunk/apps/Freemail/src/freemail/Contact.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -1,50 +0,0 @@
-package freemail;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.FileReader;
-import java.io.BufferedReader;
-import java.util.Date;
-
-import freemail.utils.DateStringFactory;
-
-public class Contact {
- public static final String KEYFILE = "key";
- private static final String LOGFILE_PREFIX = "log-";
- private final File contact_dir;
- private final File keyfile;
-
- Contact(File dir) {
- this.contact_dir = dir;
- this.keyfile = new File(dir, KEYFILE);
- }
-
- public String getKey() throws IOException {
- FileReader frdr = new FileReader(this.keyfile);
- BufferedReader br = new BufferedReader(frdr);
- String key = br.readLine();
- frdr.close();
- return key;
- }
-
- public MailLog getLog(String date) {
- return new MailLog(new File(this.contact_dir, LOGFILE_PREFIX +
date));
- }
-
- public void pruneLogs(Date keepafter) {
- File[] files = contact_dir.listFiles();
-
- int i;
- for (i = 0; i< files.length; i++) {
- if (!files[i].getName().startsWith(LOGFILE_PREFIX))
- continue;
- Date logdate =
DateStringFactory.DateFromKeyString(files[i].getName().substring(LOGFILE_PREFIX.length()));
- if (logdate == null) {
- // couldn't parse the date... hmm
- files[i].delete();
- } else if (logdate.before(keepafter)) {
- files[i].delete();
- }
- }
- }
-}
Added: trunk/apps/Freemail/src/freemail/HashSlotManager.java
===================================================================
--- trunk/apps/Freemail/src/freemail/HashSlotManager.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/HashSlotManager.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -0,0 +1,20 @@
+package freemail;
+
+import org.archive.util.Base32;
+
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+public class HashSlotManager extends SlotManager {
+ HashSlotManager(SlotSaveCallback cb, Object userdata, String slotlist) {
+ super(cb, userdata, slotlist);
+ }
+
+ protected String incSlot(String slot) {
+ byte[] buf = Base32.decode(slot);
+ SHA256Digest sha256 = new SHA256Digest();
+ sha256.update(buf, 0, buf.length);
+ sha256.doFinal(buf, 0);
+
+ return Base32.encode(buf);
+ }
+}
Modified: trunk/apps/Freemail/src/freemail/InboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/InboundContact.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/InboundContact.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -1,12 +1,21 @@
package freemail;
import java.io.File;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
import freemail.FreenetURI;
import freemail.utils.PropsFile;
+import freemail.fcp.HighLevelFCPClient;
-public class InboundContact {
+public class InboundContact extends Postman implements SlotSaveCallback {
private static final String IBCT_PROPSFILE = "props";
+ // how many slots should we poll past the last occupied one?
+ private static final int POLL_AHEAD = 6;
private File ibct_dir;
private PropsFile ibct_props;
@@ -14,7 +23,7 @@
this(contact_dir, mailsite.getKeyBody());
}
- private InboundContact(File contact_dir, String keybody) {
+ public InboundContact(File contact_dir, String keybody) {
this.ibct_dir = new File(contact_dir, keybody);
if (!this.ibct_dir.exists()) {
@@ -31,4 +40,136 @@
public String getProp(String key) {
return this.ibct_props.get(key);
}
+
+ public void fetch(MessageBank mb) {
+ HighLevelFCPClient fcpcli = new HighLevelFCPClient();
+
+ String slots = this.ibct_props.get("slots");
+ if (slots == null) {
+ System.out.println("Contact "+this.ibct_dir.getName()+"
is corrupt - account file has no 'slots' entry!");
+ // TODO: probably delete the contact. it's useless now.
+ return;
+ }
+
+ HashSlotManager sm = new HashSlotManager(this, null, slots);
+ sm.setPollAhead(POLL_AHEAD);
+
+ String basekey = this.ibct_props.get("commssk");
+ if (basekey == null) {
+ System.out.println("Contact "+this.ibct_dir.getName()+"
is corrupt - account file has no 'commssk' entry!");
+ // TODO: probably delete the contact. it's useless now.
+ return;
+ }
+ String slot;
+ while ( (slot = sm.getNextSlot()) != null) {
+ String key = basekey+slot;
+
+ System.out.println("Attempting to fetch mail on key
"+key);
+ File msg = fcpcli.fetch(key);
+ if (msg == null) {
+ System.out.println("No mail there.");
+ continue;
+ }
+ System.out.println("Found a message!");
+
+ // parse the Freemail header(s) out.
+ PropsFile msgprops = new PropsFile(msg, true);
+ String s_id = msgprops.get("id");
+ if (s_id == null) {
+ System.out.println("Got a message with an
invalid header. Discarding.");
+ sm.slotUsed();
+ msgprops.closeReader();
+ msg.delete();
+ continue;
+ }
+
+ int id;
+ try {
+ id = Integer.parseInt(s_id);
+ } catch (NumberFormatException nfe) {
+ System.out.println("Got a message with an
invalid (non-integer) id. Discarding.");
+ sm.slotUsed();
+ msgprops.closeReader();
+ msg.delete();
+ continue;
+ }
+
+ MessageLog msglog = new MessageLog(this.ibct_dir);
+ boolean isDupe;
+ try {
+ isDupe = msglog.isPresent(id);
+ } catch (IOException ioe) {
+ System.out.println("Couldn't read logfile, so
don't know whether received message is a duplicate or not. Leaving in the queue
to try later.");
+ msgprops.closeReader();
+ msg.delete();
+ continue;
+ }
+ if (isDupe) {
+ System.out.println("Got a message, but we've
already logged that message ID as received. Discarding.");
+ sm.slotUsed();
+ msgprops.closeReader();
+ msg.delete();
+ continue;
+ }
+
+ BufferedReader br = msgprops.getReader();
+ if (br == null) {
+ System.out.println("Got an invalid message.
Discarding.");
+ sm.slotUsed();
+ msgprops.closeReader();
+ msg.delete();
+ continue;
+ }
+
+ try {
+ this.storeMessage(br, mb);
+ msg.delete();
+ } catch (IOException ioe) {
+ msg.delete();
+ continue;
+ }
+ System.out.println("You've got mail!");
+ sm.slotUsed();
+ }
+ }
+
+ public void saveSlots(String s, Object userdata) {
+ System.out.println("putting "+s+" into slots");
+ this.ibct_props.put("slots", s);
+ }
+
+ private class MessageLog {
+ private static final String LOGFILE = "log";
+ private final File logfile;
+
+ public MessageLog(File ibctdir) {
+ this.logfile = new File(ibctdir, LOGFILE);
+ }
+
+ public boolean isPresent(int targetid) throws IOException {
+ BufferedReader br;
+ try {
+ br = new BufferedReader(new
FileReader(this.logfile));
+ } catch (FileNotFoundException fnfe) {
+ return false;
+ }
+
+ String line;
+ while ( (line = br.readLine()) != null) {
+ int curid = Integer.parseInt(line);
+ if (curid == targetid) return true;
+ }
+
+ br.close();
+ return false;
+ }
+
+ public void add(int id) throws IOException {
+ FileOutputStream fos = new
FileOutputStream(this.logfile, true);
+
+ PrintStream ps = new PrintStream(fos);
+ ps.println(id);
+ ps.close();
+ }
+ }
}
Copied: trunk/apps/Freemail/src/freemail/NIMContact.java (from rev 9671,
trunk/apps/Freemail/src/freemail/Contact.java)
===================================================================
--- trunk/apps/Freemail/src/freemail/Contact.java 2006-07-20 13:34:13 UTC
(rev 9671)
+++ trunk/apps/Freemail/src/freemail/NIMContact.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -0,0 +1,50 @@
+package freemail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.util.Date;
+
+import freemail.utils.DateStringFactory;
+
+public class NIMContact {
+ public static final String KEYFILE = "key";
+ private static final String LOGFILE_PREFIX = "log-";
+ private final File contact_dir;
+ private final File keyfile;
+
+ NIMContact(File dir) {
+ this.contact_dir = dir;
+ this.keyfile = new File(dir, KEYFILE);
+ }
+
+ public String getKey() throws IOException {
+ FileReader frdr = new FileReader(this.keyfile);
+ BufferedReader br = new BufferedReader(frdr);
+ String key = br.readLine();
+ frdr.close();
+ return key;
+ }
+
+ public MailLog getLog(String date) {
+ return new MailLog(new File(this.contact_dir, LOGFILE_PREFIX +
date));
+ }
+
+ public void pruneLogs(Date keepafter) {
+ File[] files = contact_dir.listFiles();
+
+ int i;
+ for (i = 0; i< files.length; i++) {
+ if (!files[i].getName().startsWith(LOGFILE_PREFIX))
+ continue;
+ Date logdate =
DateStringFactory.DateFromKeyString(files[i].getName().substring(LOGFILE_PREFIX.length()));
+ if (logdate == null) {
+ // couldn't parse the date... hmm
+ files[i].delete();
+ } else if (logdate.before(keepafter)) {
+ files[i].delete();
+ }
+ }
+ }
+}
Modified: trunk/apps/Freemail/src/freemail/NIMFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/NIMFetcher.java 2006-07-27 23:59:06 UTC
(rev 9798)
+++ trunk/apps/Freemail/src/freemail/NIMFetcher.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -4,31 +4,27 @@
import freemail.utils.DateStringFactory;
import java.io.File;
-import java.io.IOException;
-import java.io.BufferedReader;
-import java.io.PrintStream;
import java.io.FileReader;
-import java.text.SimpleDateFormat;
+import java.io.BufferedReader;
+import java.io.IOException;
import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;
-public class NIMFetcher {
+public class NIMFetcher extends Postman {
private final MessageBank mb;
private File contact_dir;
- private final SimpleDateFormat sdf;
private static final int POLL_AHEAD = 3;
private static int PASSES_PER_DAY = 3;
private static int MAX_DAYS_BACK = 30;
NIMFetcher(MessageBank m, File ctdir) {
this.mb = m;
- this.sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z");
this.contact_dir = ctdir;
}
public void fetch() {
- Contact contact = new Contact(this.contact_dir);
+ NIMContact contact = new NIMContact(this.contact_dir);
int i;
for (i = 1 - MAX_DAYS_BACK; i <= 0; i++) {
@@ -51,7 +47,7 @@
contact.pruneLogs(cal.getTime());
}
- private void fetch_day(Contact contact, MailLog log, String date) {
+ private void fetch_day(NIMContact contact, MailLog log, String date) {
HighLevelFCPClient fcpcli;
fcpcli = new HighLevelFCPClient();
@@ -73,7 +69,8 @@
if (result != null) {
System.out.println(keybase+i+": got message!");
try {
- this.storeMessage(result);
+ this.storeMessage(new
BufferedReader(new FileReader(result)), this.mb);
+ result.delete();
log.addMessage(i, "received");
} catch (IOException ioe) {
continue;
@@ -83,27 +80,4 @@
}
}
}
-
- private void storeMessage(File file) throws IOException {
- MailMessage newmsg = this.mb.createMessage();
-
- // add our own headers first
- // recieved and date
- newmsg.addHeader("Received", "(Freemail); "+this.sdf.format(new
Date()));
-
- BufferedReader rdr = new BufferedReader(new FileReader(file));
-
- newmsg.readHeaders(rdr);
-
- PrintStream ps = newmsg.writeHeadersAndGetStream();
-
- String line;
- while ( (line = rdr.readLine()) != null) {
- ps.println(line);
- }
-
- newmsg.commit();
- rdr.close();
- file.delete();
- }
}
Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -117,6 +117,8 @@
System.out.println("Sucessfully received CTS
for "+this.address.getMailsiteKey());
cts.delete();
this.contactfile.put("status", "cts-received");
+ // delete inital slot for forward secrecy
+ this.contactfile.remove("initialslot");
}
} else {
this.init();
@@ -250,7 +252,6 @@
String initialslot = this.getInitialSlot();
- this.contactfile.put("nextslot", initialslot);
rtsmessage.append("initialslot="+initialslot+"\r\n");
rtsmessage.append("messagetype=rts\r\n");
@@ -362,8 +363,11 @@
private String popNextSlot() {
String slot = this.contactfile.get("nextslot");
+ if (slot == null) {
+ slot = this.contactfile.get("initialslot");
+ }
SHA256Digest sha256 = new SHA256Digest();
- sha256.update(slot.getBytes(), 0, Base32.decode(slot).length);
+ sha256.update(Base32.decode(slot), 0,
Base32.decode(slot).length);
byte[] nextslot = new byte[sha256.getDigestSize()];
sha256.doFinal(nextslot, 0);
this.contactfile.put("nextslot", Base32.encode(nextslot));
@@ -390,9 +394,13 @@
// create a new file that contains the complete Freemail
// message, with Freemail headers
QueuedMessage qm = new QueuedMessage(uid);
+
+ File msg;
FileOutputStream fos;
try {
- fos = qm.getOutputStream();
+ msg = File.createTempFile("ogm", "msg",
Freemail.getTempDir());
+
+ fos = new FileOutputStream(msg);
} catch (IOException ioe) {
System.out.println("IO Error encountered whilst trying
to send message: "+ioe.getMessage()+" Will try again soon");
return false;
@@ -414,6 +422,7 @@
} catch (IOException ioe) {
System.out.println("IO Error encountered whilst trying
to send message: "+ioe.getMessage()+" Will try again soon");
qm.delete();
+ msg.delete();
return false;
}
@@ -421,7 +430,7 @@
qm.slot = slot;
- if (qm.saveProps()) {
+ if (qm.setMessageFile(msg) && qm.saveProps()) {
body.delete();
return true;
}
@@ -506,6 +515,8 @@
msgs[i].delete();
// treat the ACK as a CTS too
this.contactfile.put("status", "cts-received");
+ // delete inital slot for forward secrecy
+ this.contactfile.remove("initialslot");
} else {
if (System.currentTimeMillis() >
msgs[i].first_send_time + FAIL_DELAY) {
// give up and bounce the message
@@ -584,8 +595,8 @@
return new FileInputStream(this.file);
}
- public FileOutputStream getOutputStream() throws
FileNotFoundException {
- return new FileOutputStream(this.file);
+ public boolean setMessageFile(File newfile) {
+ return newfile.renameTo(this.file);
}
public boolean saveProps() {
Added: trunk/apps/Freemail/src/freemail/Postman.java
===================================================================
--- trunk/apps/Freemail/src/freemail/Postman.java 2006-07-27 23:59:06 UTC
(rev 9798)
+++ trunk/apps/Freemail/src/freemail/Postman.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -0,0 +1,35 @@
+package freemail;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.PrintStream;
+import java.io.IOException;
+
+/** A postman is any class that delivers mail to an inbox. Simple,
+ * if not politically correct.
+ */
+public abstract class Postman {
+ protected void storeMessage(BufferedReader brdr, MessageBank mb) throws
IOException {
+ MailMessage newmsg = mb.createMessage();
+
+ SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy
HH:mm:ss Z");
+
+ // add our own headers first
+ // recieved and date
+ newmsg.addHeader("Received", "(Freemail); "+sdf.format(new
Date()));
+
+ newmsg.readHeaders(brdr);
+
+ PrintStream ps = newmsg.writeHeadersAndGetStream();
+
+ String line;
+ while ( (line = brdr.readLine()) != null) {
+ ps.println(line);
+ }
+
+ newmsg.commit();
+ brdr.close();
+ }
+}
Modified: trunk/apps/Freemail/src/freemail/RTSFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/RTSFetcher.java 2006-07-27 23:59:06 UTC
(rev 9798)
+++ trunk/apps/Freemail/src/freemail/RTSFetcher.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -32,7 +32,7 @@
private static final int POLL_AHEAD = 3;
private static final int PASSES_PER_DAY = 3;
private static final int MAX_DAYS_BACK = 30;
- private static final String LOGFILE = "rtslog";
+ public static final String LOGFILE = "rtslog";
private static final int RTS_MAX_SIZE = 2 * 1024 * 1024;
private static final String RTS_UNPROC_PREFIX = "unprocessed_rts";
private static final int RTS_MAX_ATTEMPTS = 15;
@@ -330,6 +330,7 @@
String ackssk = rtsprops.get("ackssk");
if (!ackssk.endsWith("/")) ackssk += "/";
ibct.setProp("ackssk", ackssk);
+ ibct.setProp("slots", rtsprops.get("initialslot"));
// insert the cts at some point
AckProcrastinator.put(ackssk+"cts");
Modified: trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -17,18 +17,19 @@
private long mailsite_last_upload;
private final PropsFile accprops;
private final File obctdir;
+ private final File ibctdir;
private final File accdir;
SingleAccountWatcher(File accdir) {
this.accdir = accdir;
this.accprops = AccountManager.getAccountFile(accdir);
File contacts_dir = new File(accdir, CONTACTS_DIR);
- File inbound_dir = new File(contacts_dir, INBOUND_DIR);
+ this.ibctdir = new File(contacts_dir, INBOUND_DIR);
this.obctdir = new File(contacts_dir, OUTBOUND_DIR);
this.mailsite_last_upload = 0;
- if (!inbound_dir.exists()) {
- inbound_dir.mkdir();
+ if (!this.ibctdir.exists()) {
+ this.ibctdir.mkdir();
}
this.mb = new MessageBank(accdir.getName());
@@ -41,7 +42,7 @@
}
- this.rtsf = new
RTSFetcher("KSK@"+this.accprops.get("rtskey")+"-", inbound_dir, accdir);
+ this.rtsf = new
RTSFetcher("KSK@"+this.accprops.get("rtskey")+"-", this.ibctdir, accdir);
//this.mf = new MailFetcher(this.mb, inbound_dir,
Freemail.getFCPConnection());
@@ -77,7 +78,18 @@
this.rtsf.poll();
- //mf.fetch_from_all();
+ // poll for incoming message from all inbound contacts
+ File[] ibcontacts = this.ibctdir.listFiles();
+ if (ibcontacts != null) {
+ int i;
+ for (i = 0; i < ibcontacts.length; i++) {
+ if
(ibcontacts[i].getName().equals(RTSFetcher.LOGFILE)) continue;
+
+ InboundContact ibct = new
InboundContact(this.ibctdir, ibcontacts[i].getName());
+
+ ibct.fetch(this.mb);
+ }
+ }
long runtime = System.currentTimeMillis() - start;
Added: trunk/apps/Freemail/src/freemail/SlotManager.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SlotManager.java 2006-07-27 23:59:06 UTC
(rev 9798)
+++ trunk/apps/Freemail/src/freemail/SlotManager.java 2006-07-28 17:37:32 UTC
(rev 9799)
@@ -0,0 +1,152 @@
+package freemail;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/** Manages sequences of slots which are polled for messages, keeping track of
which
+ * ones still need to be checked, which ones are used and which have expired.
+ */
+public abstract class SlotManager {
+ // how long we should keep checking a slot for which a successibe slot
has
+ // had a message retrieved on
+ private static final long SLOT_LIFETIME = 7 * 24 * 60 * 60 * 1000;
+ private static final int DEFAULT_POLL_AHEAD = 3;
+
+ // 'slots' contains all unused slots, in order for which there is a
+ // higher slot that is used. If there are no such slots, it contains the
+ // first free slot
+ private Vector slots;
+ private int nextSlotNum;
+ private final SlotSaveCallback cb;
+ private final Object userdata;
+ private int pollAhead;
+
+ protected SlotManager(SlotSaveCallback cb, Object userdata, String
slotlist) {
+ this.slots = new Vector();
+ this.cb = cb;
+ this.userdata = userdata;
+ this.nextSlotNum = 0;
+ this.pollAhead = DEFAULT_POLL_AHEAD;
+
+ String parts[] = slotlist.split(",");
+ int i;
+ for (i = 0; i < parts.length; i++) {
+ String[] parts2 = parts[i].split("=", 2);
+ Slot s = new Slot();
+ s.slot = parts2[0];
+ if (parts2.length > 1)
+ s.time_added = Long.parseLong(parts2[1]);
+ else
+ s.time_added = -1;
+
+ this.slots.add(s);
+ }
+ }
+
+ /** Set the number of slots to poll after the last free one
+ */
+ public void setPollAhead(int pa) {
+ this.pollAhead = pa;
+ }
+
+ /** Mark the last given slot as used
+ */
+ public synchronized void slotUsed() {
+ if (this.nextSlotNum <= this.slots.size()) {
+ // it's one in the list. delete it and move the next
+ // pointer down to point to the same one
+ // (If nextSlotNum is 0, this should rightfully throw
+ // an ArrayIndexOutOfBoundsException
+ this.nextSlotNum--;
+ Slot s = (Slot)this.slots.remove(this.nextSlotNum);
+ // additionally, if it was the last one, we need to push
+ // the next slot onto the end
+ if (this.nextSlotNum == this.slots.size()) {
+ s.slot = this.incSlot(s.slot);
+ // time added is -1 since no subsequent slots
+ // have been used
+ s.time_added = -1;
+ this.slots.add(s);
+ }
+ } else {
+ // add all the slots before the used one that aren't
already
+ // in the list
+ int i;
+ Slot s = (Slot)this.slots.lastElement();
+ int slots_start_size = this.slots.size();
+ for (i = slots_start_size; i < this.nextSlotNum - 1;
i++) {
+ s.slot = this.incSlot(s.slot);
+ s.time_added = System.currentTimeMillis();
+ this.slots.add(s);
+ }
+ // increment to get the used slot...
+ s.slot = this.incSlot(s.slot);
+ // and again to get the one that nextSlotNum is
pointing at
+ s.slot = this.incSlot(s.slot);
+ // ...and add that
+ s.time_added = System.currentTimeMillis();
+ this.slots.add(s);
+ }
+ this.saveSlots();
+ }
+
+ private void saveSlots() {
+ StringBuffer buf = new StringBuffer();
+
+ Enumeration e = this.slots.elements();
+ boolean first = true;
+ while (e.hasMoreElements()) {
+ if (!first) buf.append(",");
+ first = false;
+ Slot s = (Slot)e.nextElement();
+ buf.append(s.slot);
+ if (s.time_added > 0)
+
buf.append(",").append(Long.toString(s.time_added));
+ }
+ this.cb.saveSlots(buf.toString(), this.userdata);
+ }
+
+ /** Method provided by subclasses to return the next slot given any slot
+ */
+ protected abstract String incSlot(String slot);
+
+ public synchronized String getNextSlot() {
+ String retval = null;
+
+ boolean tryAgain = true;
+ while (tryAgain) {
+ tryAgain = false;
+ if (this.nextSlotNum >= this.slots.size() +
this.pollAhead) {
+ // you've reached the end
+ retval = null;
+ } else if (this.nextSlotNum >= this.slots.size()) {
+ // we're into the unused slots. make one up.
+ Slot s = (Slot)this.slots.lastElement();
+ int i;
+ for (i = this.slots.size(); i <=
this.nextSlotNum; i++) {
+ s.slot = this.incSlot(s.slot);
+ }
+ retval = s.slot;
+ } else {
+ // we're looking at an unused slot
+ Slot s = (Slot)
this.slots.get(this.nextSlotNum);
+ // is this one too old?
+ if (s.time_added > 0 && s.time_added <
System.currentTimeMillis() - SLOT_LIFETIME && this.nextSlotNum !=
this.slots.size() - 1) {
+ // this slot is too old. Forget it.
+ this.slots.remove(this.nextSlotNum);
+ tryAgain = true;
+ } else {
+ retval = s.slot;
+ }
+ }
+ }
+
+ this.nextSlotNum++;
+ return retval;
+ }
+
+ private class Slot {
+ String slot;
+ long time_added;
+ }
+}
Added: trunk/apps/Freemail/src/freemail/SlotSaveCallback.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SlotSaveCallback.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/SlotSaveCallback.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -0,0 +1,5 @@
+package freemail;
+
+public interface SlotSaveCallback {
+ public void saveSlots(String slots, Object userdata);
+}
Modified: trunk/apps/Freemail/src/freemail/utils/PropsFile.java
===================================================================
--- trunk/apps/Freemail/src/freemail/utils/PropsFile.java 2006-07-27
23:59:06 UTC (rev 9798)
+++ trunk/apps/Freemail/src/freemail/utils/PropsFile.java 2006-07-28
17:37:32 UTC (rev 9799)
@@ -14,34 +14,59 @@
public class PropsFile {
private final File file;
private HashMap data;
+ private BufferedReader bufrdr;
- public PropsFile(File f) {
+ /** Pass true into stopAtBlank to cause the reader to stop upon
encountering
+ * a blank line. It's the the caller's responsibility to get
+ * (using the getReader() method) the stream and close it properly.
+ */
+ public PropsFile(File f, boolean stopAtBlank) {
this.file = f;
this.data = null;
if (f.exists()) {
try {
- this.read();
+ this.bufrdr = this.read(stopAtBlank);
} catch (IOException ioe) {
}
}
}
- private void read() throws IOException {
+ public PropsFile(File f) {
+ this(f, false);
+ }
+
+ private BufferedReader read(boolean stopAtBlank) throws IOException {
this.data = new HashMap();
BufferedReader br = new BufferedReader(new
FileReader(this.file));
String line = null;
while ( (line = br.readLine()) != null) {
+ if (stopAtBlank && line.length() == 0) {
+ return br;
+ }
String[] parts = line.split("=", 2);
if (parts.length < 2) continue;
this.data.put(parts[0], parts[1]);
}
br.close();
+ return null;
}
+ public BufferedReader getReader() {
+ return this.bufrdr;
+ }
+
+ public void closeReader() {
+ if (this.bufrdr == null) return;
+ try {
+ this.bufrdr.close();
+ } catch (IOException ioe) {
+ }
+ }
+
private void write() throws IOException {
PrintWriter pw = new PrintWriter(new
FileOutputStream(this.file));