Index: jbossweb-2.1.13/java/org/apache/catalina/authenticator/DigestAuthenticator.java
===================================================================
--- jbossweb-2.1.13/java/org/apache/catalina/authenticator/DigestAuthenticator.java	(revision 2169)
+++ jbossweb-2.1.13/java/org/apache/catalina/authenticator/DigestAuthenticator.java	(working copy)
@@ -5,30 +5,27 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina.authenticator;
 
-
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
 import java.security.SecureRandom;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Random;
-import java.security.Principal;
-import java.util.StringTokenizer;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.catalina.Container;
@@ -38,6 +35,7 @@
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.util.ConcurrentMessageDigest;
 import org.apache.catalina.util.MD5Encoder;
 import org.jboss.logging.Logger;
 import org.jboss.logging.Logger;
@@ -61,19 +59,6 @@
     // -------------------------------------------------------------- Constants
 
     /**
-     * The MD5 helper object for this class.
-     */
-    protected static final MD5Encoder md5Encoder = new MD5Encoder();
-
-
-    /**
-     * Descriptive information about this implementation.
-     */
-    protected static final String info =
-        "org.apache.catalina.authenticator.DigestAuthenticator/1.0";
-
-
-    /**
      * Tomcat's DIGEST implementation only supports auth quality of protection.
      */
     protected static final String QOP = "auth";
@@ -83,12 +68,13 @@
 
     public DigestAuthenticator() {
         super();
+        setCache(false);
         try {
-            if (md5Helper == null)
+            if (md5Helper == null) {
                 md5Helper = MessageDigest.getInstance("MD5");
+            }
         } catch (NoSuchAlgorithmException e) {
-            e.printStackTrace();
-            throw new IllegalStateException();
+            throw new IllegalStateException(e);
         }
     }
 
@@ -98,21 +84,22 @@
 
     /**
      * MD5 message digest provider.
+     * @deprecated  Unused - will be removed in Tomcat 8.0.x onwards
      */
     protected static MessageDigest md5Helper;
 
 
     /**
-     * List of client nonce values currently being tracked
+     * List of server nonce values currently being tracked
      */
-    protected Map<String,NonceInfo> cnonces;
+    protected Map<String,NonceInfo> nonces;
 
 
     /**
-     * Maximum number of client nonces to keep in the cache. If not specified,
+     * Maximum number of server nonces to keep in the cache. If not specified,
      * the default value of 1000 is used.
      */
-    protected int cnonceCacheSize = 1000;
+    protected int nonceCacheSize = 1000;
 
 
     /**
@@ -142,27 +129,16 @@
 
     // ------------------------------------------------------------- Properties
 
-    /**
-     * Return descriptive information about this Valve implementation.
-     */
-    @Override
-    public String getInfo() {
-
-        return (info);
-
+    public int getNonceCacheSize() {
+        return nonceCacheSize;
     }
 
 
-    public int getCnonceCacheSize() {
-        return cnonceCacheSize;
+    public void setNonceCacheSize(int nonceCacheSize) {
+        this.nonceCacheSize = nonceCacheSize;
     }
 
 
-    public void setCnonceCacheSize(int cnonceCacheSize) {
-        this.cnonceCacheSize = cnonceCacheSize;
-    }
-
-
     public String getKey() {
         return key;
     }
@@ -213,8 +189,6 @@
      *
      * @param request Request we are processing
      * @param response Response we are creating
-     * @param config    Login configuration describing how authentication
-     *              should be performed
      *
      * @exception IOException if an input/output error occurs
      */
@@ -222,19 +196,18 @@
     public boolean authenticate(Request request,
                                 Response response,
                                 LoginConfig config)
-        throws IOException {
+            throws IOException {
 
         // Have we already authenticated someone?
         Principal principal = request.getUserPrincipal();
         //String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
         if (principal != null) {
-            if (log.isDebugEnabled())
-                log.debug("Already authenticated '" + principal.getName() + "'");
             // Associate the session with any existing SSO session in order
             // to get coordinated session invalidation at logout
             String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
-            if (ssoId != null)
+            if (ssoId != null) {
                 associate(ssoId, request.getSessionInternal(true));
+            }
             return (true);
         }
 
@@ -266,19 +239,20 @@
         // Validate any credentials already included with this request
         String authorization = request.getHeader("authorization");
         DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(),
-                getKey(), cnonces, isValidateUri());
+                getKey(), nonces, isValidateUri());
         if (authorization != null) {
-            if (digestInfo.validate(request, authorization, config)) {
-                principal = digestInfo.authenticate(context.getRealm());
+            if (digestInfo.parse(request, authorization)) {
+                if (digestInfo.validate(request, config)) {
+                    principal = digestInfo.authenticate(context.getRealm());
+                }
+
+                if (principal != null && !digestInfo.isNonceStale()) {
+                    register(request, response, principal,
+                            HttpServletRequest.DIGEST_AUTH,
+                            digestInfo.getUsername(), null);
+                    return true;
+                }
             }
-            
-            if (principal != null) {
-                String username = parseUsername(authorization);
-                register(request, response, principal,
-                         Constants.DIGEST_METHOD,
-                         username, null);
-                return (true);
-            }
         }
 
         // Send an "unauthorized" response and an appropriate challenge
@@ -288,11 +262,9 @@
         String nonce = generateNonce(request);
 
         setAuthenticateHeader(request, response, config, nonce,
-                digestInfo.isNonceStale());
+                principal != null && digestInfo.isNonceStale());
         response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-        //      hres.flushBuffer();
-        return (false);
-
+        return false;
     }
 
 
@@ -300,42 +272,6 @@
 
 
     /**
-     * Parse the username from the specified authorization string.  If none
-     * can be identified, return <code>null</code>
-     *
-     * @param authorization Authorization string to be parsed
-     */
-    protected String parseUsername(String authorization) {
-
-        // Validate the authorization credentials format
-        if (authorization == null)
-            return (null);
-        if (!authorization.startsWith("Digest "))
-            return (null);
-        authorization = authorization.substring(7).trim();
-
-        StringTokenizer commaTokenizer =
-            new StringTokenizer(authorization, ",");
-
-        while (commaTokenizer.hasMoreTokens()) {
-            String currentToken = commaTokenizer.nextToken();
-            int equalSign = currentToken.indexOf('=');
-            if (equalSign < 0)
-                return null;
-            String currentTokenName =
-                currentToken.substring(0, equalSign).trim();
-            String currentTokenValue =
-                currentToken.substring(equalSign + 1).trim();
-            if ("username".equals(currentTokenName))
-                return (removeQuotes(currentTokenValue));
-        }
-
-        return (null);
-
-    }
-
-
-    /**
      * Removes the quotes on a string. RFC2617 states quotes are optional for
      * all parameters except realm.
      */
@@ -348,7 +284,7 @@
         } else if (quotedString.length() > 2) {
             return quotedString.substring(1, quotedString.length() - 1);
         } else {
-            return new String();
+            return "";
         }
     }
 
@@ -370,16 +306,19 @@
 
         long currentTime = System.currentTimeMillis();
 
-        
+
         String ipTimeKey =
             request.getRemoteAddr() + ":" + currentTime + ":" + getKey();
 
-        byte[] buffer;
-        synchronized (md5Helper) {
-            buffer = md5Helper.digest(ipTimeKey.getBytes());
+        byte[] buffer = ConcurrentMessageDigest.digestMD5(ipTimeKey.getBytes());
+        String nonce = currentTime + ":" + MD5Encoder.encode(buffer);
+
+        NonceInfo info = new NonceInfo(currentTime, 100);
+        synchronized (nonces) {
+            nonces.put(nonce, info);
         }
 
-        return currentTime + ":" + md5Encoder.encode(buffer);
+        return nonce;
     }
 
 
@@ -406,12 +345,10 @@
      *
      * @param request HTTP Servlet request
      * @param response HTTP Servlet response
-     * @param config    Login configuration describing how authentication
-     *              should be performed
      * @param nonce nonce token
      */
-    protected void setAuthenticateHeader(Request request,
-                                         Response response,
+    protected void setAuthenticateHeader(HttpServletRequest request,
+                                         HttpServletResponse response,
                                          LoginConfig config,
                                          String nonce,
                                          boolean isNonceStale) {
@@ -430,15 +367,15 @@
             authenticateHeader = "Digest realm=\"" + realmName + "\", " +
             "qop=\"" + QOP + "\", nonce=\"" + nonce + "\", " + "opaque=\"" +
             getOpaque() + "\"";
-       }
+        }
 
-        response.setHeader("WWW-Authenticate", authenticateHeader);
+        response.setHeader(AUTH_HEADER_NAME, authenticateHeader);
 
     }
 
 
     // ------------------------------------------------------- Lifecycle Methods
-    
+
     @Override
     public void start() throws LifecycleException {
         super.start();
@@ -447,14 +384,14 @@
         if (getKey() == null) {
             setKey(generateSessionId());
         }
-        
+
         // Generate the opaque string the same way
         if (getOpaque() == null) {
             setOpaque(generateSessionId());
         }
-        
-        cnonces = new LinkedHashMap<String, DigestAuthenticator.NonceInfo>() {
 
+        nonces = new LinkedHashMap<String, DigestAuthenticator.NonceInfo>() {
+
             private static final long serialVersionUID = 1L;
             private static final long LOG_SUPPRESS_TIME = 5 * 60 * 1000;
 
@@ -465,7 +402,7 @@
                     Map.Entry<String,NonceInfo> eldest) {
                 // This is called from a sync so keep it simple
                 long currentTime = System.currentTimeMillis();
-                if (size() > getCnonceCacheSize()) {
+                if (size() > getNonceCacheSize()) {
                     if (lastLog < currentTime &&
                             currentTime - eldest.getValue().getTimestamp() <
                             getNonceValidity()) {
@@ -480,13 +417,13 @@
             }
         };
     }
- 
+
     private static class DigestInfo {
 
-        private String opaque;
-        private long nonceValidity;
-        private String key;
-        private Map<String,NonceInfo> cnonces;
+        private final String opaque;
+        private final long nonceValidity;
+        private final String key;
+        private final Map<String,NonceInfo> nonces;
         private boolean validateUri = true;
 
         private String userName = null;
@@ -498,21 +435,27 @@
         private String cnonce = null;
         private String realmName = null;
         private String qop = null;
+        private String opaqueReceived = null;
 
         private boolean nonceStale = false;
 
 
         public DigestInfo(String opaque, long nonceValidity, String key,
-                Map<String,NonceInfo> cnonces, boolean validateUri) {
+                Map<String,NonceInfo> nonces, boolean validateUri) {
             this.opaque = opaque;
             this.nonceValidity = nonceValidity;
             this.key = key;
-            this.cnonces = cnonces;
+            this.nonces = nonces;
             this.validateUri = validateUri;
         }
 
-        public boolean validate(Request request, String authorization,
-                LoginConfig config) {
+
+        public String getUsername() {
+            return userName;
+        }
+
+
+        public boolean parse(Request request, String authorization) {
             // Validate the authorization credentials format
             if (authorization == null) {
                 return false;
@@ -526,12 +469,12 @@
             String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
 
             method = request.getMethod();
-            String opaque = null;
 
             for (int i = 0; i < tokens.length; i++) {
                 String currentToken = tokens[i];
-                if (currentToken.length() == 0)
+                if (currentToken.length() == 0) {
                     continue;
+                }
 
                 int equalSign = currentToken.indexOf('=');
                 if (equalSign < 0) {
@@ -541,26 +484,39 @@
                     currentToken.substring(0, equalSign).trim();
                 String currentTokenValue =
                     currentToken.substring(equalSign + 1).trim();
-                if ("username".equals(currentTokenName))
+                if ("username".equals(currentTokenName)) {
                     userName = removeQuotes(currentTokenValue);
-                if ("realm".equals(currentTokenName))
+                }
+                if ("realm".equals(currentTokenName)) {
                     realmName = removeQuotes(currentTokenValue, true);
-                if ("nonce".equals(currentTokenName))
+                }
+                if ("nonce".equals(currentTokenName)) {
                     nonce = removeQuotes(currentTokenValue);
-                if ("nc".equals(currentTokenName))
+                }
+                if ("nc".equals(currentTokenName)) {
                     nc = removeQuotes(currentTokenValue);
-                if ("cnonce".equals(currentTokenName))
+                }
+                if ("cnonce".equals(currentTokenName)) {
                     cnonce = removeQuotes(currentTokenValue);
-                if ("qop".equals(currentTokenName))
+                }
+                if ("qop".equals(currentTokenName)) {
                     qop = removeQuotes(currentTokenValue);
-                if ("uri".equals(currentTokenName))
+                }
+                if ("uri".equals(currentTokenName)) {
                     uri = removeQuotes(currentTokenValue);
-                if ("response".equals(currentTokenName))
+                }
+                if ("response".equals(currentTokenName)) {
                     response = removeQuotes(currentTokenValue);
-                if ("opaque".equals(currentTokenName))
-                    opaque = removeQuotes(currentTokenValue);
+                }
+                if ("opaque".equals(currentTokenName)) {
+                    opaqueReceived = removeQuotes(currentTokenValue);
+                }
             }
 
+            return true;
+        }
+
+        public boolean validate(Request request, LoginConfig config) {
             if ( (userName == null) || (realmName == null) || (nonce == null)
                  || (uri == null) || (response == null) ) {
                 return false;
@@ -576,7 +532,23 @@
                     uriQuery = request.getRequestURI() + "?" + query;
                 }
                 if (!uri.equals(uriQuery)) {
-                    return false;
+                    // Some clients (older Android) use an absolute URI for
+                    // DIGEST but a relative URI in the request line.
+                    // request. 2.3.5 < fixed Android version <= 4.0.3
+                    String host = request.getHeader("host");
+                    String scheme = request.getScheme();
+                    if (host != null && !uriQuery.startsWith(scheme)) {
+                        StringBuilder absolute = new StringBuilder();
+                        absolute.append(scheme);
+                        absolute.append("://");
+                        absolute.append(host);
+                        absolute.append(uriQuery);
+                        if (!uri.equals(absolute.toString())) {
+                            return false;
+                        }
+                    } else {
+                        return false;
+                    }
                 }
             }
 
@@ -588,9 +560,9 @@
             if (!lcRealm.equals(realmName)) {
                 return false;
             }
-            
+
             // Validate the opaque string
-            if (!this.opaque.equals(opaque)) {
+            if (!opaque.equals(opaqueReceived)) {
                 return false;
             }
 
@@ -609,15 +581,15 @@
             long currentTime = System.currentTimeMillis();
             if ((currentTime - nonceTime) > nonceValidity) {
                 nonceStale = true;
-                return false;
+                synchronized (nonces) {
+                    nonces.remove(nonce);
+                }
             }
             String serverIpTimeKey =
                 request.getRemoteAddr() + ":" + nonceTime + ":" + key;
-            byte[] buffer = null;
-            synchronized (md5Helper) {
-                buffer = md5Helper.digest(serverIpTimeKey.getBytes());
-            }
-            String md5ServerIpTimeKey = md5Encoder.encode(buffer);
+            byte[] buffer = ConcurrentMessageDigest.digestMD5(
+                    serverIpTimeKey.getBytes());
+            String md5ServerIpTimeKey = MD5Encoder.encode(buffer);
             if (!md5ServerIpTimeKey.equals(md5clientIpTimeKey)) {
                 return false;
             }
@@ -628,7 +600,7 @@
             }
 
             // Validate cnonce and nc
-            // Check if presence of nc and nonce is consistent with presence of qop
+            // Check if presence of nc and Cnonce is consistent with presence of qop
             if (qop == null) {
                 if (cnonce != null || nc != null) {
                     return false;
@@ -637,7 +609,9 @@
                 if (cnonce == null || nc == null) {
                     return false;
                 }
-                if (nc.length() != 8) {
+                // RFC 2617 says nc must be 8 digits long. Older Android clients
+                // use 6. 2.3.5 < fixed Android version <= 4.0.3
+                if (nc.length() < 6 || nc.length() > 8) {
                     return false;
                 }
                 long count;
@@ -647,21 +621,18 @@
                     return false;
                 }
                 NonceInfo info;
-                synchronized (cnonces) {
-                    info = cnonces.get(cnonce);
+                synchronized (nonces) {
+                    info = nonces.get(nonce);
                 }
                 if (info == null) {
-                    info = new NonceInfo();
+                    // Nonce is valid but not in cache. It must have dropped out
+                    // of the cache - force a re-authentication
+                    nonceStale = true;
                 } else {
-                    if (count <= info.getCount()) {
+                    if (!info.nonceCountValid(count)) {
                         return false;
                     }
                 }
-                info.setCount(count);
-                info.setTimestamp(currentTime);
-                synchronized (cnonces) {
-                    cnonces.put(cnonce, info);
-                }
             }
             return true;
         }
@@ -675,11 +646,9 @@
             // MD5(Method + ":" + uri)
             String a2 = method + ":" + uri;
 
-            byte[] buffer;
-            synchronized (md5Helper) {
-                buffer = md5Helper.digest(a2.getBytes());
-            }
-            String md5a2 = md5Encoder.encode(buffer);
+            byte[] buffer = ConcurrentMessageDigest.digestMD5(
+                    a2.getBytes());
+            String md5a2 = MD5Encoder.encode(buffer);
 
             return realm.authenticate(userName, response, nonce, nc, cnonce,
                     qop, realmName, md5a2);
@@ -688,21 +657,33 @@
     }
 
     private static class NonceInfo {
-        private volatile long count;
         private volatile long timestamp;
-        
-        public void setCount(long l) {
-            count = l;
+        private volatile boolean seen[];
+        private volatile int offset;
+        private volatile int count = 0;
+
+        public NonceInfo(long currentTime, int seenWindowSize) {
+            this.timestamp = currentTime;
+            seen = new boolean[seenWindowSize];
+            offset = seenWindowSize / 2;
         }
-        
-        public long getCount() {
-            return count;
+
+        public synchronized boolean nonceCountValid(long nonceCount) {
+            if ((count - offset) >= nonceCount ||
+                    (nonceCount > count - offset + seen.length)) {
+                return false;
+            }
+            int checkIndex = (int) ((nonceCount + offset) % seen.length);
+            if (seen[checkIndex]) {
+                return false;
+            } else {
+                seen[checkIndex] = true;
+                seen[count % seen.length] = false;
+                count++;
+                return true;
+            }
         }
-        
-        public void setTimestamp(long l) {
-            timestamp = l;
-        }
-        
+
         public long getTimestamp() {
             return timestamp;
         }
Index: jbossweb-2.1.13/java/org/apache/catalina/util/ConcurrentMessageDigest.java
===================================================================
--- jbossweb-2.1.13/java/org/apache/catalina/util/ConcurrentMessageDigest.java	(revision 0)
+++ jbossweb-2.1.13/java/org/apache/catalina/util/ConcurrentMessageDigest.java	(working copy)
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * A thread safe wrapper around {@link MessageDigest} that does not make use
+ * of ThreadLocal and - broadly - only creates enough MessageDigest objects
+ * to satisfy the concurrency requirements.
+ */
+public class ConcurrentMessageDigest {
+
+    private static final String MD5 = "MD5";
+
+    private static final Map<String,Queue<MessageDigest>> queues =
+            new HashMap<String,Queue<MessageDigest>>();
+
+
+    private ConcurrentMessageDigest() {
+        // Hide default constructor for this utility class
+    }
+
+    static {
+        try {
+            // Init commonly used algorithms
+            init(MD5);
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public static byte[] digestMD5(byte[] input) {
+        return digest(MD5, input);
+    }
+
+    public static byte[] digest(String algorithm, byte[] input) {
+
+        Queue<MessageDigest> queue = queues.get(algorithm);
+        if (queue == null) {
+            throw new IllegalStateException("Must call init() first");
+        }
+
+        MessageDigest md = queue.poll();
+        if (md == null) {
+            try {
+                md = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException e) {
+                // Ignore. Impossible if init() has been successfully called
+                // first.
+                throw new IllegalStateException("Must call init() first");
+            }
+        }
+
+        byte[] result = md.digest(input);
+
+        queue.add(md);
+
+        return result;
+    }
+
+
+    /**
+     * Ensures that {@link #digest(String, byte[])} and
+     * {@link #digestAsHex(String, byte[])} will support the specified
+     * algorithm. This method <b>must</b> be called and return successfully
+     * before using {@link #digest(String, byte[])} or
+     * {@link #digestAsHex(String, byte[])}.
+     *
+     * @param algorithm The message digest algorithm to be supported
+     *
+     * @throws NoSuchAlgorithmException If the algorithm is not supported by the
+     *                                  JVM
+     */
+    public static void init(String algorithm) throws NoSuchAlgorithmException {
+        synchronized (queues) {
+            if (!queues.containsKey(algorithm)) {
+                MessageDigest md = MessageDigest.getInstance(algorithm);
+                Queue<MessageDigest> queue = new ConcurrentLinkedQueue<MessageDigest>();
+                queue.add(md);
+                queues.put(algorithm, queue);
+            }
+        }
+    }
+}
Index: jbossweb-2.1.13/java/org/apache/catalina/util/MD5Encoder.java
===================================================================
--- jbossweb-2.1.13/java/org/apache/catalina/util/MD5Encoder.java	(revision 2169)
+++ jbossweb-2.1.13/java/org/apache/catalina/util/MD5Encoder.java	(working copy)
@@ -50,7 +50,7 @@
      * @param binaryData Array containing the digest
      * @return Encoded MD5, or null if encoding failed
      */
-    public String encode( byte[] binaryData ) {
+    public static String encode( byte[] binaryData ) {
 
         if (binaryData.length != 16)
             return null;
Index: jbossweb-2.1.13/webapps/docs/changelog.xml
===================================================================
--- jbossweb-2.1.13/webapps/docs/changelog.xml	(revision 2169)
+++ jbossweb-2.1.13/webapps/docs/changelog.xml	(working copy)
@@ -57,6 +57,12 @@
       <fix>
         <bug>46982</bug>: Correct reporting of DST offset in access logs. (markt)
       </fix>
+      <fix>
+         Various improvements to the DIGEST authenticator including
+         <bug>52954</bug>, the disabling caching of an authenticated user in the
+         session by default, tracking server rather than client nonces and better
+         handling of stale nonce values. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">

