Author: dbkr
Date: 2006-07-30 15:41:59 +0000 (Sun, 30 Jul 2006)
New Revision: 9822
Modified:
trunk/apps/Freemail/docs/spec/spec.tex
trunk/apps/Freemail/src/freemail/AckProcrastinator.java
trunk/apps/Freemail/src/freemail/MailMessage.java
trunk/apps/Freemail/src/freemail/MessageSender.java
trunk/apps/Freemail/src/freemail/OutboundContact.java
trunk/apps/Freemail/src/freemail/Postman.java
Log:
Bounce messages, and change the spec to reflect the base32 crypto parameter
encoding thing.
Modified: trunk/apps/Freemail/docs/spec/spec.tex
===================================================================
--- trunk/apps/Freemail/docs/spec/spec.tex 2006-07-30 15:10:48 UTC (rev
9821)
+++ trunk/apps/Freemail/docs/spec/spec.tex 2006-07-30 15:41:59 UTC (rev
9822)
@@ -33,8 +33,8 @@
\begin{itemize}
\item rtskey - This is an arbitrary string of alphanumeric characters which is
used to derive a KSK that can be used to send data to the owner of the mailsite
in order to establish a communication channel.
-\item asymkey.modulus - The modulus of the owner's RSA encryption key, as an
integer in base 10.
-\item asymkey.pubexponent - The public exponent of the owner's RSA encryption
key, as an integer in base 10.
+\item asymkey.modulus - The modulus of the owner's RSA encryption key, as an
integer in base 32.
+\item asymkey.pubexponent - The public exponent of the owner's RSA encryption
key, as an integer in base 32.
\end{itemize}
\subsection{RTS Messages}
Modified: trunk/apps/Freemail/src/freemail/AckProcrastinator.java
===================================================================
--- trunk/apps/Freemail/src/freemail/AckProcrastinator.java 2006-07-30
15:10:48 UTC (rev 9821)
+++ trunk/apps/Freemail/src/freemail/AckProcrastinator.java 2006-07-30
15:41:59 UTC (rev 9822)
@@ -79,8 +79,10 @@
FCPInsertErrorMessage err =
fcpcli.put(bis, key);
if (err == null) {
acks[i].delete();
+ System.out.println("ACK
insertion to "+key+" sucessful");
} else if (err.errorcode ==
FCPInsertErrorMessage.COLLISION) {
acks[i].delete();
+ System.out.println("ACK
insertion to "+key+" sucessful");
}
} catch (FCPBadFileException bfe) {
// won't occur
Modified: trunk/apps/Freemail/src/freemail/MailMessage.java
===================================================================
--- trunk/apps/Freemail/src/freemail/MailMessage.java 2006-07-30 15:10:48 UTC
(rev 9821)
+++ trunk/apps/Freemail/src/freemail/MailMessage.java 2006-07-30 15:41:59 UTC
(rev 9822)
@@ -105,6 +105,14 @@
}
}
+ public void cancel() {
+ try {
+ this.os.close();
+ } catch (IOException ioe) {
+ }
+ this.file.delete();
+ }
+
public void readHeaders() throws IOException {
BufferedReader bufrdr = new BufferedReader(new
FileReader(this.file));
Modified: trunk/apps/Freemail/src/freemail/MessageSender.java
===================================================================
--- trunk/apps/Freemail/src/freemail/MessageSender.java 2006-07-30 15:10:48 UTC
(rev 9821)
+++ trunk/apps/Freemail/src/freemail/MessageSender.java 2006-07-30 15:41:59 UTC
(rev 9822)
@@ -154,25 +154,10 @@
try {
ct = new OutboundContact(accdir, addr);
} catch (BadFreemailAddressException bfae) {
- // TODO: bounce
- return true;
+ // bounce
+ return Postman.bounceMessage(msg, new
MessageBank(accdir.getName()), "The address that this message was destined for
("+addr+") is not a valid Freemail address.");
}
- boolean ready;
- if (!ct.ready()) {
- try {
- System.out.println("initing outbound contact");
- ready = ct.init();
- } catch (OutboundContactFatalException fe) {
- // will never succeed, so report success to
delete the message
- // TODO: send a bounce message or something
- return true;
- }
- } else {
- ready = true;
- }
- if (!ready) return false;
-
return ct.sendMessage(msg);
}
}
Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-07-30
15:10:48 UTC (rev 9821)
+++ trunk/apps/Freemail/src/freemail/OutboundContact.java 2006-07-30
15:41:59 UTC (rev 9822)
@@ -113,9 +113,11 @@
HighLevelFCPClient fcpcli = new HighLevelFCPClient();
+ System.out.println("polling for CTS message: "+ctskey);
File cts = fcpcli.fetch(ctskey);
if (cts == null) {
+ System.out.println("CTS not received");
// haven't got the CTS message. should we give
up yet?
String senttime =
this.contactfile.get("rts-sent-at");
@@ -136,21 +138,6 @@
}
}
- /*
- * Whether or not we're ready to communicate with the other party
- */
- public boolean ready() {
- if (!this.contactfile.exists()) return false;
-
- String status = this.contactfile.get("status");
- if (status == null) return false;
- // don't wait for an ack before inserting the message, but be
ready to insert it again
- // if the ack never arrives
- if (status.equals("rts-sent")) return true;
- if (status.equals("cts-received")) return true;
- return false;
- }
-
private SSKKeyPair getCommKeyPair() {
SSKKeyPair ssk = new SSKKeyPair();
@@ -265,8 +252,9 @@
*
* @return true for success
*/
- public boolean init() throws OutboundContactFatalException {
+ private boolean init() throws OutboundContactFatalException {
// try to fetch get all necessary info. will fetch mailsite /
generate new keys if necessary
+ String initialslot = this.getInitialSlot();
SSKKeyPair commssk = this.getCommKeyPair();
if (commssk == null) return false;
SSKKeyPair ackssk = this.getAckKeyPair();
@@ -283,8 +271,6 @@
rtsmessage.append("ackssk="+ackssk.privkey+"\r\n");
- String initialslot = this.getInitialSlot();
-
rtsmessage.append("initialslot="+initialslot+"\r\n");
rtsmessage.append("messagetype=rts\r\n");
@@ -372,6 +358,7 @@
// insert it!
HighLevelFCPClient cli = new HighLevelFCPClient();
if (cli.SlotInsert(encmsg,
"KSK@"+rtsksk+"-"+DateStringFactory.getKeyString(), 1, "") < 0) {
+ // safe to copy the message into the contact outbox
though
return false;
}
@@ -382,7 +369,6 @@
// and since that's been sucessfully inserted to that key, we
can
// throw away the symmetric key
this.contactfile.remove("aesparams");
-
return true;
}
@@ -454,6 +440,16 @@
}
public boolean sendMessage(File body) {
+ if (!this.contactfile.exists()) {
+ try {
+ this.init();
+ } catch (OutboundContactFatalException fe) {
+ if (Postman.bounceMessage(body, new
MessageBank(this.accdir.getName()), fe.getMessage())) {
+ return true;
+ }
+ }
+ }
+
int uid = this.popNextUid();
// create a new file that contains the complete Freemail
@@ -508,6 +504,19 @@
}
private void sendQueued() {
+ boolean ready;
+ String ctstatus = this.contactfile.get("status");
+ if (ctstatus == null) ctstatus = "notsent";
+ if (ctstatus.equals("rts-sent") ||
ctstatus.equals("cts-received")) {
+ ready = true;
+ } else {
+ try {
+ ready = this.init();
+ } catch (OutboundContactFatalException fe) {
+ ready = false;
+ }
+ }
+
HighLevelFCPClient fcpcli = null;
QueuedMessage[] msgs = this.getSendQueue();
@@ -517,6 +526,15 @@
if (msgs[i] == null) continue;
if (msgs[i].last_send_time > 0) continue;
+ if (!ready) {
+ if (msgs[i].added_time + FAIL_DELAY <
System.currentTimeMillis()) {
+ if
(Postman.bounceMessage(msgs[i].getMessageFile(), new
MessageBank(this.accdir.getName()), "Freemail has been trying to establish a
communication channel with this party for too long without success. Check that
the Freemail address is valid, and that the recipient still runs Freemail on at
least a semi-regular basis.", true)) {
+ msgs[i].delete();
+ }
+ }
+ continue;
+ }
+
if (fcpcli == null) fcpcli = new HighLevelFCPClient();
String key = this.contactfile.get("commssk.privkey");
@@ -548,6 +566,11 @@
msgs[i].first_send_time =
System.currentTimeMillis();
msgs[i].last_send_time =
System.currentTimeMillis();
msgs[i].saveProps();
+ } else if (msgs[i].added_time + FAIL_DELAY <
System.currentTimeMillis()) {
+ System.out.println("Giving up on a message -
been trying to send for too long. Bouncing.");
+ if
(Postman.bounceMessage(msgs[i].getMessageFile(), new
MessageBank(this.accdir.getName()), "Freemail has been trying to deliver this
message for too long without success. This is likley to be due to a poor
connection to Freenet. Check your Freenet node.", true)) {
+ msgs[i].delete();
+ }
} else {
System.out.println("Failed to insert "+key+"
will try again soon.");
}
@@ -585,7 +608,9 @@
} else {
if (System.currentTimeMillis() >
msgs[i].first_send_time + FAIL_DELAY) {
// give up and bounce the message
- // TODO: bounce message
+ File m = msgs[i].getMessageFile();
+
+ Postman.bounceMessage(m, new
MessageBank(this.accdir.getName()), "Freemail has been trying for too long to
deliver this message, and has received no acknowledgement. It is possivle that
the recipient has not run Freemail since you sent the message. If you believe
this is likely, try resending the message.", true);
System.out.println("Giving up on
message - been trying for too long.");
msgs[i].delete();
} else if (System.currentTimeMillis() >
msgs[i].last_send_time + RETRANSMIT_DELAY) {
@@ -631,6 +656,7 @@
final int uid;
String slot;
+ long added_time;
long first_send_time;
long last_send_time;
private final File file;
@@ -654,12 +680,21 @@
else
this.last_send_time = Long.parseLong(s_last);
+ String s_added = this.index.get(uid+".added_time");
+ if (s_added == null)
+ this.added_time = System.currentTimeMillis();
+ else
+ this.added_time = Long.parseLong(s_added);
}
public FileInputStream getInputStream() throws
FileNotFoundException {
return new FileInputStream(this.file);
}
+ public File getMessageFile() {
+ return this.file;
+ }
+
public boolean setMessageFile(File newfile) {
return newfile.renameTo(this.file);
}
@@ -669,6 +704,7 @@
suc &= this.index.put(uid+".slot", this.slot);
suc &= this.index.put(uid+".first_send_time",
this.first_send_time);
suc &= this.index.put(uid+".last_send_time",
this.last_send_time);
+ suc &= this.index.put(uid+".added_time",
this.added_time);
return suc;
}
Modified: trunk/apps/Freemail/src/freemail/Postman.java
===================================================================
--- trunk/apps/Freemail/src/freemail/Postman.java 2006-07-30 15:10:48 UTC
(rev 9821)
+++ trunk/apps/Freemail/src/freemail/Postman.java 2006-07-30 15:41:59 UTC
(rev 9822)
@@ -2,15 +2,19 @@
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.Random;
import java.io.File;
import java.io.BufferedReader;
+import java.io.FileReader;
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 {
+public class Postman {
+ private static final int BOUNDARY_LENGTH = 32;
+
protected void storeMessage(BufferedReader brdr, MessageBank mb) throws
IOException {
MailMessage newmsg = mb.createMessage();
@@ -32,4 +36,104 @@
newmsg.commit();
brdr.close();
}
+
+ public static boolean bounceMessage(File origmsg, MessageBank mb,
String errmsg) {
+ return bounceMessage(origmsg, mb, errmsg, false);
+ }
+
+ public static boolean bounceMessage(File origmsg, MessageBank mb,
String errmsg, boolean isFreemailFormat) {
+ MailMessage bmsg = null;
+ try {
+ bmsg = mb.createMessage();
+
+ SimpleDateFormat sdf = new SimpleDateFormat("dd MMM
yyyy HH:mm:ss Z");
+
+ bmsg.addHeader("From", "Freemail Postmaster <postmaster
at freemail>");
+ bmsg.addHeader("Subject", "Undeliverable Freemail");
+ String origFrom = extractFromAddress(origmsg,
isFreemailFormat);
+ if (origFrom != null)
+ bmsg.addHeader("To", origFrom);
+ bmsg.addHeader("Date", sdf.format(new Date()));
+ bmsg.addHeader("MIME-Version", "1.0");
+ String boundary="boundary-";
+ Random rnd = new Random();
+ int i;
+ for (i = 0; i < BOUNDARY_LENGTH; i++) {
+ boundary += (char)(rnd.nextInt(25) + (int)'a');
+ }
+ bmsg.addHeader("Content-Type", "Multipart/Mixed;
boundary=\""+boundary+"\"");
+
+ PrintStream ps = bmsg.writeHeadersAndGetStream();
+
+ ps.println("--"+boundary);
+ ps.println("Content-Type: text/plain");
+ ps.println("Content-Disposition: inline");
+ ps.println("");
+ ps.println("Freemail was unable to deliver your
message. The problem was:");
+ ps.println("");
+ ps.println(errmsg);
+ ps.println("");
+ ps.println("The original message is included below.");
+ ps.println("");
+ ps.println("--"+boundary);
+ ps.println("Content-Type: message/rfc822;");
+ ps.println("Content-Disposition: inline");
+ ps.println("");
+
+ BufferedReader br = new BufferedReader(new
FileReader(origmsg));
+
+ String line;
+ if (isFreemailFormat) {
+ while ( (line = br.readLine()) != null) {
+ if (line.length() == 0) break;
+ }
+ }
+
+ while ( (line = br.readLine()) != null) {
+ if (line.indexOf(boundary) > 0) {
+ // The random boundary string appears
in the
+ // message! What are the odds!?
+ // try again
+ br.close();
+ bmsg.cancel();
+ bounceMessage(origmsg, mb, errmsg);
+ }
+ ps.println(line);
+ }
+
+ br.close();
+ ps.println("--"+boundary);
+ bmsg.commit();
+ } catch (IOException ioe) {
+ if (bmsg != null) bmsg.cancel();
+ return false;
+ }
+ return true;
+ }
+
+ private static String extractFromAddress(File msg, boolean
isFreemailFormat) {
+ try {
+ BufferedReader br = new BufferedReader(new
FileReader(msg));
+
+ String line;
+ if (isFreemailFormat) {
+ while ( (line = br.readLine()) != null) {
+ if (line.length() == 0) break;
+ }
+ }
+
+ while ( (line = br.readLine()) != null) {
+ if (line.length() == 0) return null;
+ String[] parts = line.split(": ", 2);
+ if (parts.length < 2) continue;
+ if (parts[0].equalsIgnoreCase("From")) {
+ br.close();
+ return parts[1];
+ }
+ }
+ br.close();
+ } catch (IOException ioe) {
+ }
+ return null;
+ }
}