[SCM] davmail packaging branch, master, updated. 02c2b101f323ee4dc652f941a0d9441a59cfa0da

Alexandre Rossi alexandre.rossi at gmail.com
Mon Oct 1 09:07:57 UTC 2012


The following commit has been merged in the master branch:
commit 53954f6134ccce2d33b051a70f8030622c1df570
Author: Alexandre Rossi <alexandre.rossi at gmail.com>
Date:   Mon Oct 1 11:03:26 2012 +0200

    Imported Upstream version 4.1.0-2042

diff --git a/build.xml b/build.xml
index 0cbba32..9528088 100644
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,6 @@
 <project name="DavMail" default="dist" basedir=".">
     <property file="user.properties"/>
-    <property name="version" value="4.0.0"/>
+    <property name="version" value="4.1.0"/>
 
     <path id="classpath">
         <pathelement location="classes"/>
@@ -23,7 +23,10 @@
     </condition>
 
     <condition property="is.java6">
-        <equals arg1="${ant.java.version}" arg2="1.6"/>
+        <or>
+            <equals arg1="${ant.java.version}" arg2="1.6"/>
+            <equals arg1="${ant.java.version}" arg2="1.7"/>
+        </or>
     </condition>
 
     <target name="check-java6" unless="is.java6">
@@ -111,6 +114,9 @@
         <!-- prepare hide from dock option -->
         <replaceregexp file="dist/DavMail.app/Contents/Info.plist" match="<key>CFBundleName</key>"
                        replace="<key>LSUIElement</key><string>0</string><key>CFBundleName</key>"/>
+        <!-- Retina display support -->
+        <replaceregexp file="dist/DavMail.app/Contents/Info.plist" match="<key>CFBundleName</key>"
+                       replace="<key>NSHighResolutionCapable</key><true/><key>CFBundleName</key>"/>
         <zip file="dist/DavMail-MacOSX-${release}.app.zip">
             <zipfileset dir="dist">
                 <include name="DavMail.app/**/*"/>
diff --git a/pom.xml b/pom.xml
index edbe389..e4514b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
     <groupId>davmail</groupId>
     <artifactId>davmail</artifactId>
     <packaging>jar</packaging>
-    <version>4.0.0</version>
+    <version>4.1.0</version>
     <name>DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange Gateway</name>
     <organization>
         <name>Mickaël Guessant</name>
diff --git a/releasenotes.txt b/releasenotes.txt
index e462000..d1b5bdf 100644
--- a/releasenotes.txt
+++ b/releasenotes.txt
@@ -1,3 +1,45 @@
+** DavMail 4.1.0 released **
+Bugfix release with improved IMAP support, including IMAP flags mapping to Outlook categories,
+enhanced IMAP noop/idle support, fixed emClient Caldav support and many Caldav and EWS fixes.
+
+Documentation:
+- Doc: update roadmap
+- Doc: new FAQ entry, Exchange RSA two factor authentication form
+
+Caldav:
+- Caldav: do not try to load tasks MIME body
+- Caldav: workaround for 3569922: quick fix for broken Israeli Timezone issue
+- Caldav: remove urlencoding workaround for emClient >= 4
+- Caldav: Ignore 401 unauthorized on public event, return 200
+- Caldav: Rename TZID also in RECURRENCE-ID
+- Caldav: force 403 forbidden instead of 401 on unauthorized update to public folder item
+- Caldav: Fix 3569934 NullPointerException on broken PROPFIND request
+- Caldav: Fix 3567364, regression on from/to/cc handling in calendar related to IMAP search enhancement. Separate mapping for message fields/headers
+
+IMAP:
+- IMAP: send updated flags on folder refresh
+- IMAP: fix keyword handling to avoid sending \Seen as keyword
+- IMAP: retrieve message count on folder
+- IMAP: apply flag to keyword conversion in SEARCH, refresh folder before search
+- IMAP: improve keyword support, map $label1 to 5 from Thunderbird to Outlook categories
+- IMAP: fix keywords implementation, make it case insensitive, implement KEYWORD search
+- IMAP: implement generic FLAGS mapping to Outlook categories
+- IMAP: fix 3566412, range iterator is on folder messages, not messages returned from search
+
+EWS:
+- EWS: Get primary smtp email address with ResolveNames in direct EWS mode
+
+Enhancements
+- Allow Java 7 to build DavMail
+- Prepare message keywords/categories support
+
+WebDav:
+- Dav: implement multivalued property suppord in ExchangeDavMethod
+
+Web:
+- Web: Fix 3566941 Imap protocol is not activated by default in .war
+
+
 ** DavMail 4.0.0 released **
 Includes full Exchange 2007 and 2010 support with EWS implementation, 
 fixed OSX Mountain Lion support, switched Windows wrappers to WinRun4J 
diff --git a/src/java/davmail/caldav/CaldavConnection.java b/src/java/davmail/caldav/CaldavConnection.java
index 516d9c9..18fc922 100644
--- a/src/java/davmail/caldav/CaldavConnection.java
+++ b/src/java/davmail/caldav/CaldavConnection.java
@@ -1428,7 +1428,7 @@ public class CaldavConnection extends AbstractConnection {
         }
 
         protected boolean isBrokenHrefEncoding() {
-            return isUserAgent("DAVKit/3") || isUserAgent("eM Client/") || isBrokenLightning();
+            return isUserAgent("DAVKit/3") || isUserAgent("eM Client/3") || isBrokenLightning();
         }
 
         protected boolean isBrokenLightning() {
@@ -1480,6 +1480,9 @@ public class CaldavConnection extends AbstractConnection {
         }
 
         protected void parseXmlBody() throws IOException {
+            if (body == null) {
+                throw new DavMailException("EXCEPTION_INVALID_CALDAV_REQUEST", "Missing body");
+            }
             XMLStreamReader streamReader = null;
             try {
                 streamReader = XMLStreamUtil.createXMLStreamReader(body);
diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java
index 68c53df..89e8350 100644
--- a/src/java/davmail/exchange/ExchangeSession.java
+++ b/src/java/davmail/exchange/ExchangeSession.java
@@ -788,6 +788,7 @@ public abstract class ExchangeSession {
         IMAP_MESSAGE_ATTRIBUTES.add("lastmodified");
         // OSX IMAP requests content-class
         IMAP_MESSAGE_ATTRIBUTES.add("contentclass");
+        IMAP_MESSAGE_ATTRIBUTES.add("keywords");
     }
 
     protected static final Set<String> UID_MESSAGE_ATTRIBUTES = new HashSet<String>();
@@ -1470,6 +1471,43 @@ public abstract class ExchangeSession {
     protected abstract void moveToTrash(Message message) throws IOException;
 
     /**
+     * Convert keyword value to IMAP flag.
+     * @param value keyword value
+     * @return IMAP flag
+     */
+    public String convertKeywordToFlag(String value) {
+        String result = value;
+        // convert flags to Thunderbird flags
+        ResourceBundle flagBundle = ResourceBundle.getBundle("imapflags");
+        Enumeration<String> flagEnumeration = flagBundle.getKeys();
+        while (flagEnumeration.hasMoreElements()) {
+            String key = flagEnumeration.nextElement();
+            if (value.equalsIgnoreCase(flagBundle.getString(key))) {
+                result = key;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Convert IMAP flag to keyword value.
+     * @param value IMAP flag
+     * @return keyword value
+     */
+    public String convertFlagToKeyword(String value) {
+        String result = value;
+        // convert flags to Thunderbird flags
+        ResourceBundle flagBundle = ResourceBundle.getBundle("imapflags");
+        try {
+            result = flagBundle.getString(value);
+        } catch (MissingResourceException e) {
+            // ignore
+        }
+
+        return result;
+    }
+
+    /**
      * Exchange folder with IMAP properties
      */
     public class Folder {
@@ -1487,6 +1525,10 @@ public abstract class ExchangeSession {
          */
         public String folderClass;
         /**
+         * Folder message count.
+         */
+        public int count;
+        /**
          * Folder unread message count.
          */
         public int unreadCount;
@@ -1622,16 +1664,16 @@ public abstract class ExchangeSession {
         }
 
         /**
-         * Get current folder messages imap uids
+         * Get current folder messages imap uids and flags
          *
          * @return imap uid list
          */
-        public List<Long> getImapUidList() {
-            ArrayList<Long> imapUidList = new ArrayList<Long>();
+        public TreeMap<Long,String> getImapFlagMap() {
+            TreeMap<Long,String> imapFlagMap = new TreeMap<Long,String>();
             for (ExchangeSession.Message message : messages) {
-                imapUidList.add(message.getImapUid());
+                imapFlagMap.put(message.getImapUid(), message.getImapFlags());
             }
-            return imapUidList;
+            return imapFlagMap;
         }
 
         /**
@@ -1696,6 +1738,10 @@ public abstract class ExchangeSession {
          */
         public String contentClass;
         /**
+         * Message keywords (categories).
+         */
+        public String keywords;
+        /**
          * Message IMAP uid, unique in folder (x0e230003).
          */
         public long imapUid;
@@ -1817,6 +1863,11 @@ public abstract class ExchangeSession {
             if (forwarded) {
                 buffer.append("$Forwarded ");
             }
+            if (keywords != null) {
+                for (String keyword:keywords.split(",")) {
+                    buffer.append(convertKeywordToFlag(keyword)).append(" ");
+                }
+            }
             return buffer.toString().trim();
         }
 
@@ -1989,6 +2040,51 @@ public abstract class ExchangeSession {
         public int hashCode() {
             return (int) (imapUid ^ (imapUid >>> 32));
         }
+
+        public String removeFlag(String flag) {
+            if (keywords != null) {
+                final String exchangeFlag = convertFlagToKeyword(flag);
+                Set<String> keywordSet = new HashSet<String>();
+                String[] keywordArray = keywords.split(",");
+                for (String value : keywordArray) {
+                    if (!value.equalsIgnoreCase(exchangeFlag)) {
+                        keywordSet.add(value);
+                    }
+                }
+                keywords = StringUtil.join(keywordSet, ",");
+            }
+            return keywords;
+        }
+
+        public String addFlag(String flag) {
+            final String exchangeFlag = convertFlagToKeyword(flag);
+            HashSet<String> keywordSet = new HashSet<String>();
+            boolean hasFlag = false;
+            if (keywords != null) {
+                String[] keywordArray = keywords.split(",");
+                for (String value : keywordArray) {
+                    keywordSet.add(value);
+                    if (value.equalsIgnoreCase(exchangeFlag)) {
+                        hasFlag = true;
+                    }
+                }
+            }
+            if (!hasFlag) {
+                keywordSet.add(exchangeFlag);
+            }
+            keywords = StringUtil.join(keywordSet, ",");
+            return keywords;
+        }
+
+        public String setFlags(HashSet<String> flags) {
+            HashSet<String> keywordSet = new HashSet<String>();
+            for (String flag : flags) {
+                keywordSet.add(convertFlagToKeyword(flag));
+            }
+            keywords = StringUtil.join(keywordSet, ",");
+            return keywords;
+        }
+
     }
 
     /**
diff --git a/src/java/davmail/exchange/ExchangeSessionFactory.java b/src/java/davmail/exchange/ExchangeSessionFactory.java
index 6094f71..1b2be21 100644
--- a/src/java/davmail/exchange/ExchangeSessionFactory.java
+++ b/src/java/davmail/exchange/ExchangeSessionFactory.java
@@ -169,6 +169,8 @@ public final class ExchangeSessionFactory {
             throw exc;
         } catch (IllegalStateException exc) {
             throw exc;
+        } catch (NullPointerException exc) {
+            throw exc;
         } catch (Exception exc) {
             handleNetworkDown(exc);
         }
diff --git a/src/java/davmail/exchange/VCalendar.java b/src/java/davmail/exchange/VCalendar.java
index c61f06b..ec9b3fc 100644
--- a/src/java/davmail/exchange/VCalendar.java
+++ b/src/java/davmail/exchange/VCalendar.java
@@ -246,6 +246,10 @@ public class VCalendar extends VObject {
                         if (dtEnd != null && dtStart.getParam("TZID") != null) {
                             dtEnd.setParam("TZID", tzid);
                         }
+                        VProperty reccurrenceId = vObject.getProperty("RECURRENCE-ID");
+                        if (reccurrenceId != null && reccurrenceId.getParam("TZID") != null) {
+                            reccurrenceId.setParam("TZID", tzid);
+                        }
                     }
                     // remove unsupported attachment reference
                     if (vObject.getProperty("ATTACH") != null) {
@@ -319,6 +323,15 @@ public class VCalendar extends VObject {
             vTimezone.vObjects.add(standard);
             vTimezone.vObjects.add(daylight);
         }
+        // fix 3569922: quick workaround for broken Israeli Timezone issue
+        if (vTimezone != null && vTimezone.vObjects != null) {
+            for (VObject vObject:vTimezone.vObjects) {
+                VProperty rrule = vObject.getProperty("RRULE");
+                if (rrule != null && rrule.getValues().size() == 3 && "BYDAY=-2SU".equals(rrule.getValues().get(1))) {
+                    rrule.getValues().set(1, "BYDAY=4SU");
+                }
+            }
+        }
     }
 
     private void fixTzid(VProperty property) {
diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java
index 8b97101..7639eb8 100644
--- a/src/java/davmail/exchange/dav/DavExchangeSession.java
+++ b/src/java/davmail/exchange/dav/DavExchangeSession.java
@@ -1347,27 +1347,30 @@ public class DavExchangeSession extends ExchangeSession {
         @Override
         public byte[] getEventContent() throws IOException {
             byte[] result = null;
-            LOGGER.debug("Get event subject: " + subject + " href: " + getHref() + " permanentUrl: " + permanentUrl);
-            // try to get PR_INTERNET_CONTENT
-            try {
-                result = getICSFromInternetContentProperty();
-                if (result == null) {
-                    GetMethod method = new GetMethod(encodeAndFixUrl(permanentUrl));
-                    method.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
-                    method.setRequestHeader("Translate", "f");
-                    try {
-                        DavGatewayHttpClientFacade.executeGetMethod(httpClient, method, true);
-                        result = getICS(method.getResponseBodyAsStream());
-                    } finally {
-                        method.releaseConnection();
+            LOGGER.debug("Get event subject: " + subject + " contentclass: "+contentClass+" href: " + getHref() + " permanentUrl: " + permanentUrl);
+            // do not try to load tasks MIME body
+            if (!"urn:content-classes:task".equals(contentClass)) {
+                // try to get PR_INTERNET_CONTENT
+                try {
+                    result = getICSFromInternetContentProperty();
+                    if (result == null) {
+                        GetMethod method = new GetMethod(encodeAndFixUrl(permanentUrl));
+                        method.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
+                        method.setRequestHeader("Translate", "f");
+                        try {
+                            DavGatewayHttpClientFacade.executeGetMethod(httpClient, method, true);
+                            result = getICS(method.getResponseBodyAsStream());
+                        } finally {
+                            method.releaseConnection();
+                        }
                     }
+                } catch (DavException e) {
+                    LOGGER.warn(e.getMessage());
+                } catch (IOException e) {
+                    LOGGER.warn(e.getMessage());
+                } catch (MessagingException e) {
+                    LOGGER.warn(e.getMessage());
                 }
-            } catch (DavException e) {
-                LOGGER.warn(e.getMessage());
-            } catch (IOException e) {
-                LOGGER.warn(e.getMessage());
-            } catch (MessagingException e) {
-                LOGGER.warn(e.getMessage());
             }
 
             // failover: rebuild event from MAPI properties
@@ -1676,6 +1679,9 @@ public class DavExchangeSession extends ExchangeSession {
                 // 440 means forbidden on Exchange
                 if (status == 440) {
                     status = HttpStatus.SC_FORBIDDEN;
+                } else if (status == HttpStatus.SC_UNAUTHORIZED && getHref().startsWith("/public")) {
+                    LOGGER.warn("Ignore 401 unauthorized on public event");
+                    status = HttpStatus.SC_OK;
                 }
                 itemResult.status = status;
                 if (putMethod.getResponseHeader("GetETag") != null) {
@@ -1715,7 +1721,10 @@ public class DavExchangeSession extends ExchangeSession {
         folder.folderClass = getPropertyIfExists(properties, "folderclass");
         folder.hasChildren = "1".equals(getPropertyIfExists(properties, "hassubs"));
         folder.noInferiors = "1".equals(getPropertyIfExists(properties, "nosubs"));
+        folder.count = getIntPropertyIfExists(properties, "count");
         folder.unreadCount = getIntPropertyIfExists(properties, "unreadcount");
+        // fake recent value
+        folder.recent = folder.unreadCount;
         folder.ctag = getPropertyIfExists(properties, "contenttag");
         folder.etag = getPropertyIfExists(properties, "lastmodified");
 
@@ -1764,6 +1773,7 @@ public class DavExchangeSession extends ExchangeSession {
         FOLDER_PROPERTIES.add("folderclass");
         FOLDER_PROPERTIES.add("hassubs");
         FOLDER_PROPERTIES.add("nosubs");
+        FOLDER_PROPERTIES.add("count");
         FOLDER_PROPERTIES.add("unreadcount");
         FOLDER_PROPERTIES.add("contenttag");
         FOLDER_PROPERTIES.add("lastmodified");
@@ -1929,7 +1939,13 @@ public class DavExchangeSession extends ExchangeSession {
                     if (buffer.length() > 0) {
                         buffer.append(',');
                     }
-                    buffer.append(((Node) node).getTextContent());
+                    if (node instanceof Node) {
+                        // jackrabbit
+                        buffer.append(((Node) node).getTextContent());
+                    } else {
+                        // ExchangeDavMethod
+                        buffer.append(node);
+                    }
                 }
                 return buffer.toString();
             } else {
@@ -2010,6 +2026,8 @@ public class DavExchangeSession extends ExchangeSession {
         String lastmodified = convertDateFromExchange(getPropertyIfExists(properties, "lastmodified"));
         message.recent = !message.read && lastmodified != null && lastmodified.equals(message.date);
 
+        message.keywords = getPropertyIfExists(properties, "keywords");
+
         if (LOGGER.isDebugEnabled()) {
             StringBuilder buffer = new StringBuilder();
             buffer.append("Message");
@@ -2066,6 +2084,7 @@ public class DavExchangeSession extends ExchangeSession {
         ITEM_PROPERTIES.add("instancetype");
         ITEM_PROPERTIES.add("urlcompname");
         ITEM_PROPERTIES.add("subject");
+        ITEM_PROPERTIES.add("contentclass");
     }
 
     protected Set<String> getItemProperties() {
@@ -2486,6 +2505,8 @@ public class DavExchangeSession extends ExchangeSession {
                     list.add(Field.createDavProperty("deleted", entry.getValue()));
                 } else if ("datereceived".equals(entry.getKey())) {
                     list.add(Field.createDavProperty("datereceived", entry.getValue()));
+                } else if ("keywords".equals(entry.getKey())) {
+                    list.add(Field.createDavProperty("keywords", entry.getValue()));
                 }
             }
         }
diff --git a/src/java/davmail/exchange/dav/ExchangeDavMethod.java b/src/java/davmail/exchange/dav/ExchangeDavMethod.java
index 757a703..c48d4dd 100644
--- a/src/java/davmail/exchange/dav/ExchangeDavMethod.java
+++ b/src/java/davmail/exchange/dav/ExchangeDavMethod.java
@@ -178,12 +178,32 @@ public abstract class ExchangeDavMethod extends PostMethod {
             if (XMLStreamUtil.isStartTag(reader)) {
                 Namespace namespace = Namespace.getNamespace(reader.getNamespaceURI());
                 String tagLocalName = reader.getLocalName();
+                if (reader.getAttributeCount() > 0 && "mv.string".equals(reader.getAttributeValue(0))) {
+                     handleMultiValuedProperty(reader, multiStatusResponse);
+                } else {
+                    String tagContent = getTagContent(reader);
+                    if (tagContent != null) {
+                        multiStatusResponse.add(new DefaultDavProperty(tagLocalName, tagContent, namespace));
+                    }
+                }
+            }
+        }
+    }
+
+    protected void handleMultiValuedProperty(XMLStreamReader reader, MultiStatusResponse multiStatusResponse) throws XMLStreamException {
+        String tagLocalName = reader.getLocalName();
+        Namespace namespace = Namespace.getNamespace(reader.getNamespaceURI());
+        ArrayList<String> values = new ArrayList<String>();
+        while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, tagLocalName)) {
+            reader.next();
+            if (XMLStreamUtil.isStartTag(reader)) {
                 String tagContent = getTagContent(reader);
                 if (tagContent != null) {
-                    multiStatusResponse.add(new DefaultDavProperty(tagLocalName, tagContent, namespace));
+                    values.add(tagContent);
                 }
             }
         }
+        multiStatusResponse.add(new DefaultDavProperty(tagLocalName, values, namespace));
     }
 
     protected String getTagContent(XMLStreamReader reader) throws XMLStreamException {
diff --git a/src/java/davmail/exchange/dav/Field.java b/src/java/davmail/exchange/dav/Field.java
index fabfe59..15b71b9 100644
--- a/src/java/davmail/exchange/dav/Field.java
+++ b/src/java/davmail/exchange/dav/Field.java
@@ -106,6 +106,7 @@ public class Field {
         createField("folderclass", SCHEMAS_EXCHANGE, "outlookfolderclass");
         createField(DAV, "hassubs");
         createField(DAV, "nosubs");
+        createField("count", DAV, "objectcount");
         createField(URN_SCHEMAS_HTTPMAIL, "unreadcount");
         createField(SCHEMAS_REPL, "contenttag");
 
@@ -537,7 +538,7 @@ public class Field {
         } else if (field.isMultivalued) {
             // multivalued field, split values separated by \n
             List<XmlSerializable> valueList = new ArrayList<XmlSerializable>();
-            String[] values = value.split("\n");
+            String[] values = value.split(",");
             for (final String singleValue : values) {
                 valueList.add(new XmlSerializable() {
                     public Element toXml(Document document) {
diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java
index 41b25f3..5cd960e 100644
--- a/src/java/davmail/exchange/ews/EwsExchangeSession.java
+++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java
@@ -186,7 +186,12 @@ public class EwsExchangeSession extends ExchangeSession {
         // no need to check logon method body
         if (method != null) {
             method.releaseConnection();
-            // need to retrieve email and alias
+        }
+        boolean directEws = method == null || "/ews/services.wsdl".equalsIgnoreCase(method.getPath());
+
+        // options page is not available in direct EWS mode
+        if (!directEws) {
+            // retrieve email and alias from options page
             getEmailAndAliasFromOptions();
         }
 
@@ -240,6 +245,23 @@ public class EwsExchangeSession extends ExchangeSession {
             httpClient.getParams().setParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
         }
 
+        // direct EWS: get primary smtp email address with ResolveNames
+        if (directEws) {
+            try {
+                ResolveNamesMethod resolveNamesMethod = new ResolveNamesMethod(alias);
+                executeMethod(resolveNamesMethod);
+                List<EWSMethod.Item> responses = resolveNamesMethod.getResponseItems();
+                for (EWSMethod.Item response : responses) {
+                    if (alias.equalsIgnoreCase(response.get("Name"))) {
+                        email = response.get("EmailAddress");
+                        currentMailboxPath = "/users/" + email.toLowerCase();
+                    }
+                }
+            } catch (IOException e) {
+                LOGGER.warn("Unable to get primary email address with ResolveNames", e);
+            }
+        }
+
         try {
             folderIdMap = new HashMap<String, String>();
             // load actual well known folder ids
@@ -428,6 +450,8 @@ public class EwsExchangeSession extends ExchangeSession {
                 list.add(Field.createFieldUpdate("deleted", entry.getValue()));
             } else if ("datereceived".equals(entry.getKey())) {
                 list.add(Field.createFieldUpdate("datereceived", entry.getValue()));
+            } else if ("keywords".equals(entry.getKey())) {
+                list.add(Field.createFieldUpdate("keywords", entry.getValue()));
             }
         }
         return list;
@@ -617,6 +641,8 @@ public class EwsExchangeSession extends ExchangeSession {
         String lastmodified = convertDateFromExchange(response.get(Field.get("lastmodified").getResponseName()));
         message.recent = !message.read && lastmodified != null && lastmodified.equals(message.date);
 
+        message.keywords = response.get(Field.get("keywords").getResponseName());
+
         if (LOGGER.isDebugEnabled()) {
             StringBuilder buffer = new StringBuilder();
             buffer.append("Message");
@@ -865,10 +891,11 @@ public class EwsExchangeSession extends ExchangeSession {
     @Override
     public Condition headerIsEqualTo(String headerName, String value) {
         if (serverVersion.startsWith("Exchange2010")) {
-            if ("message-id".equals(headerName)
-                    || "from".equals(headerName)
+            if ("from".equals(headerName)
                     || "to".equals(headerName)
-                    || "cc".equals(headerName)
+                    || "cc".equals(headerName)) {
+                return new AttributeCondition("msg"+headerName, Operator.Contains, value, ContainmentMode.Substring, ContainmentComparison.IgnoreCase);
+            } else if ("message-id".equals(headerName)
                     || "bcc".equals(headerName)) {
                 return new AttributeCondition(headerName, Operator.Contains, value, ContainmentMode.Substring, ContainmentComparison.IgnoreCase);
             } else {
@@ -933,6 +960,7 @@ public class EwsExchangeSession extends ExchangeSession {
         FOLDER_PROPERTIES.add(Field.get("lastmodified"));
         FOLDER_PROPERTIES.add(Field.get("folderclass"));
         FOLDER_PROPERTIES.add(Field.get("ctag"));
+        FOLDER_PROPERTIES.add(Field.get("count"));
         FOLDER_PROPERTIES.add(Field.get("unread"));
         FOLDER_PROPERTIES.add(Field.get("hassubs"));
         FOLDER_PROPERTIES.add(Field.get("uidNext"));
@@ -946,7 +974,10 @@ public class EwsExchangeSession extends ExchangeSession {
         folder.folderClass = item.get(Field.get("folderclass").getResponseName());
         folder.etag = item.get(Field.get("lastmodified").getResponseName());
         folder.ctag = item.get(Field.get("ctag").getResponseName());
+        folder.count = item.getInt(Field.get("count").getResponseName());
         folder.unreadCount = item.getInt(Field.get("unread").getResponseName());
+        // fake recent value
+        folder.recent = folder.unreadCount;
         folder.hasChildren = item.getBoolean(Field.get("hassubs").getResponseName());
         // noInferiors not implemented
         folder.uidNext = item.getInt(Field.get("uidNext").getResponseName());
@@ -2088,7 +2119,7 @@ public class EwsExchangeSession extends ExchangeSession {
 
     protected FolderId getFolderIdIfExists(String folderPath) throws IOException {
         String lowerCaseFolderPath = folderPath.toLowerCase();
-        if (currentMailboxPath.equals(lowerCaseFolderPath)) {
+        if (lowerCaseFolderPath.equals(currentMailboxPath)) {
             return getSubFolderIdIfExists(null, "");
         } else if (lowerCaseFolderPath.startsWith(currentMailboxPath + '/')) {
             return getSubFolderIdIfExists(null, folderPath.substring(currentMailboxPath.length() + 1));
diff --git a/src/java/davmail/exchange/ews/ExtendedFieldURI.java b/src/java/davmail/exchange/ews/ExtendedFieldURI.java
index 9571801..702ba01 100644
--- a/src/java/davmail/exchange/ews/ExtendedFieldURI.java
+++ b/src/java/davmail/exchange/ews/ExtendedFieldURI.java
@@ -128,7 +128,7 @@ public class ExtendedFieldURI implements FieldURI {
         appendTo(buffer);
         if (propertyType == PropertyType.StringArray) {
             buffer.append("<t:Values>");
-            String[] values = value.split("\n");
+            String[] values = value.split(",");
             for (final String singleValue : values) {
                 buffer.append("<t:Value>");
                 buffer.append(StringUtil.xmlEncode(singleValue));
diff --git a/src/java/davmail/exchange/ews/Field.java b/src/java/davmail/exchange/ews/Field.java
index f7b0c0e..3fa0549 100644
--- a/src/java/davmail/exchange/ews/Field.java
+++ b/src/java/davmail/exchange/ews/Field.java
@@ -40,7 +40,9 @@ public final class Field {
 
         // folder
         FIELD_MAP.put("ctag", new ExtendedFieldURI(0x670a, ExtendedFieldURI.PropertyType.SystemTime)); // PR_LOCAL_COMMIT_TIME_MAX
+        FIELD_MAP.put("count", new ExtendedFieldURI(0x3602, ExtendedFieldURI.PropertyType.Integer)); // PR_CONTENT_COUNT
         FIELD_MAP.put("unread", new ExtendedFieldURI(0x3603, ExtendedFieldURI.PropertyType.Integer)); // PR_CONTENT_UNREAD
+
         FIELD_MAP.put("hassubs", new ExtendedFieldURI(0x360a, ExtendedFieldURI.PropertyType.Boolean)); // PR_SUBFOLDERS
         FIELD_MAP.put("folderDisplayName", new UnindexedFieldURI("folder:DisplayName"));
 
@@ -74,15 +76,22 @@ public final class Field {
         FIELD_MAP.put("iconIndex", new ExtendedFieldURI(0x1080, ExtendedFieldURI.PropertyType.Integer));// PR_ICON_INDEX
         FIELD_MAP.put("datereceived", new ExtendedFieldURI(0x0e06, ExtendedFieldURI.PropertyType.SystemTime));// PR_MESSAGE_DELIVERY_TIME
 
-        FIELD_MAP.put("to", new UnindexedFieldURI("message:ToRecipients"));
-        FIELD_MAP.put("cc", new UnindexedFieldURI("message:CcRecipients"));
-        FIELD_MAP.put("from", new UnindexedFieldURI("message:From"));
+        FIELD_MAP.put("msgfrom", new UnindexedFieldURI("message:From"));
+        FIELD_MAP.put("msgto", new UnindexedFieldURI("message:ToRecipients"));
+        FIELD_MAP.put("msgcc", new UnindexedFieldURI("message:CcRecipients"));
+
+        FIELD_MAP.put("from", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, "from"));
+        FIELD_MAP.put("to", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, "to"));
+        FIELD_MAP.put("cc", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, "cc"));
+        FIELD_MAP.put("bcc", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, "bcc"));
+
+        FIELD_MAP.put("message-id", new UnindexedFieldURI("message:InternetMessageId"));
         FIELD_MAP.put("bcc", new UnindexedFieldURI("message:BccRecipients"));
 
         FIELD_MAP.put("messageheaders", new ExtendedFieldURI(0x007D, ExtendedFieldURI.PropertyType.String)); // PR_TRANSPORT_MESSAGE_HEADERS
 
         FIELD_MAP.put("contentclass", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, "content-class"));
-        FIELD_MAP.put("message-id", new UnindexedFieldURI("message:InternetMessageId"));
+
 
         FIELD_MAP.put("body", new UnindexedFieldURI("item:Body"));
 
diff --git a/src/java/davmail/imap/ImapConnection.java b/src/java/davmail/imap/ImapConnection.java
index ecabef9..b42004e 100644
--- a/src/java/davmail/imap/ImapConnection.java
+++ b/src/java/davmail/imap/ImapConnection.java
@@ -228,7 +228,7 @@ public class ImapConnection extends AbstractConnection {
                                                 sendClient("* OK [UIDNEXT " + currentFolder.getUidNext() + ']');
                                             }
                                             sendClient("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded Junk)");
-                                            sendClient("* OK [PERMANENTFLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded Junk)]");
+                                            sendClient("* OK [PERMANENTFLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded Junk \\*)]");
                                             if ("select".equalsIgnoreCase(command)) {
                                                 sendClient(commandId + " OK [READ-WRITE] " + command + " completed");
                                             } else {
@@ -434,7 +434,7 @@ public class ImapConnection extends AbstractConnection {
                                     // handle optional flags
                                     String nextToken = tokens.nextQuotedToken();
                                     if (nextToken.startsWith("(")) {
-                                        flags = removeQuotes(nextToken);
+                                        flags = StringUtil.removeQuotes(nextToken);
                                         if (tokens.hasMoreTokens()) {
                                             nextToken = tokens.nextToken();
                                             if (tokens.hasMoreTokens()) {
@@ -443,7 +443,7 @@ public class ImapConnection extends AbstractConnection {
                                             }
                                         }
                                     } else if (tokens.hasMoreTokens()) {
-                                        date = removeQuotes(nextToken);
+                                        date = StringUtil.removeQuotes(nextToken);
                                         nextToken = tokens.nextToken();
                                     }
 
@@ -492,7 +492,7 @@ public class ImapConnection extends AbstractConnection {
 
                                         properties.put("datereceived", dateFormatter.format(dateReceived));
                                     }
-                                    int size = Integer.parseInt(removeQuotes(nextToken));
+                                    int size = Integer.parseInt(StringUtil.removeQuotes(nextToken));
                                     sendClient("+ send literal data");
                                     byte[] buffer = in.readContent(size);
                                     // empty line
@@ -517,9 +517,9 @@ public class ImapConnection extends AbstractConnection {
                                             while (in.available() == 0) {
                                                 if (++count >= imapIdleDelay) {
                                                     count = 0;
-                                                    List<Long> previousImapUidList = currentFolder.getImapUidList();
+                                                    TreeMap<Long,String> previousImapFlagMap = currentFolder.getImapFlagMap();
                                                     if (session.refreshFolder(currentFolder)) {
-                                                        handleRefresh(previousImapUidList, currentFolder.getImapUidList());
+                                                        handleRefresh(previousImapFlagMap, currentFolder.getImapFlagMap());
                                                     }
                                                 }
                                                 // sleep 1 second
@@ -542,9 +542,9 @@ public class ImapConnection extends AbstractConnection {
                                 } else if ("noop".equalsIgnoreCase(command) || "check".equalsIgnoreCase(command)) {
                                     if (currentFolder != null) {
                                         DavGatewayTray.debug(new BundleMessage("LOG_IMAP_COMMAND", command, currentFolder.folderPath));
-                                        List<Long> previousImapUidList = currentFolder.getImapUidList();
+                                        TreeMap<Long,String> previousImapFlagMap = currentFolder.getImapFlagMap();
                                         if (session.refreshFolder(currentFolder)) {
-                                            handleRefresh(previousImapUidList, currentFolder.getImapUidList());
+                                            handleRefresh(previousImapFlagMap, currentFolder.getImapFlagMap());
                                         }
                                     }
                                     sendClient(commandId + " OK " + command + " completed");
@@ -691,16 +691,21 @@ public class ImapConnection extends AbstractConnection {
      * @param imapUidList         uid list after refresh
      * @throws IOException on error
      */
-    private void handleRefresh(List<Long> previousImapUidList, List<Long> imapUidList) throws IOException {
-        //
+    private void handleRefresh(TreeMap<Long,String> previousImapFlagMap, TreeMap<Long,String> imapFlagMap) throws IOException {
+        // send deleted message expunge notification
         int index = 1;
-        for (long previousImapUid : previousImapUidList) {
-            if (!imapUidList.contains(previousImapUid)) {
+        for (long previousImapUid : previousImapFlagMap.keySet()) {
+            if (!imapFlagMap.keySet().contains(previousImapUid)) {
                 sendClient("* " + index + " EXPUNGE");
             } else {
+	            // send updated flags
+                if (!previousImapFlagMap.get(previousImapUid).equals(imapFlagMap.get(previousImapUid))) {
+                    sendClient("* " + index + " FETCH (UID "+previousImapUid+" FLAGS ("+imapFlagMap.get(previousImapUid)+"))");
+                }
                 index++;
             }
         }
+
         sendClient("* " + currentFolder.count() + " EXISTS");
         sendClient("* " + currentFolder.recent + " RECENT");
     }
@@ -931,14 +936,22 @@ public class ImapConnection extends AbstractConnection {
 
     protected List<Long> handleSearch(IMAPTokenizer tokens) throws IOException {
         List<Long> uidList = new ArrayList<Long>();
+        List<Long> localMessagesUidList = null;
         SearchConditions conditions = new SearchConditions();
         ExchangeSession.Condition condition = buildConditions(conditions, tokens);
+        session.refreshFolder(currentFolder);
         ExchangeSession.MessageList localMessages = currentFolder.searchMessages(condition);
         Iterator<ExchangeSession.Message> iterator;
         if (conditions.uidRange != null) {
             iterator = new UIDRangeIterator(localMessages, conditions.uidRange);
         } else if (conditions.indexRange != null) {
-            iterator = new RangeIterator(localMessages, conditions.indexRange);
+            // range iterator is on folder messages, not messages returned from search
+            iterator = new RangeIterator(currentFolder.messages, conditions.indexRange);
+            localMessagesUidList = new ArrayList<Long>();
+            // build search result uid list
+            for (ExchangeSession.Message message:localMessages) {
+                localMessagesUidList.add(message.getImapUid());
+            }
         } else {
             iterator = localMessages.iterator();
         }
@@ -946,7 +959,9 @@ public class ImapConnection extends AbstractConnection {
             ExchangeSession.Message message = iterator.next();
             if ((conditions.flagged == null || message.flagged == conditions.flagged)
                     && (conditions.answered == null || message.answered == conditions.answered)
-                    && (conditions.draft == null || message.draft == conditions.draft)) {
+                    && (conditions.draft == null || message.draft == conditions.draft)
+                    // range iterator: include messages available in search result
+                    && (localMessagesUidList == null || localMessagesUidList.contains(message.getImapUid()))) {
                 uidList.add(message.getImapUid());
             }
         }
@@ -1262,6 +1277,8 @@ public class ImapConnection extends AbstractConnection {
                     session.contains("from", value),
                     session.contains("to", value),
                     session.contains("cc", value));
+        } else if ("KEYWORD".equals(token)) {
+            return session.contains("keywords", session.convertFlagToKeyword(tokens.nextToken()));
         } else if ("FROM".equals(token)) {
             return session.contains("from", tokens.nextToken());
         } else if ("TO".equals(token)) {
@@ -1381,48 +1398,76 @@ public class ImapConnection extends AbstractConnection {
             StringTokenizer flagtokenizer = new StringTokenizer(flags);
             while (flagtokenizer.hasMoreTokens()) {
                 String flag = flagtokenizer.nextToken();
-                if ("\\Seen".equalsIgnoreCase(flag) && message.read) {
-                    properties.put("read", "0");
-                    message.read = false;
-                } else if ("\\Flagged".equalsIgnoreCase(flag) && message.flagged) {
-                    properties.put("flagged", "0");
-                    message.flagged = false;
-                } else if ("\\Deleted".equalsIgnoreCase(flag) && message.deleted) {
-                    properties.put("deleted", null);
-                    message.deleted = false;
-                } else if ("Junk".equalsIgnoreCase(flag) && message.junk) {
-                    properties.put("junk", "0");
-                    message.junk = false;
-                } else if ("$Forwarded".equalsIgnoreCase(flag) && message.forwarded) {
-                    properties.put("forwarded", null);
-                    message.forwarded = false;
-                } else if ("\\Answered".equalsIgnoreCase(flag) && message.answered) {
-                    properties.put("answered", null);
-                    message.answered = false;
+                if ("\\Seen".equalsIgnoreCase(flag)) {
+                    if (message.read) {
+                        properties.put("read", "0");
+                        message.read = false;
+                    }
+                } else if ("\\Flagged".equalsIgnoreCase(flag)) {
+                    if (message.flagged) {
+                        properties.put("flagged", "0");
+                        message.flagged = false;
+                    }
+                } else if ("\\Deleted".equalsIgnoreCase(flag)) {
+                    if (message.deleted) {
+                        properties.put("deleted", null);
+                        message.deleted = false;
+                    }
+                } else if ("Junk".equalsIgnoreCase(flag)) {
+                    if (message.junk) {
+                        properties.put("junk", "0");
+                        message.junk = false;
+                    }
+                } else if ("$Forwarded".equalsIgnoreCase(flag)) {
+                    if (message.forwarded) {
+                        properties.put("forwarded", null);
+                        message.forwarded = false;
+                    }
+                } else if ("\\Answered".equalsIgnoreCase(flag)) {
+                    if (message.answered) {
+                        properties.put("answered", null);
+                        message.answered = false;
+                    }
+                } else if (message.keywords != null) {
+                    properties.put("keywords", message.removeFlag(flag));
                 }
             }
         } else if ("+Flags".equalsIgnoreCase(action) || "+FLAGS.SILENT".equalsIgnoreCase(action)) {
             StringTokenizer flagtokenizer = new StringTokenizer(flags);
             while (flagtokenizer.hasMoreTokens()) {
                 String flag = flagtokenizer.nextToken();
-                if ("\\Seen".equalsIgnoreCase(flag) && !message.read) {
-                    properties.put("read", "1");
-                    message.read = true;
-                } else if ("\\Deleted".equalsIgnoreCase(flag) && !message.deleted) {
-                    message.deleted = true;
-                    properties.put("deleted", "1");
-                } else if ("\\Flagged".equalsIgnoreCase(flag) && !message.flagged) {
-                    properties.put("flagged", "2");
-                    message.flagged = true;
-                } else if ("\\Answered".equalsIgnoreCase(flag) && !message.answered) {
-                    properties.put("answered", "102");
-                    message.answered = true;
-                } else if ("$Forwarded".equalsIgnoreCase(flag) && !message.forwarded) {
-                    properties.put("forwarded", "104");
-                    message.forwarded = true;
-                } else if ("Junk".equalsIgnoreCase(flag) && !message.junk) {
-                    properties.put("junk", "1");
-                    message.junk = true;
+                if ("\\Seen".equalsIgnoreCase(flag)) {
+                    if (!message.read) {
+                        properties.put("read", "1");
+                        message.read = true;
+                    }
+                } else if ("\\Deleted".equalsIgnoreCase(flag)) {
+                    if (!message.deleted) {
+                        message.deleted = true;
+                        properties.put("deleted", "1");
+                    }
+                } else if ("\\Flagged".equalsIgnoreCase(flag)) {
+                    if (!message.flagged) {
+                        properties.put("flagged", "2");
+                        message.flagged = true;
+                    }
+                } else if ("\\Answered".equalsIgnoreCase(flag)) {
+                    if (!message.answered) {
+                        properties.put("answered", "102");
+                        message.answered = true;
+                    }
+                } else if ("$Forwarded".equalsIgnoreCase(flag)) {
+                    if (!message.forwarded) {
+                        properties.put("forwarded", "104");
+                        message.forwarded = true;
+                    }
+                } else if ("Junk".equalsIgnoreCase(flag)) {
+                    if (!message.junk) {
+                        properties.put("junk", "1");
+                        message.junk = true;
+                    }
+                } else {
+                    properties.put("keywords", message.addFlag(flag));
                 }
             }
         } else if ("FLAGS".equalsIgnoreCase(action) || "FLAGS.SILENT".equalsIgnoreCase(action)) {
@@ -1433,6 +1478,7 @@ public class ImapConnection extends AbstractConnection {
             boolean flagged = false;
             boolean answered = false;
             boolean forwarded = false;
+            HashSet<String> keywords = null;
             // set flags from new flag list
             StringTokenizer flagtokenizer = new StringTokenizer(flags);
             while (flagtokenizer.hasMoreTokens()) {
@@ -1449,8 +1495,16 @@ public class ImapConnection extends AbstractConnection {
                     forwarded = true;
                 } else if ("Junk".equalsIgnoreCase(flag)) {
                     junk = true;
+                } else {
+                    if (keywords == null) {
+                        keywords = new HashSet<String>();
+                    }
+                    keywords.add(flag);
                 }
             }
+            if (keywords != null) {
+                properties.put("keywords", message.setFlags(keywords));
+            }
             if (read != message.read) {
                 message.read = read;
                 if (message.read) {
@@ -1533,17 +1587,6 @@ public class ImapConnection extends AbstractConnection {
         }
     }
 
-    protected String removeQuotes(String value) {
-        String result = value;
-        if (result.startsWith("\"") || result.startsWith("{") || result.startsWith("(")) {
-            result = result.substring(1);
-        }
-        if (result.endsWith("\"") || result.endsWith("}") || result.endsWith(")")) {
-            result = result.substring(0, result.length() - 1);
-        }
-        return result;
-    }
-
     /**
      * Filter to output only headers, also count full size
      */
@@ -1808,7 +1851,7 @@ public class ImapConnection extends AbstractConnection {
 
         @Override
         public String nextToken() {
-            return removeQuotes(nextQuotedToken());
+            return StringUtil.removeQuotes(nextQuotedToken());
         }
 
         public String nextQuotedToken() {
diff --git a/src/java/imapflags.properties b/src/java/imapflags.properties
new file mode 100644
index 0000000..a3c0ef4
--- /dev/null
+++ b/src/java/imapflags.properties
@@ -0,0 +1,23 @@
+#
+# DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
+# Copyright (C) 2012  Mickael Guessant
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+$label1=Important
+$label2=Work
+$label3=Personal
+$label4=To Do
+$label5=Later
diff --git a/src/java/imapflags_fr.properties b/src/java/imapflags_fr.properties
new file mode 100644
index 0000000..057ba67
--- /dev/null
+++ b/src/java/imapflags_fr.properties
@@ -0,0 +1,23 @@
+#
+# DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
+# Copyright (C) 2012  Mickael Guessant
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+$label1=Important
+$label2=Travail
+$label3=Personnel
+$label4=À faire
+$label5=En attente
diff --git a/src/site/xdoc/faq.xml b/src/site/xdoc/faq.xml
index 08754e3..c180f9b 100644
--- a/src/site/xdoc/faq.xml
+++ b/src/site/xdoc/faq.xml
@@ -63,7 +63,6 @@
                 <p>Note to Mac users: OSX applications do not like username with backslash, you have to set windows
                     domain name in DavMail advanced settings and use the simple username in client application.
                 </p>
-
                 <p>
                     <strong>Authentication fails with error in parsing the status line</strong>
                 </p>
@@ -75,6 +74,14 @@
                     OWA url.
                 </p>
                 <p>
+                    <strong>Exchange RSA two factor authentication form</strong>
+                </p>
+                <p>Exchange now supports two factor authentication for RSA tokens. This requires two potentially
+                    different user names in fields userid and username. Use a pipe in client to provide both values:
+                    <code>userid|username</code>. In some cases you will need to also provide domain name:
+                    <code>userid|domain\username</code>, do not set default windows domain name.
+                </p>
+                <p>
                     <strong>Where can I find DavMail settings file ?</strong>
                 </p>
                 <p>The default location for DavMail settings is a file named .davmail.properties in user home
diff --git a/src/site/xdoc/roadmap.xml b/src/site/xdoc/roadmap.xml
index 4bde3fd..d25137f 100644
--- a/src/site/xdoc/roadmap.xml
+++ b/src/site/xdoc/roadmap.xml
@@ -15,17 +15,7 @@
                 for improvement. The following section lists the expected new features
                 and enhancements in next DavMail versions.
             </p>
-            <subsection name="4.0">
-                <p>
-                    <strong>Next major version</strong>
-                </p>
-                <ul>
-                    <li>Implement optional ICS Todo/Task conversion to Outlook tasks: Done</li>
-                    <li>Exchange 2010 support through EWS: Done</li>
-                    <li>Switch wrappers to WinRun4J</li>
-                </ul>
-            </subsection>
-            <subsection name="4.0.1">
+            <subsection name="4.1.1">
                 <p>
                     <strong>Next patch release</strong>
                 </p>
@@ -33,7 +23,7 @@
                     <li>Test windows install on seven without admin rights</li>
                 </ul>
             </subsection>
-            <subsection name="4.1">
+            <subsection name="4.2">
                 <p>
                     <strong>Next minor release</strong>
                 </p>
diff --git a/src/web/WEB-INF/classes/davmail.properties b/src/web/WEB-INF/classes/davmail.properties
index 0a49025..0ac9ffc 100644
--- a/src/web/WEB-INF/classes/davmail.properties
+++ b/src/web/WEB-INF/classes/davmail.properties
@@ -1,6 +1,7 @@
 davmail.url=http://exchangeServer/exchange/
 davmail.popPort=1110
 davmail.smtpPort=1025
+davmail.imapPort=1143
 davmail.keepDelay=30
 davmail.sentKeepDelay=90
 davmail.caldavPastDelay=90

-- 
davmail packaging



More information about the pkg-java-commits mailing list