Index: subversion/include/svn_xml.h
===================================================================
--- subversion/include/svn_xml.h	(revision 12853)
+++ subversion/include/svn_xml.h	(working copy)
@@ -117,7 +117,14 @@
                                   const char *string,
                                   apr_pool_t *pool);
 
+/** Return @c string on finding no ASCII control characters. Otherwise
+ *  return a copy created with @c pool, with control characters
+ *  replaced by "0x\d+" sequences.
+ */
+const char* svn_xml_fuzzy_escape (const char *string,
+                                  apr_pool_t *pool);
 
+
 /*---------------------------------------------------------------*/
 
 /* Generalized Subversion XML Parsing */
Index: subversion/libsvn_subr/xml.c
===================================================================
--- subversion/libsvn_subr/xml.c	(revision 12853)
+++ subversion/libsvn_subr/xml.c	(working copy)
@@ -25,6 +25,7 @@
 #include "svn_pools.h"
 #include "svn_xml.h"
 #include "svn_error.h"
+#include "svn_ctype.h"
 
 #ifdef SVN_HAVE_OLD_EXPAT
 #include "xmlparse.h"
@@ -265,7 +266,47 @@
   xml_escape_attr (outstr, string, (apr_size_t) strlen (string), pool);
 }
 
+/* Escape any ASCII control chars. */
+const char*
+svn_xml_fuzzy_escape (const char *string, apr_pool_t *pool)
+{
+  const char *end = string + strlen (string);
+  const char *p = string, *q;
+  svn_stringbuf_t *outstr;
 
+  for (q = p; q < end; q++)
+    if (svn_ctype_iscntrl(*q))
+      break;
+
+  /* return original string if no control characters found*/
+  if (q == end)
+    return string;
+
+  outstr = svn_stringbuf_create ("", pool);
+  while (1)
+    {
+      q = p;
+      /* Traverse till either control character or eos */
+      while (q < end && !svn_ctype_iscntrl(*q))
+        q++;
+
+      /* copy chunk before marker */
+      svn_stringbuf_appendbytes (outstr, p, q - p);
+
+      if (q == end)
+        break;
+
+      /* Append the control character in 0x%x format. */
+      char escaped_char[7];
+      sprintf (escaped_char, "0x%x", *q);
+      svn_stringbuf_appendcstr (outstr, escaped_char);
+
+      p = q + 1;
+    }
+
+  return outstr->data;
+}
+
 
 /*** Map from the Expat callback types to the SVN XML types. ***/
 
Index: subversion/mod_dav_svn/log.c
===================================================================
--- subversion/mod_dav_svn/log.c	(revision 12853)
+++ subversion/mod_dav_svn/log.c	(working copy)
@@ -98,9 +98,10 @@
   if (msg)
     SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
                                "<D:comment>%s</D:comment>" DEBUG_CR,
-                               apr_xml_quote_string(pool, msg, 0)) );
+                               svn_xml_fuzzy_escape (
+                                 apr_xml_quote_string (pool, msg, 0),
+                                 pool)) );
 
-
   if (changed_paths)
     {
       apr_hash_index_t *hi;
Index: subversion/tests/clients/cmdline/log_tests.py
===================================================================
--- subversion/tests/clients/cmdline/log_tests.py	(revision 12853)
+++ subversion/tests/clients/cmdline/log_tests.py	(working copy)
@@ -516,6 +516,70 @@
   svntest.actions.run_and_verify_svn (None, [], SVNAnyOutput,
                                       'log', '-r', '2', mu2_URL)
   
+#----------------------------------------------------------------------
+def escape_control_chars(sbox):
+  "make sure mod_dav_svn escapes control chars"
+
+  dump_str = """SVN-fs-dump-format-version: 2
+
+UUID: ffcae364-69ee-0310-a980-ca5f10462af2
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2005-01-24T10:09:21.759592Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 37
+this msg contains a Ctrl-T:  <- here
+K 10
+svn:author
+V 7
+jrandom
+K 8
+svn:date
+V 27
+2005-01-24T10:09:22.012524Z
+PROPS-END
+"""
+
+  # Create virgin repos and working copy
+  svntest.main.safe_rmtree(sbox.repo_dir, 1)
+  svntest.main.create_repos(sbox.repo_dir)
+  svntest.main.set_repos_paths(sbox.repo_dir)
+
+  URL = svntest.main.current_repo_url
+
+  # load dumpfile with control character into repos to get
+  # a log with control char content
+  output, errput = \
+    svntest.main.run_command_stdin(
+    "%s load --quiet %s" % (svntest.main.svnadmin_binary, sbox.repo_dir),
+    None, 1, dump_str)
+
+  # run log
+  output, errput = svntest.actions.run_and_verify_svn ("", None, [], 'log', URL)
+
+  # verify output contains expected fuzzy escape sequence
+  match_re = "this msg contains a Ctrl-T.*"
+  for line in output:
+    if re.match (match_re, line):
+      break
+  else:
+    raise svntest.Failure ("Failed to find " + str(match_re) + " in " + str(output),
+                           "error: " + str(errput))
+
+
 ########################################################################
 # Run the tests
 
@@ -530,6 +594,7 @@
               log_with_path_args,
               url_missing_in_head,
               log_through_copyfrom_history,
+              escape_control_chars,
              ]
 
 if __name__ == '__main__':
