diff --git a/composer.json b/composer.json
index f55f9dd38f326d0ce37231cd86c4b8fb3bd2061d..ed30be3b5ef7371ca1004f87c45c0307f255c12c 100644
--- a/composer.json
+++ b/composer.json
@@ -38,6 +38,7 @@
         "ext-mbstring": "*",
         "gettext/gettext": "^4.6",
         "jaimeperez/twig-configurable-i18n": "^2.0",
+        "phpmailer/phpmailer": "^6.0",
         "robrichards/xmlseclibs": "^3.0",
         "simplesamlphp/saml2": "^3.3",
         "simplesamlphp/simplesamlphp-module-cdc": "^1.0",
diff --git a/composer.lock b/composer.lock
index 233ced2b2f39182d21e470e7e8d107351a0baa24..07f1831d7dc071d4ac9a6f64892449d03505019a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "225bbfa4f3bdc5cac6a7943c5a705be5",
+    "content-hash": "b8847137c81850b370dc6f05691d31db",
     "packages": [
         {
             "name": "gettext/gettext",
@@ -222,6 +222,72 @@
             ],
             "time": "2019-01-03T20:59:08+00:00"
         },
+        {
+            "name": "phpmailer/phpmailer",
+            "version": "v6.0.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPMailer/PHPMailer.git",
+                "reference": "0c41a36d4508d470e376498c1c0c527aa36a2d59"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/0c41a36d4508d470e376498c1c0c527aa36a2d59",
+                "reference": "0c41a36d4508d470e376498c1c0c527aa36a2d59",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-filter": "*",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "doctrine/annotations": "1.2.*",
+                "friendsofphp/php-cs-fixer": "^2.2",
+                "phpdocumentor/phpdocumentor": "2.*",
+                "phpunit/phpunit": "^4.8 || ^5.7",
+                "zendframework/zend-eventmanager": "3.0.*",
+                "zendframework/zend-i18n": "2.7.3",
+                "zendframework/zend-serializer": "2.7.*"
+            },
+            "suggest": {
+                "ext-mbstring": "Needed to send email in multibyte encoding charset",
+                "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
+                "league/oauth2-google": "Needed for Google XOAUTH2 authentication",
+                "psr/log": "For optional PSR-3 debug logging",
+                "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
+                "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PHPMailer\\PHPMailer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1"
+            ],
+            "authors": [
+                {
+                    "name": "Jim Jagielski",
+                    "email": "jimjag@gmail.com"
+                },
+                {
+                    "name": "Marcus Bointon",
+                    "email": "phpmailer@synchromedia.co.uk"
+                },
+                {
+                    "name": "Andy Prevost",
+                    "email": "codeworxtech@users.sourceforge.net"
+                },
+                {
+                    "name": "Brent R. Matzelle"
+                }
+            ],
+            "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
+            "time": "2019-02-01T15:04:28+00:00"
+        },
         {
             "name": "psr/container",
             "version": "1.0.0",
diff --git a/lib/SimpleSAML/Utils/EMail.php b/lib/SimpleSAML/Utils/EMail.php
new file mode 100644
index 0000000000000000000000000000000000000000..8644b0af967d38a805bd9af1c33d456b9dcb2ec0
--- /dev/null
+++ b/lib/SimpleSAML/Utils/EMail.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace SimpleSAML\Utils;
+
+use PHPMailer\PHPMailer\PHPMailer;
+use PHPMailer\PHPMailer\Exception;
+
+use SimpleSAML\Configuration;
+use SimpleSAML\Logger;
+use SimpleSAML\XHTML\Template;
+
+/**
+ * E-mailer class that can generate a formatted e-mail from array
+ * input data.
+ *
+ * @author Jørn Åne de Jong, Uninett AS <jorn.dejong@uninett.no>
+ * @package SimpleSAMLphp
+ */
+
+class EMail
+{
+
+    /**
+     * Get the default e-mail address from the configuration
+     * This is used both as source and destination address
+     * unless something else is provided at the constructor.
+     *
+     * It will refuse to return the SimpleSAMLphp default address,
+     * which is na@example.org.
+     *
+     * @return string Default mail address
+     */
+    public static function getDefaultMailAddress()
+    {
+        $config = Configuration::getInstance();
+        $address = $config->getString('technicalcontact_email', 'na@example.org');
+        if ('na@example.org' === $address) {
+            throw new \Exception('technicalcontact_email must be changed from the default value');
+        }
+        return $address;
+    }
+
+    /** @var array Dictionary with multivalues */
+    private $data;
+    /** @var string Introduction text */
+    private $text;
+    /** @var PHPMailer The mailer instance */
+    private $mail;
+
+    /**
+     * Constructor
+     *
+     * If $from or $to is not provided, the <code>technicalcontact_email</code>
+     * from the configuration is used.
+     *
+     * @param string $subject The subject of the e-mail
+     * @param string $from The from-address (both envelope and header)
+     * @param string $to The recipient
+     *
+     * @throws PHPMailer\PHPMailer\Exception
+     */
+    public function __construct($subject, $from = null, $to = null)
+    {
+        $this->mail = new PHPMailer(true);
+        $this->mail->Subject = $subject;
+        $this->mail->setFrom($from ?: static::getDefaultMailAddress());
+        $this->mail->addAddress($to ?: static::getDefaultMailAddress());
+    }
+
+    /**
+     * Set the data that should be embedded in the e-mail body
+     *
+     * @param array $data The data that should be embedded in the e-mail body
+     */
+    public function setData(array $data)
+    {
+        /*
+         * Convert every non-array value to an array with the original
+         * as its only element. This guarantees that every value of $data
+         * can be iterated over.
+         */
+        $this->data = array_map(function($v){return is_array($v) ? $v : [$v];}, $data);
+    }
+
+    /**
+     * Set an introduction text for the e-mail
+     *
+     * @param string $text Introduction text
+     */
+    public function setText($text)
+    {
+        $this->text = $text;
+    }
+
+    /**
+     * Add a Reply-To address to the mail
+     *
+     * @param string $address Reply-To e-mail address
+     */
+    public function addReplyTo($address)
+    {
+        $this->mail->addReplyTo($address);
+    }
+
+    /**
+     * Send the mail
+     *
+     * @param bool $plainTextOnly Do not send HTML payload
+     *
+     * @throws \PHPMailer\PHPMailer\Exception
+     */
+    public function send($plainTextOnly = false)
+    {
+        if ($plainTextOnly) {
+            $this->mail->isHTML(false);
+            $this->mail->Body = $this->generateBody('mailtxt.twig');
+        } else {
+            $this->mail->isHTML(true);
+            $this->mail->Body = $this->generateBody('mailhtml.twig');
+            $this->mail->AltBody = $this->generateBody('mailtxt.twig');
+        }
+
+        $this->mail->send();
+    }
+
+    /**
+     * Generate the body of the e-mail
+     *
+     * @param string $template The name of the template to use
+     *
+     * @return string The body of the e-mail
+     */
+    public function generateBody($template)
+    {
+        $config = Configuration::loadFromArray([
+            'usenewui' => true,
+        ]);
+        $t = new Template($config, $template);
+        $twig = $t->getTwig();
+        if (is_bool($twig)) {
+            throw new \Exception('Even though we explicitly configure that we want Twig, the Template class does not give us Twig. This is a bug.');
+        }
+        $result = $twig->render($template, [
+                'subject' => $this->mail->Subject,
+                'text' => $this->text,
+                'data' => $this->data
+            ]);
+        return $result;
+    }
+
+}
diff --git a/lib/SimpleSAML/XHTML/EMail.php b/lib/SimpleSAML/XHTML/EMail.php
deleted file mode 100644
index ac52e411147e948f09f421c67923e1ab1482b49a..0000000000000000000000000000000000000000
--- a/lib/SimpleSAML/XHTML/EMail.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace SimpleSAML\XHTML;
-
-/**
- * A minimalistic Emailer class. Creates and sends HTML emails.
- *
- * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package SimpleSAMLphp
- */
-
-class EMail
-{
-    /** @var string|null */
-    private $to = null;
-
-    /** @var string|null */
-    private $cc = null;
-
-    /** @var string|null */
-    private $body = null;
-
-    /** @var string|null */
-    private $from = null;
-
-    /** @var string|null */
-    private $replyto = null;
-
-    /** @var string|null */
-    private $subject = null;
-
-    /** @var array */
-    private $headers = [];
-
-
-    /**
-     * Constructor
-     *
-     * @param string $to
-     * @param string $subject
-     * @param string|null $from
-     * @param string|null $cc
-     * @param string|null $replyto
-     */
-    public function __construct($to, $subject, $from = null, $cc = null, $replyto = null)
-    {
-        $this->to = $to;
-        $this->cc = $cc;
-        $this->from = $from;
-        $this->replyto = $replyto;
-        $this->subject = $subject;
-    }
-
-    /**
-     * @param string $body
-     * @return void
-     */
-    public function setBody($body)
-    {
-        $this->body = $body;
-    }
-
-
-    /**
-     * @param string $body
-     * @return string
-     */
-    private function getHTML($body)
-    {
-        $config = \SimpleSAML\Configuration::getInstance();
-        $t = new \SimpleSAML\XHTML\Template($config, 'errorreport_mail.twig');
-        $twig = $t->getTwig();
-        return $twig->render('errorreport_mail.twig', ['body' => $body]);
-    }
-
-
-    /**
-     * @return void
-     */
-    public function send()
-    {
-        if ($this->to === null) {
-            throw new \Exception('EMail field [to] is required and not set.');
-        } elseif ($this->subject === null) {
-            throw new \Exception('EMail field [subject] is required and not set.');
-        } elseif ($this->body === null) {
-            throw new \Exception('EMail field [body] is required and not set.');
-        }
-
-        $random_hash = bin2hex(openssl_random_pseudo_bytes(16));
-
-        if (isset($this->from)) {
-            $this->headers[] = 'From: '.$this->from;
-        }
-        if (isset($this->replyto)) {
-            $this->headers[] = 'Reply-To: '.$this->replyto;
-        }
-
-        $this->headers[] = 'Content-Type: multipart/alternative; boundary="simplesamlphp-'.$random_hash.'"';
-
-        $message = '
---simplesamlphp-'.$random_hash.'
-Content-Type: text/plain; charset="utf-8" 
-Content-Transfer-Encoding: 8bit
-
-'.strip_tags(html_entity_decode($this->body)).'
-
---simplesamlphp-'.$random_hash.'
-Content-Type: text/html; charset="utf-8" 
-Content-Transfer-Encoding: 8bit
-
-'.$this->getHTML($this->body).'
-
---simplesamlphp-'.$random_hash.'--
-';
-        $headers = implode("\n", $this->headers);
-
-        $mail_sent = @mail($this->to, $this->subject, $message, $headers);
-        \SimpleSAML\Logger::debug('Email: Sending e-mail to ['.$this->to.'] : '.($mail_sent ? 'OK' : 'Failed'));
-        if (!$mail_sent) {
-            throw new \Exception('Error when sending e-mail');
-        }
-    }
-}
diff --git a/modules/cron/www/cron.php b/modules/cron/www/cron.php
index fb930592812ce20bf6370618cdb708677d63cb81..5dd7439d0a701953913229d8c10f0c64ba82a7b7 100644
--- a/modules/cron/www/cron.php
+++ b/modules/cron/www/cron.php
@@ -23,20 +23,9 @@ $croninfo = $cron->runTag($_REQUEST['tag']);
 $summary = $croninfo['summary'];
 
 if ($cronconfig->getValue('sendemail', true) && count($summary) > 0) {
-    $message = '<h1>Cron report</h1><p>Cron ran at '.$time.'</p>'.
-        '<p>URL: <code>'.$url.'</code></p>'.
-        '<p>Tag: '.$croninfo['tag']."</p>\n\n".
-        '<ul><li>'.join('</li><li>', $summary).'</li></ul>';
-
-    $toaddress = $config->getString('technicalcontact_email', 'na@example.org');
-    if ($toaddress == 'na@example.org') {
-        \SimpleSAML\Logger::error('Cron - Could not send email. [technicalcontact_email] not set in config.');
-    } else {
-        // Use $toaddress for both TO and FROM
-        $email = new \SimpleSAML\XHTML\EMail($toaddress, 'SimpleSAMLphp cron report', $toaddress);
-        $email->setBody($message);
-        $email->send();
-    }
+    $mail = new \SimpleSAML\Utils\EMail('SimpleSAMLphp cron report');
+    $mail->setData(['url' => $url, 'tag' => $croninfo['tag'], 'summary' => $croninfo['summary']]);
+    $mail->send();
 }
 
 if (isset($_REQUEST['output']) && $_REQUEST['output'] == "xhtml") {
diff --git a/templates/mailhtml.twig b/templates/mailhtml.twig
new file mode 100644
index 0000000000000000000000000000000000000000..1f01ce16dad1b887da9b3425d7927a56e8e19c3c
--- /dev/null
+++ b/templates/mailhtml.twig
@@ -0,0 +1,25 @@
+<style type="text/css">
+body {
+    font-family: sans-serif;
+}
+ul {
+    list-style: none;
+    padding-left: 1em;
+}
+p {
+    white-space: pre-line;
+}
+</style>
+<h1>{{ subject }}</h1>
+
+<p>{{ text }}</p>
+
+{% for name, values in data %}
+<h2>{{ name }}</h2>
+<ul>
+{% for value in values %}
+    <li><pre>{{ value }}</pre></li>
+{% endfor %}
+</ul>
+
+{% endfor %}
diff --git a/templates/mailtxt.twig b/templates/mailtxt.twig
new file mode 100644
index 0000000000000000000000000000000000000000..b560656da3d73d1d219d932fe95212be90a278c3
--- /dev/null
+++ b/templates/mailtxt.twig
@@ -0,0 +1,13 @@
+{% autoescape false %}
+# {{ subject }}
+
+{{ text }}
+
+{% for name, values in data %}
+## {{ name }}
+{% for value in values %}
+- {{ value }}
+{% endfor %}
+
+{% endfor %}
+{% endautoescape %}
diff --git a/tests/lib/SimpleSAML/Utils/EMailTestCase.php b/tests/lib/SimpleSAML/Utils/EMailTestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..0117d2e1ab26d3629869860c0a5248da40e091f9
--- /dev/null
+++ b/tests/lib/SimpleSAML/Utils/EMailTestCase.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SimpleSAML\Test\Utils;
+
+use SimpleSAML\Test\Utils\TestCase;
+
+use SimpleSAML\Configuration;
+use SimpleSAML\Utils\EMail;
+
+/**
+ * A base SSP test case that tests some simple e-mail related calls
+ */
+class EMailTestCase extends ClearStateTestCase
+{
+    public function setUp()
+    {
+        parent::setUp();
+
+        // Override configuration
+        Configuration::loadFromArray([
+            'technicalcontact_email' => 'na@example.org',
+        ], '[ARRAY]', 'simplesaml');
+    }
+
+    /**
+     * Test that an exception is thrown if using default configuration,
+     * and no custom from address is specified.
+     * @expectedException Exception
+     */
+    public function testMailFromDefaultConfigurationException()
+    {
+        new EMail('test', null, 'phpunit@simplesamlphp.org');
+    }
+
+    /**
+     * Test that an exception is thrown if using an invalid "From"-address
+     * @expectedException Exception
+     */
+    public function testInvalidFromAddressException()
+    {
+        new EMail('test', "phpunit@simplesamlphp.org\nLorem Ipsum", 'phpunit@simplesamlphp.org');
+    }
+
+    /**
+     * Test that an exception is thrown if using an invalid "To"-address
+     * @expectedException Exception
+     */
+    public function testInvalidToAddressException()
+    {
+        new EMail('test', 'phpunit@simplesamlphp.org', "phpunit@simplesamlphp.org\nLorem Ipsum");
+    }
+
+    /**
+     * Test that the data given is visible in the resulting mail
+     * @dataProvider mailTemplates
+     */
+    public function testMailContents($template)
+    {
+        $mail = new EMail('subject-subject-subject-subject-subject-subject-subject', 'phpunit@simplesamlphp.org', 'phpunit@simplesamlphp.org');
+        $mail->setText('text-text-text-text-text-text-text');
+        $mail->setData(['key-key-key-key-key-key-key' => 'value-value-value-value-value-value-value']);
+        $result = $mail->generateBody($template);
+        $this->assertRegexp('/(subject-){6}/', $result);
+        $this->assertRegexp('/(text-){6}/', $result);
+        $this->assertRegexp('/(key-){6}/', $result);
+        $this->assertRegexp('/(value-){6}/', $result);
+    }
+    /** All templates that should be tested in #testMailContents($template) */
+    public static function mailTemplates()
+    {
+        return [['mailtxt.twig'], ['mailhtml.twig']];
+    }
+
+}
diff --git a/www/errorreport.php b/www/errorreport.php
index 0193e4767b589133c67e27bbef489fc0f9d072b9..cd4be1d79aac450dc6fc9f5508efe0aec846845f 100644
--- a/www/errorreport.php
+++ b/www/errorreport.php
@@ -13,9 +13,9 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
     exit;
 }
 
-$reportId = (string) $_REQUEST['reportId'];
-$email = (string) $_REQUEST['email'];
-$text = htmlspecialchars((string) $_REQUEST['text']);
+$reportId = $_REQUEST['reportId'];
+$email = $_REQUEST['email'];
+$text = $_REQUEST['text'];
 
 $data = null;
 try {
@@ -29,10 +29,8 @@ if ($data === null) {
     $data = [
         'exceptionMsg'   => 'not set',
         'exceptionTrace' => 'not set',
-        'reportId'       => $reportId,
         'trackId'        => 'not set',
         'url'            => 'not set',
-        'version'        => $config->getVersion(),
         'referer'        => 'not set',
     ];
 
@@ -41,90 +39,18 @@ if ($data === null) {
     }
 }
 
-foreach ($data as $k => $v) {
-    $data[$k] = htmlspecialchars($v);
-}
-
-// build the email message
-$message = <<<MESSAGE
-<h1>SimpleSAMLphp Error Report</h1>
-
-<p>Message from user:</p>
-<div class="box" style="background: yellow; color: #888; border: 1px solid #999900; padding: .4em; margin: .5em">
-    %s
-</div>
-
-<p>Exception: <strong>%s</strong></p>
-<pre>%s</pre>
-
-<p>URL:</p>
-<pre><a href="%s">%s</a></pre>
-
-<p>Host:</p>
-<pre>%s</pre>
-
-<p>Directory:</p>
-<pre>%s</pre>
-
-<p>Track ID:</p>
-<pre>%s</pre>
-
-<p>Version: <code>%s</code></p>
-
-<p>Report ID: <code>%s</code></p>
-
-<p>Referer: <code>%s</code></p>
-
-<hr />
-<div class="footer">
-    This message was sent using SimpleSAMLphp. Visit the <a href="http://simplesamlphp.org/">SimpleSAMLphp homepage</a>.
-</div>
-MESSAGE;
-$message = sprintf(
-    $message,
-    $text,
-    $data['exceptionMsg'],
-    $data['exceptionTrace'],
-    $data['url'],
-    $data['url'],
-    htmlspecialchars(php_uname('n')),
-    dirname(dirname(__FILE__)),
-    $data['trackId'],
-    $data['version'],
-    $data['reportId'],
-    $data['referer']
-);
-
-// add the email address of the submitter as the Reply-To address
-$email = trim($email);
-
-// check that it looks like a valid email address
-if (!preg_match('/\s/', $email) && strpos($email, '@') !== false) {
-    $replyto = $email;
-} else {
-    $replyto = null;
-}
-
-$from = $config->getString('sendmail_from', null);
-if ($from === null || $from === '') {
-    $from = ini_get('sendmail_from');
-    if ($from === '' || $from === false) {
-        $from = 'no-reply@example.org';
-    }
-}
-
-// If no sender email was configured at least set some relevant from address
-if ($from === 'no-reply@example.org' && $replyto !== null) {
-    $from = $replyto;
-}
+$data['reportId'] = $reportId;
+$data['version'] = $config->getVersion();
+$data['hostname'] = php_uname('n');
+$data['directory'] = dirname(dirname(__FILE__));
 
-// send the email
-$toAddress = $config->getString('technicalcontact_email', 'na@example.org');
-if ($config->getBoolean('errorreporting', true) && $toAddress !== 'na@example.org') {
-    $email = new \SimpleSAML\XHTML\EMail($toAddress, 'SimpleSAMLphp error report', $from);
-    $email->setBody($message);
-    $email->send();
-    SimpleSAML\Logger::error('Report with id '.$reportId.' sent to <'.$toAddress.'>.');
+if ($config->getBoolean('errorreporting', true)) {
+    $mail = new SimpleSAML\Utils\EMail('SimpleSAMLphp error report from '.$email);
+    $mail->setData($data);
+    $mail->addReplyTo($email);
+    $mail->setText($text);
+    $mail->send();
+    SimpleSAML\Logger::error('Report with id '.$reportId.' sent');
 }
 
 // redirect the user back to this page to clear the POST request