Author: dbkr
Date: 2008-04-22 22:38:33 +0000 (Tue, 22 Apr 2008)
New Revision: 19505

Added:
   trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java
Modified:
   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/SlotManager.java
   trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
Log:
Change FCP API so fetches throw exceptions on failure. More checking of fetch 
failure codes, especially when fetching RTS messages. Ignore RTS slots whose 
fetches have produced fatal errors.


Modified: trunk/apps/Freemail/src/freemail/InboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/InboundContact.java        2008-04-22 
21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/InboundContact.java        2008-04-22 
22:38:33 UTC (rev 19505)
@@ -34,6 +34,7 @@
 import freemail.utils.PropsFile;
 import freemail.utils.EmailAddress;
 import freemail.utils.Logger;
+import freemail.fcp.FCPFetchException;
 import freemail.fcp.HighLevelFCPClient;
 import freemail.fcp.ConnectionTerminatedException;

@@ -106,9 +107,9 @@
                                msg = fcpcli.fetch(key);
                        } catch (ConnectionTerminatedException cte) {
                                return;
-                       }
-                       if (msg == null) {
-                               Logger.minor(this,"No mail there.");
+                       } catch (FCPFetchException fe) {
+                               // XXX: Slot should be marked dead if this is a 
fatal error
+                               Logger.minor(this,"No mail in slot (fetch 
returned "+fe.getMessage()+")");
                                continue;
                        }
                        Logger.normal(this,"Found a message!");
@@ -212,14 +213,15 @@
                        if (sd.indexOf("\r") > 0 || sd.indexOf("\n") > 0) 
return false;

                        Logger.normal(this,"Attempting to fetch sender's 
mailsite to validate From address...");
-                       File result = 
cli.fetch("KSK@"+sd+MailSite.ALIAS_SUFFIX);
-                       
-                       if (result == null) {
+                       File result;
+                       try {
+                               result = 
cli.fetch("KSK@"+sd+MailSite.ALIAS_SUFFIX);
+                       } catch (FCPFetchException fe) {
                                // we just received the message so we can 
assume our
                                // network connection is healthy, and the 
mailsite
                                // ought to be easily retrievable, so fail.
                                // If this proves to be an issue, change it.
-                               Logger.error(this,"Failed to fetch sender's 
mailsite. Sender's From address therefore not valid.");
+                               Logger.error(this,"Failed to fetch sender's 
mailsite ("+fe.getMessage()+"). Sender's From address therefore not valid.");
                                return false;
                        }
                        Logger.normal(this,"Fetched sender's mailsite");

Modified: trunk/apps/Freemail/src/freemail/NIMFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/NIMFetcher.java    2008-04-22 21:37:38 UTC 
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/NIMFetcher.java    2008-04-22 22:38:33 UTC 
(rev 19505)
@@ -21,6 +21,7 @@

 package freemail;

+import freemail.fcp.FCPFetchException;
 import freemail.fcp.HighLevelFCPClient;
 import freemail.fcp.ConnectionTerminatedException;
 import freemail.utils.DateStringFactory;
@@ -87,19 +88,21 @@
                for (int i = startnum; i < startnum + POLL_AHEAD; i++) {
                        Logger.normal(this,"trying to fetch "+keybase+i);

-                       File result = fcpcli.fetch(keybase+i);
+                       File result;
+                       try {
+                               result = fcpcli.fetch(keybase+i);
+                       } catch (FCPFetchException fe) {
+                               Logger.normal(this,keybase+i+": no message 
("+fe.getMessage()+").");
+                               continue;
+                       }

-                       if (result != null) {
-                               Logger.normal(this,keybase+i+": got message!");
-                               try {
-                                       this.storeMessage(new 
BufferedReader(new FileReader(result)), this.mb);
-                                       result.delete();
-                                       log.addMessage(i, "received");
-                               } catch (IOException ioe) {
-                                       continue;
-                               }
-                       } else {
-                               Logger.normal(this,keybase+i+": no message.");
+                       Logger.normal(this,keybase+i+": got message!");
+                       try {
+                               this.storeMessage(new BufferedReader(new 
FileReader(result)), this.mb);
+                               result.delete();
+                               log.addMessage(i, "received");
+                       } catch (IOException ioe) {
+                               continue;
                        }
                }
        }

Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/OutboundContact.java       2008-04-22 
21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/OutboundContact.java       2008-04-22 
22:38:33 UTC (rev 19505)
@@ -37,6 +37,7 @@
 import freemail.utils.EmailAddress;
 import freemail.utils.PropsFile;
 import freemail.utils.DateStringFactory;
+import freemail.fcp.FCPFetchException;
 import freemail.fcp.HighLevelFCPClient;
 import freemail.fcp.FCPInsertErrorMessage;
 import freemail.fcp.FCPBadFileException;
@@ -170,9 +171,15 @@
                        HighLevelFCPClient fcpcli = new HighLevelFCPClient();

                        Logger.minor(this,"polling for CTS message: "+ctskey);
-                       File cts = fcpcli.fetch(ctskey);
-                       
-                       if (cts == null) {
+                       try {
+                               File cts = fcpcli.fetch(ctskey);
+                               
+                               Logger.normal(this,"Sucessfully received CTS 
for "+this.address.getSubDomain());
+                               cts.delete();
+                               this.contactfile.put("status", "cts-received");
+                               // delete initial slot for forward secrecy
+                               this.contactfile.remove("initialslot");
+                       } catch (FCPFetchException fe) {
                                Logger.minor(this,"CTS not received");
                                // haven't got the CTS message. should we give 
up yet?
                                String senttime = 
this.contactfile.get("rts-sent-at");
@@ -181,13 +188,6 @@
                                        // yes, send another RTS
                                        this.init();
                                }
-                               
-                       } else {
-                               Logger.normal(this,"Sucessfully received CTS 
for "+this.address.getSubDomain());
-                               cts.delete();
-                               this.contactfile.put("status", "cts-received");
-                               // delete initial slot for forward secrecy
-                               this.contactfile.remove("initialslot");
                        }
                } else {
                        this.init();
@@ -436,10 +436,11 @@
                HighLevelFCPClient cli = new HighLevelFCPClient();

                Logger.normal(this,"Attempting to fetch mailsite redirect 
"+key);
-               File result = cli.fetch(key);
-               
-               if (result == null) {
-                       Logger.normal(this,"Failed to retrieve mailsite 
redirect "+key);
+               File result;
+               try {
+                       result = cli.fetch(key);
+               } catch (FCPFetchException fe) {
+                       Logger.normal(this,"Failed to retrieve mailsite 
redirect "+key+" ("+fe.getMessage()+")");
                        return null;
                }

@@ -472,9 +473,10 @@
                HighLevelFCPClient cli = new HighLevelFCPClient();

                Logger.normal(this,"Attempting to fetch 
"+this.address.getMailpageKey());
-               File mailsite_file = cli.fetch(this.address.getMailpageKey());
-               
-               if (mailsite_file == null) {
+               File mailsite_file;
+               try {
+                       mailsite_file = 
cli.fetch(this.address.getMailpageKey());
+               } catch (FCPFetchException fe) {
                        Logger.normal(this,"Failed to retrieve mailsite 
"+this.address.getMailpageKey());
                        return false;
                }
@@ -698,8 +700,8 @@

                        Logger.minor(this,"Looking for message ack on "+key);

-                       File ack = fcpcli.fetch(key);
-                       if (ack != null) {
+                       try {
+                               File ack = fcpcli.fetch(key);
                                Logger.normal(this,"Ack received for message 
"+msgs[i].uid+" on contact "+this.address.domain+". Now that's a job well 
done.");
                                ack.delete();
                                msgs[i].delete();
@@ -707,24 +709,26 @@
                                this.contactfile.put("status", "cts-received");
                                // delete initial slot for forward secrecy
                                this.contactfile.remove("initialslot");
-                       } else {
-                               Logger.minor(this,"Failed to receive ack on 
"+key);
-                               if (System.currentTimeMillis() > 
msgs[i].first_send_time + FAIL_DELAY) {
-                                       // give up and bounce the message
-                                       File m = msgs[i].getMessageFile();
-                                       
-                                       Postman.bounceMessage(m, 
account.getMessageBank(),
-                                                       "Freemail has been 
trying for too long to deliver this message, and has received no 
acknowledgement. "
-                                                       +"It is possible that 
the recipient has not run Freemail since you sent the message. "
-                                                       +"If you believe this 
is likely, try resending the message.", true);
-                                       Logger.normal(this,"Giving up on 
message - been trying for too long.");
-                                       msgs[i].delete();
-                               } else if (System.currentTimeMillis() > 
msgs[i].last_send_time + RETRANSMIT_DELAY) {
-                                       // no ack yet - retransmit on another 
slot
-                                       msgs[i].slot = this.popNextSlot();
-                                       // mark for re-insertion
-                                       msgs[i].last_send_time = -1;
-                                       msgs[i].saveProps();
+                       } catch (FCPFetchException fe) {
+                               Logger.minor(this,"Failed to receive ack on 
"+key+" ("+fe.getMessage()+")");
+                               if (!fe.isNetworkError()) {
+                                       if (System.currentTimeMillis() > 
msgs[i].first_send_time + FAIL_DELAY) {
+                                               // give up and bounce the 
message
+                                               File m = 
msgs[i].getMessageFile();
+                                               
+                                               Postman.bounceMessage(m, 
account.getMessageBank(),
+                                                               "Freemail has 
been trying for too long to deliver this message, and has received no 
acknowledgement. "
+                                                               +"It is 
possible that the recipient has not run Freemail since you sent the message. "
+                                                               +"If you 
believe this is likely, try resending the message.", true);
+                                               Logger.normal(this,"Giving up 
on message - been trying for too long.");
+                                               msgs[i].delete();
+                                       } else if (System.currentTimeMillis() > 
msgs[i].last_send_time + RETRANSMIT_DELAY) {
+                                               // no ack yet - retransmit on 
another slot
+                                               msgs[i].slot = 
this.popNextSlot();
+                                               // mark for re-insertion
+                                               msgs[i].last_send_time = -1;
+                                               msgs[i].saveProps();
+                                       }
                                }
                        }
                }

Modified: trunk/apps/Freemail/src/freemail/RTSFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/RTSFetcher.java    2008-04-22 21:37:38 UTC 
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/RTSFetcher.java    2008-04-22 22:38:33 UTC 
(rev 19505)
@@ -21,6 +21,7 @@

 package freemail;

+import freemail.fcp.FCPFetchException;
 import freemail.fcp.HighLevelFCPClient;
 import freemail.fcp.ConnectionTerminatedException;
 import freemail.utils.DateStringFactory;
@@ -157,9 +158,9 @@
                while ( (slot = sm.getNextSlotNat()) > 0) {
                        Logger.minor(this,"trying to fetch "+keybase+slot);

-                       File result = fcpcli.fetch(keybase+slot);
-                       
-                       if (result != null) {
+                       try {
+                               File result = fcpcli.fetch(keybase+slot);
+                               
                                Logger.normal(this,keybase+slot+": got RTS!");

                                File rts_dest = new File(this.contact_dir, 
RTS_UNPROC_PREFIX + "-" + log.getAndIncUnprocNextId()+",0");
@@ -169,8 +170,23 @@
                                        // provided that worked, we can move on 
to the next RTS message
                                        sm.slotUsed();
                                }
-                       } else {
-                               Logger.minor(this,keybase+slot+": no RTS.");
+                       } catch (FCPFetchException fe) {
+                               if (fe.isFatal()) {
+                                       Logger.error(this,keybase+slot+": fatal 
fetch error - marking slot as used.");
+                                       sm.slotUsed();
+                               } else if (fe.getCode() == 
FCPFetchException.ALL_DATA_NOT_FOUND) {
+                                       // This could be the node not managing 
to find the CHK containing the actual data (since RTS messages are
+                                       // over 1KB, the node will opaquely 
insert them as a KSK redirect to a CHK, since a KSK/SSK can only hold
+                                       // 1KB of data). It could also be 
someone inserting dummy redirects to our RTS queue. We'll have to keep
+                                       // checking it, but we have to check 
slots until we find some that are really empty, we'd never manage
+                                       // to fetch anything if they are dead 
keys.
+                                       Logger.error(this,keybase+slot+": All 
Data not found - leaving slot in queue and will poll an extra key");
+                                       sm.incPollAhead();
+                               } else if (fe.getCode() == 
FCPFetchException.DATA_NOT_FOUND) {
+                                       Logger.minor(this,keybase+slot+": no 
RTS.");
+                               } else {
+                                       Logger.minor(this,keybase+slot+": other 
non-fatal fetch error:"+fe.getMessage());
+                               }
                        }
                }
        }
@@ -300,9 +316,10 @@


                Logger.normal(this,"Trying to fetch sender's mailsite: 
"+their_mailsite);
-               
-               File msfile = fcpcli.fetch(their_mailsite);
-               if (msfile == null) {
+               File msfile;
+               try {
+                       msfile = fcpcli.fetch(their_mailsite);
+               } catch (FCPFetchException fe) {
                        // oh well, try again in a bit
                        rtsfile.delete();
                        return false;

Modified: trunk/apps/Freemail/src/freemail/SlotManager.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SlotManager.java   2008-04-22 21:37:38 UTC 
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/SlotManager.java   2008-04-22 22:38:33 UTC 
(rev 19505)
@@ -70,6 +70,10 @@
                this.pollAhead = pa;
        }

+       public void incPollAhead() {
+               ++pollAhead;
+       }
+       
        /** Mark the last given slot as used
         */
        public synchronized void slotUsed() {

Added: trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java
===================================================================
--- trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java                 
        (rev 0)
+++ trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java 2008-04-22 
22:38:33 UTC (rev 19505)
@@ -0,0 +1,133 @@
+package freemail.fcp;
+
+import freemail.utils.Logger;
+
+public class FCPFetchException extends Exception {
+       static final long serialVersionUID = -1;
+       
+       // The following code shamelessly stolen from Freenet's 
FetchException.java (but reordered)
+       /** Too many levels of recursion into archives */
+       public static final int TOO_DEEP_ARCHIVE_RECURSION = 1;
+       /** Don't know what to do with splitfile */
+       public static final int UNKNOWN_SPLITFILE_METADATA = 2;
+       /** Don't know what to do with metadata */
+       public static final int UNKNOWN_METADATA = 3;
+       /** Got a MetadataParseException */
+       public static final int INVALID_METADATA = 4;
+       /** Got an ArchiveFailureException */
+       public static final int ARCHIVE_FAILURE = 5;
+       /** Failed to decode a block */
+       public static final int BLOCK_DECODE_ERROR = 6;
+       /** Too many split metadata levels */
+       public static final int TOO_MANY_METADATA_LEVELS = 7;
+       /** Too many archive restarts */
+       public static final int TOO_MANY_ARCHIVE_RESTARTS = 8;
+       /** Too deep recursion */
+       public static final int TOO_MUCH_RECURSION = 9;
+       /** Tried to access an archive file but not in an archive */
+       public static final int NOT_IN_ARCHIVE = 10;
+       /** Too many meta strings. E.g. requesting CHK at blah,blah,blah as CHK 
at blah,blah,blah/filename.ext */
+       public static final int TOO_MANY_PATH_COMPONENTS = 11;
+       /** Failed to read from or write to a bucket; a kind of internal error 
*/
+       public static final int BUCKET_ERROR = 12;
+       /** Data not found */
+       public static final int DATA_NOT_FOUND = 13;
+       /** Route not found */
+       public static final int ROUTE_NOT_FOUND = 14;
+       /** Downstream overload */
+       public static final int REJECTED_OVERLOAD = 15;
+       /** Too many redirects */
+       public static final int TOO_MANY_REDIRECTS = 16;
+       /** An internal error occurred */
+       public static final int INTERNAL_ERROR = 17;
+       /** The node found the data but the transfer failed */
+       public static final int TRANSFER_FAILED = 18;
+       /** Splitfile error. This should be a SplitFetchException. */
+       public static final int SPLITFILE_ERROR = 19;
+       /** Invalid URI. */
+       public static final int INVALID_URI = 20;
+       /** Too big */
+       public static final int TOO_BIG = 21;
+       /** Metadata too big */
+       public static final int TOO_BIG_METADATA = 22;
+       /** Splitfile has too big segments */
+       public static final int TOO_MANY_BLOCKS_PER_SEGMENT = 23;
+       /** Not enough meta strings in URI given and no default document */
+       public static final int NOT_ENOUGH_PATH_COMPONENTS = 24;
+       /** Explicitly cancelled */
+       public static final int CANCELLED = 25;
+       /** Archive restart */
+       public static final int ARCHIVE_RESTART = 26;
+       /** There is a more recent version of the USK, ~= HTTP 301; FProxy will 
turn this into a 301 */
+       public static final int PERMANENT_REDIRECT = 27;
+       /** Not all data was found; some DNFs but some successes */
+       public static final int ALL_DATA_NOT_FOUND = 28;
+       /** Requestor specified a list of allowed MIME types, and the key's 
type wasn't in the list */
+       public static final int WRONG_MIME_TYPE = 29;
+       /** A node killed the request because it had recently been tried and 
had DNFed */
+       public static final int RECENTLY_FAILED = 30;
+       
+       private final FCPMessage fcpMessage;
+       
+       public FCPFetchException(FCPMessage fcpmsg) {
+               fcpMessage = fcpmsg;
+       }
+       
+       public FCPMessage getFailureMessage() {
+               return fcpMessage;
+       }
+       
+       public int getCode() {
+               String strCode = (String)fcpMessage.headers.get("Code");
+               if (strCode == null) {
+                       Logger.error(this, "FCP error message with no error 
code!");
+                       return 0;
+               }
+               return Integer.parseInt(strCode);
+       }
+       
+       public String getMessage() {
+               String msg = 
(String)fcpMessage.headers.get("ShortCodeDescription");
+               if (msg != null) return msg;
+               
+               // No short description? try the long one.
+               msg = (String)fcpMessage.headers.get("CodeDescription");
+               if (msg != null) return msg;
+               
+               // No? Does it have a code?
+               msg = (String)fcpMessage.headers.get("Code");
+               if (msg != null) return "Error number "+msg+" (no description 
given)";
+               
+               // Give up then
+               return "Unknown error (no hints given by the node)";
+       }
+       
+       /**
+        * @return true if the error is the fault of the network or our 
connection to it, and has no bearing on whether or not
+        *              the key itself is present or not.
+        */
+       public boolean isNetworkError() {
+               switch (getCode()) {
+                       case BUCKET_ERROR:
+                       case ROUTE_NOT_FOUND:
+                       case REJECTED_OVERLOAD:
+                       case INTERNAL_ERROR:
+                       case TRANSFER_FAILED:
+                       case CANCELLED:
+                               return true;
+               }
+               return false;
+       }
+       
+       /**
+        * @return true if all future requests for this this key will fail too
+        */
+       public boolean isFatal() {
+               String fatal = (String)fcpMessage.headers.get("Fatal");
+               if (fatal == null) {
+                       Logger.error(this, "No 'fatal' field found - FCP 
protocol change? Assuming not fatal.");
+                       return false;
+               }
+               return fatal.equalsIgnoreCase("true");
+       }
+}

Modified: trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
===================================================================
--- trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java        
2008-04-22 21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java        
2008-04-22 22:38:33 UTC (rev 19505)
@@ -43,7 +43,7 @@

        // It's up to the client to delete this File once they're
        // done with it
-       public synchronized File fetch(String key) throws 
ConnectionTerminatedException {
+       public synchronized File fetch(String key) throws 
ConnectionTerminatedException, FCPFetchException {
                FCPMessage msg = this.conn.getMessage("ClientGet");
                msg.headers.put("URI", key);
                msg.headers.put("ReturnType", "direct");
@@ -55,8 +55,8 @@
                                break;
                        } catch (NoNodeConnectionException nnce) {
                                try {
-                                       Logger.error(this,"got no conn 
exception: "+nnce.getMessage());
-                                       Thread.sleep(5000);
+                                       Logger.error(this,"No Freenet node 
available - waiting: "+nnce.getMessage());
+                                       Thread.sleep(10000);
                                } catch (InterruptedException ie) {
                                }
                        } catch (FCPBadFileException bfe) {
@@ -84,9 +84,9 @@
                                if (newuri == null) return null;
                                return this.fetch(newuri);
                        }
-                       return null;
+                       throw new FCPFetchException(donemsg);
                } else {
-                       return null;
+                       throw new FCPFetchException(donemsg);
                }
        }



Reply via email to