diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php
index ba0cf1d1cc523f3030017b5cbbc821aed8185933..d6d30a1b0476072e0d9464ff9dc8cfde00296db8 100644
--- a/include/ajax.kbase.php
+++ b/include/ajax.kbase.php
@@ -42,7 +42,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $resp['files'] = $canned->attachments->getSeparates();
 
                 if (!$cfg->isHtmlThreadEnabled()) {
-                    $resp['response'] = convert_html_to_text($resp['response'], 90);
+                    $resp['response'] = Format::html2text($resp['response'], 90);
                     $resp['files'] += $canned->attachments->getInlines();
                 }
 
@@ -54,7 +54,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $response =$ticket?$ticket->replaceVars($canned->getResponse()):$canned->getResponse();
 
                 if (!$cfg->isHtmlThreadEnabled())
-                    $response = convert_html_to_text($response, 90);
+                    $response = Format::html2text($response, 90);
         }
 
         return $response;
diff --git a/include/class.format.php b/include/class.format.php
index f3e78fc8b08884cbf559a2ccdb4fbf11372506f5..f86982a38b4973644ef2bbd3ac6866ae0eaa7ab2 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -128,8 +128,8 @@ class Format {
         return is_array($var)?array_map(array('Format','strip_slashes'),$var):stripslashes($var);
     }
 
-    function wrap($text,$len=75) {
-        return wordwrap($text,$len,"\n",true);
+    function wrap($text, $len=75) {
+        return $len ? wordwrap($text, $len, "\n", true) : $text;
     }
 
     function html($html, $config=array('balance'=>1)) {
@@ -137,6 +137,30 @@ class Format {
         return htmLawed($html, $config);
     }
 
+    function html2text($html, $width=74, $tidy=true) {
+
+
+        # Tidy html: decode, balance, sanitize tags
+        if($tidy)
+            $html = Format::html(Format::htmldecode($html), array('balance' => 1));
+
+        # See if advanced html2text is available (requires xml extension)
+        if (function_exists('convert_html_to_text')
+                && extension_loaded('xml'))
+            return convert_html_to_text($html, $width);
+
+        # Try simple html2text  - insert line breaks after new line tags.
+        $html = preg_replace(
+                array(':<br ?/?\>:i', ':(</div>)\s*:i', ':(</p>)\s*:i')
+                array("\n", "$1\n", "$1\n\n"),
+                $html);
+
+        # Strip tags, decode html chars and wrap resulting text.
+        return Format::wrap(
+                Format::htmldecode( Format::striptags($html, false)),
+                $width);
+    }
+
     function safe_html($html) {
         // Remove HEAD and STYLE sections
         $html = preg_replace(':<(head|style).+</\1>:is','', $html);
diff --git a/include/class.mailer.php b/include/class.mailer.php
index e13eff86e450589c82253468e14864d0b97d4c1b..61dda2c001b1895ed1ad9643e55c0c5bde4db1e4 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -143,8 +143,8 @@ class Mailer {
         if (!(isset($options['text']) && $options['text'])
                 && preg_match('/^\s*</', $message)) {
             // Make sure nothing unsafe has creeped into the message
-            $message = Format::safe_html($message);
-            $mime->setTXTBody(convert_html_to_text($message));
+            $message = Format::safe_html($message); //XXX??
+            $mime->setTXTBody(Format::html2text($message), 90, false);
         }
         else {
             $mime->setTXTBody($message);
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index d270f01408e249c4615cb0dc75beac7278f094d0..f71c002517911d3fc3769086130eba35f8f622eb 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -441,7 +441,7 @@ class MailFetcher {
                 $body = Format::htmlchars($body);
             }
             elseif ($body=$this->getPart($mid, 'text/html', $this->charset)) {
-                $body = convert_html_to_text(Format::safe_html($body), 100);
+                $body = Format::html2text(Format::safe_html($body), 100, false);
             }
             $body = trim($body)
                 ? sprintf('<div style="white-space:pre-wrap">%s</div>',
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 2438d4f4d2b4461067a18f30f39fc626e7508938..08e2f92b04c38ba01caeb64d05ea3f9610ba462f 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -191,7 +191,7 @@ class Mail_Parse {
                 $body = Format::htmlchars($body);
             }
             elseif ($body=$this->getPart($this->struct,'text/html')) {
-                $body = convert_html_to_text($body, 100);
+                $body = Format::html2text(Format::safe_html($body), 100, false);
             }
             $body = trim($body)
                 ? sprintf('<div style="white-space:pre-wrap">%s</div>',
diff --git a/include/html2text.php b/include/html2text.php
index 72ca01f217471480e9c15358a893d9e33ba3e181..713b449274a062328f64d45f52da8b91412002c0 100644
--- a/include/html2text.php
+++ b/include/html2text.php
@@ -25,16 +25,8 @@
  * @return the HTML converted, as best as possible, to text
  */
 function convert_html_to_text($html, $width=74) {
-    $html = fix_newlines($html);
-
-    if (!extension_loaded('xml')) {
-        $html = preg_replace(
-           array(':<br ?/?>|</div>:i', ':</p>:i'),
-           array("\n", "\n\n"),
-           $html);
-        return Format::striptags($html);
-    }
 
+    $html = fix_newlines($html);
     $doc = new DOMDocument('1.0', 'utf-8');
     if (!@$doc->loadHTML($html))
         return $html;