Changeset 266

Show
Ignore:
Timestamp:
08/04/08 09:17:20 PM (4 months ago)
Author:
octorian
Message:

Initial IMAP IDLE implementation

Location:
trunk/LogicMail/src/org/logicprobe/LogicMail
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/AbstractMailConnectionHandler.java

    r253 r266  
    315315         * a new request arrives, or it is commanded to shutdown. 
    316316         * </p> 
    317          */ 
    318         protected abstract void handleBeginIdle(); 
     317     *  
     318     * @throw IOException on I/O errors 
     319     * @throw MailException on protocol errors 
     320         */ 
     321        protected abstract void handleBeginIdle() throws IOException, MailException; 
    319322         
    320323    /** 
     
    373376        protected Queue getRequestQueue() { 
    374377                return this.requestQueue; 
     378        } 
     379         
     380        /** 
     381         * Gets whether a shutdown is currently in progress. 
     382         * @return True if shutdown is in progress. 
     383         */ 
     384        protected boolean getShutdownInProgress() { 
     385                return this.shutdownInProgress; 
     386        } 
     387         
     388        /** 
     389         * Sleep the connection thread. 
     390         * @param time Time to sleep, in milliseconds. 
     391         */ 
     392        protected void sleepConnectionThread(long time) { 
     393                if(!connectionThread.isShutdown()) { 
     394                        try { 
     395                                ConnectionThread.sleep(time); 
     396                        } catch (InterruptedException e) { } 
     397                } 
    375398        } 
    376399         
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/imap/ImapClient.java

    r264 r266  
    269269        return true; 
    270270    } 
     271 
     272    public boolean hasIdle() { 
     273                return true; 
     274        } 
    271275     
    272276    public FolderTreeItem getFolderTree() throws IOException, MailException { 
     
    575579        imapProtocol.executeAppend(folder.getPath(), rawMessage, flags); 
    576580    } 
     581 
     582        public void idleModeBegin() throws IOException, MailException { 
     583                imapProtocol.executeIdle(); 
     584        } 
     585 
     586        public void idleModeEnd() throws IOException, MailException { 
     587                imapProtocol.executeIdleDone(); 
     588        } 
     589 
     590        public boolean idleModePoll() throws IOException, MailException { 
     591                return imapProtocol.executeIdlePoll(); 
     592        } 
    577593} 
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/imap/ImapProtocol.java

    r264 r266  
    4848public class ImapProtocol { 
    4949    private Connection connection; 
     50    private String idleCommandTag; 
    5051 
    5152    /** 
     
    889890 
    890891    /** 
     892     * Execute the "IDLE" command. 
     893     */ 
     894    public void executeIdle() throws IOException, MailException { 
     895        if(EventLogger.getMinimumLevel() >= EventLogger.DEBUG_INFO) { 
     896            EventLogger.logEvent( 
     897            AppInfo.GUID, 
     898            ("ImapProtocol.executeIdle()").getBytes(), 
     899            EventLogger.DEBUG_INFO); 
     900        } 
     901        idleCommandTag = executeNoReply("IDLE", null); 
     902    } 
     903     
     904    /** 
     905     * Execute the "DONE" command. 
     906     */ 
     907    public void executeIdleDone() throws IOException, MailException { 
     908        if(EventLogger.getMinimumLevel() >= EventLogger.DEBUG_INFO) { 
     909            EventLogger.logEvent( 
     910            AppInfo.GUID, 
     911            ("ImapProtocol.executeIdleDone()").getBytes(), 
     912            EventLogger.DEBUG_INFO); 
     913        } 
     914        executeUntagged("DONE", null, idleCommandTag); 
     915        idleCommandTag = null; 
     916    } 
     917     
     918    /** 
     919     * Polls the connection during the IDLE state. 
     920     * For now, this is a simple implementation.  It just checks for 
     921     * the presence of a "+ ?? recent" untagged response, and returns 
     922     * true if one is found. 
     923     */ 
     924    public boolean executeIdlePoll() throws IOException, MailException { 
     925                String result = receive(); 
     926                if(result != null && result.startsWith("*") && result.toLowerCase().endsWith("recent")) { 
     927                        return true; 
     928                } 
     929                else { 
     930                        return false; 
     931                } 
     932    } 
     933     
     934    /** 
    891935     * Executes an IMAP command several times, with different arguments, 
    892936     * and return the replies as an array of strings. 
     
    10061050        return result; 
    10071051    } 
     1052     
     1053    /** 
     1054     * Attempts to read a line of text from the server, without sending 
     1055     * anything first. 
     1056     * @return Returned string, or null if nothing is available 
     1057     */ 
     1058    protected String receive() 
     1059        throws IOException, MailException 
     1060        { 
     1061        String result; 
     1062         
     1063        if(connection.available() > 0) { 
     1064                System.err.println("-->connection.available() == " + connection.available()); 
     1065                result = connection.receive(); 
     1066                System.err.println("-->Read in "+result.length()); 
     1067                System.err.println("-->connection.available() == " + connection.available()); 
     1068        } 
     1069        else { 
     1070                result = null; 
     1071        } 
     1072        return result; 
     1073        } 
     1074     
     1075    /** 
     1076     * Executes an IMAP command directly and expects no reply. 
     1077     * @param command IMAP command 
     1078     * @param arguments Arguments for the command 
     1079     * @return Tag used to send the command 
     1080     */ 
     1081    protected String executeNoReply(String command, String arguments) 
     1082        throws IOException, MailException 
     1083        { 
     1084        String tag = "A" + commandCount++ + " "; 
     1085        connection.sendCommand(tag + command + (arguments == null ? "" : " " + arguments)); 
     1086 
     1087        return tag; 
     1088    } 
     1089     
     1090    /** 
     1091     * Executes an IMAP command without a tag prefix, 
     1092     * and returns the reply as an array of strings. 
     1093     * @param command IMAP command 
     1094     * @param arguments Arguments for the command 
     1095     * @param Known tag that indicates the end of the result 
     1096     * @return List of returned strings 
     1097     */ 
     1098    protected String[] executeUntagged(String command, String arguments, String endTag) 
     1099        throws IOException, MailException 
     1100    { 
     1101        String[] result = new String[0]; 
     1102         
     1103        connection.sendCommand(command + (arguments == null ? "" : " " + arguments)); 
     1104         
     1105        String temp = connection.receive(); 
     1106        while (!temp.startsWith(endTag)) { 
     1107            Arrays.add(result, temp); 
     1108            temp = connection.receive(); 
     1109        } 
     1110 
     1111        temp = temp.substring(endTag.length()); 
     1112        if (temp.startsWith("BAD ") || temp.startsWith("NO ")) { 
     1113            throw new MailException(temp); 
     1114        } 
     1115        return result; 
     1116    } 
    10081117} 
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/IncomingMailClient.java

    r264 r266  
    6969     
    7070    /** 
     71     * Return whether the underlying protocol supports an idle connection mode. 
     72     *  
     73     * @return True if an idle mode is supported, false otherwise 
     74     */ 
     75    public abstract boolean hasIdle(); 
     76     
     77    /** 
    7178     * Get the mail folder tree. 
    7279     * This should return null if folders are not supported 
     
    173180     */ 
    174181    public abstract void undeleteMessage(FolderMessage folderMessage) throws IOException, MailException; 
     182 
     183    /** 
     184     * Begins the idle mode for the underlying protocol. 
     185     * This should do nothing if the underlying protocol does not support idling. 
     186     *  
     187     * @throws IOException on I/O errors 
     188     * @throws MailException on protocol errors 
     189     */ 
     190    public abstract void idleModeBegin() throws IOException, MailException; 
     191     
     192    /** 
     193     * Ends the idle mode for the underlying protocol. 
     194     * This should do nothing if the underlying protocol does not support idling. 
     195     *  
     196     * @throws IOException on I/O errors 
     197     * @throws MailException on protocol errors 
     198     */ 
     199    public abstract void idleModeEnd() throws IOException, MailException; 
     200 
     201    /** 
     202     * Polls the connecting during the idle mode for the underlying protocol. 
     203     * This should do nothing if the underlying protocol does not support idling. 
     204     *  
     205     * @return True if the mailbox has new data, false otherwise. 
     206     * @throws IOException on I/O errors 
     207     * @throws MailException on protocol errors 
     208     */ 
     209    public abstract boolean idleModePoll() throws IOException, MailException; 
    175210} 
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/IncomingMailConnectionHandler.java

    r264 r266  
    3939import org.logicprobe.LogicMail.message.Message; 
    4040import org.logicprobe.LogicMail.message.MessageFlags; 
     41import org.logicprobe.LogicMail.util.Queue; 
    4142 
    4243public class IncomingMailConnectionHandler extends AbstractMailConnectionHandler { 
     
    113114         * Handles the start of the IDLE state. 
    114115         */ 
    115         protected void handleBeginIdle() { 
     116        protected void handleBeginIdle() throws IOException, MailException { 
     117                if(incomingClient.hasIdle()) { 
     118                        incomingClient.idleModeBegin(); 
     119                        boolean endIdle = false; 
     120                        while(!endIdle) { 
     121                                sleepConnectionThread(500); 
     122                                if(incomingClient.idleModePoll()) { 
     123                                        addRequest( 
     124                                                        IncomingMailConnectionHandler.REQUEST_FOLDER_MESSAGES_RECENT, 
     125                                                        new Object[] { incomingClient.getActiveFolder() }); 
     126                                        endIdle = true; 
     127                                } 
     128                                else if(getShutdownInProgress()) { 
     129                                        endIdle = true; 
     130                                } 
     131                                else 
     132                                { 
     133                                        Queue requestQueue = getRequestQueue(); 
     134                                        synchronized(requestQueue) { 
     135                                                if(requestQueue.element() != null) { 
     136                                                        endIdle = true; 
     137                                                } 
     138                                        } 
     139                                } 
     140                        } 
     141                        incomingClient.idleModeEnd(); 
     142                } 
    116143        } 
    117144 
  • trunk/LogicMail/src/org/logicprobe/LogicMail/mail/pop/PopClient.java

    r264 r266  
    210210    } 
    211211 
     212    public boolean hasIdle() { 
     213                return false; 
     214        } 
     215 
    212216    public FolderTreeItem getFolderTree() throws IOException, MailException { 
    213217        return null; 
     
    336340        // Undelete is not supported, so we do nothing here. 
    337341    } 
     342 
     343        public void idleModeBegin() throws IOException, MailException { 
     344                // Idle mode is not supported, so we do nothing here. 
     345        } 
     346 
     347        public void idleModeEnd() throws IOException, MailException { 
     348                // Idle mode is not supported, so we do nothing here. 
     349        } 
     350 
     351        public boolean idleModePoll() throws IOException, MailException { 
     352                // Idle mode is not supported, so we do nothing here. 
     353                return false; 
     354        } 
    338355} 
  • trunk/LogicMail/src/org/logicprobe/LogicMail/util/Connection.java

    r209 r266  
    103103    protected OutputStream output; 
    104104    private boolean useWiFi; 
     105    private int fakeAvailable = -1; 
    105106     
    106107    /** 
     
    114115    private byte[] buffer = new byte[128]; 
    115116     
    116     /** 
    117      * Holds the actual number of bytes in the buffer. 
    118      */ 
    119     private int count; 
    120  
    121117    /** 
    122118     * Holds a list of open connections 
     
    150146         
    151147        String protocolStr = (useSSL ? "ssl" : "socket"); 
    152         // This param, which allows bypassing the MDS proxy, should probably 
     148        // This parameter, which allows bypassing the MDS proxy, should probably 
    153149        // be a global user configurable option 
    154150        String paramStr = (deviceSide ? ";deviceside=true" : ""); 
     
    287283     * Sends a string to the server. This method is used internally for 
    288284     * all outgoing communication to the server. The main thing it does 
    289      * it terminate the line with a CR/LF. If there are occurences of CR or 
     285     * it terminate the line with a CR/LF. If there are occurrences of CR or 
    290286     * LF inside the string, the method ensures that proper CR/LF sequences 
    291      * are sent for them, since this is what most internet protocols expect. 
     287     * are sent for them, since this is what most Internet protocols expect. 
    292288     * 
    293289     * @see #receive 
     
    316312                 
    317313                /** 
    318                  * Find next occurence of a line separator or the end of the 
     314                 * Find next occurrence of a line separator or the end of the 
    319315                 * string. 
    320316                 */ 
     
    384380    } 
    385381 
     382    /** 
     383     * Returns the number of bytes available for reading. 
     384     * Used to poll the connection without blocking. 
     385     *  
     386     * @see InputStream#available() 
     387     */ 
     388    public int available() throws IOException { 
     389        if(fakeAvailable == -1) { 
     390                return input.available(); 
     391        } 
     392        else { 
     393                return fakeAvailable; 
     394        } 
     395    } 
     396     
    386397    /** 
    387398     * Receives a string from the server. This method is used internally for 
     
    403414         * characters belonging to a line were read. The result was 
    404415         * a high level of heap fragmentation, which made applications 
    405          * instable on MIDP devices (that don't provide compacting GC, 
     416         * unstable on MIDP devices (that don't provide compacting GC, 
    406417         * that is). 
    407418         * 
     
    423434        StringBuffer resultBuffer = new StringBuffer(); 
    424435        boolean stop = false; 
     436        int actualAvailable = input.available(); 
     437        int readBytes = 0; 
     438        int count; 
    425439         
    426440        /** 
     
    470484                 */ 
    471485                // Note: We really should look for CRLF, and not use this 
    472                 // half-assed approach which screws up on mid-lime LFs. (DK) 
     486                // approach which screws up on mid-line LFs. (DK) 
    473487                else { 
    474488                    byte b = buffer[count]; 
     489                    readBytes++; 
    475490                     
    476491                    /** 
     
    504519            EventLogger.logEvent(AppInfo.GUID, ("[RECV] " + resultBuffer.toString()).getBytes(), EventLogger.DEBUG_INFO); 
    505520        } 
     521        if(actualAvailable > readBytes) { 
     522                fakeAvailable = actualAvailable - readBytes; 
     523        } 
     524        else { 
     525                fakeAvailable = -1; 
     526        } 
    506527         
    507528        return resultBuffer.toString();