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;
+       }
 }


Reply via email to