Mail autoconfiguration for MS Outlook, Thunderbird and Apple devices

The latest downloads will be at my github page.

I am working as admin and have also some domains which have lots of users, who need to configure the POP3, SMTP and IMAP client configuration of their own systems.

Some of them use primarily the Webmail service that we offer, but some want to use MS Outlook, Thunderbird or Apple iPads / iPhones. To get the mail clients running - it is a nice feature, that some of them will automatically search for their correct setting ;)

This is the nginx configuration, which gets included by all sites we are running on the server. I am using the location = /exact/path.xml - to minify cpu usage, since the three location definitions are on all nginx virtual hosts. For some Microsoft clients we have to use the case insensitive matching: location ~* ^/autodiscover/autodiscover.xml
# MS Outlook
# sample1: https://mcmilk.de/autodiscover/autodiscover.xml
# sample2: https://mcmilk.de/Autodiscover/Autodiscover.xml
location ~* ^/autodiscover/autodiscover.xml {
    root /var/www/apps/autoconfig;
    try_files /autodiscover.php =404;
    include        fastcgi.conf;
    fastcgi_pass   unix:/run/php-fpm/php74-fpm.sock;
}

# Thunderbird
# sample: https://mcmilk.de/.well-known/autoconfig/mail/config-v1.1.xml
location = /.well-known/autoconfig/mail/config-v1.1.xml {
    root /var/www/apps/autoconfig;
    try_files /config-v1.1.php =404;
    include        fastcgi.conf;
    fastcgi_pass   unix:/run/php-fpm/php74-fpm.sock;
}

# Apple devices
# sample: https://mcmilk.de/apple/get-mobileconfig
location = /apple/get-mobileconfig {
    root /var/www/apps/autoconfig;
    try_files /apple.php =404;
    include        fastcgi.conf;
    fastcgi_pass   unix:/run/php-fpm/php74-fpm.sock;
}

# disable logging for Apple Touch Icons
location ~ /apple-touch-icon(|-\d+x\d+)(|-precomposed).png {
    log_not_found off;
    access_log off;
}

nginx-autodiscover.conf

Outlook searches here: https://example.org/autodiscover/autodiscover.xml

This PHP code will create the correct answers for Microsoft email clients.

<?php
  $LoginName = "";
  $raw = file_get_contents('php://input');
  $matches = array();
  preg_match('/<EMailAddress>(.*)<\/EMailAddress>/', $raw, $matches);
  if (isset($matches[1])) {
    $LoginName = "<LoginName>".$matches[1]."</LoginName>";
  }
  header('Content-Type: application/xml');
?>
<?xml version="1.0" encoding="utf-8" ?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
  <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
    <User>
      <DisplayName>Mail Server of SomeOne</DisplayName>
    </User>

    <Account>
      <AccountType>email</AccountType>
      <Action>settings</Action>

      <Protocol>
        <Type>IMAP</Type>
        <Server>imap.example.org</Server>
        <Port>993</Port>
        <SPA>off</SPA>
        <SSL>on</SSL>
        <AuthRequired>on</AuthRequired>
        <?php echo $LoginName; ?>
      </Protocol>

      <Protocol>
        <Type>POP3</Type>
        <Server>pop3.example.org</Server>
        <Port>995</Port>
        <SPA>off</SPA>
        <SSL>on</SSL>
        <AuthRequired>on</AuthRequired>
        <?php echo $LoginName; ?>
      </Protocol>

      <Protocol>
        <Type>SMTP</Type>
        <Server>smtp.example.org</Server>
        <Port>465</Port>
        <SPA>off</SPA>
        <SSL>on</SSL>
        <AuthRequired>on</AuthRequired>
        <?php echo $LoginName; ?>
      </Protocol>

    </Account>
  </Response>
</Autodiscover>

autodiscover.phps

Thunderbird searches here: https://example.org/.well-known/autoconfig/mail/config-v1.1.xml

This PHP code will create the correct answers for Thunderbird clients. The most important part is this: $_SERVER["SERVER_NAME"];

<?php header("Content-Type: application/xml"); ?>
<?xml version="1.0" encoding="UTF-8"?>

<clientConfig version="1.1">
  <emailProvider id="example.org">
    <domain><?php echo $_SERVER["SERVER_NAME"]; ?></domain>

    <displayName>Mail Server of SomeOne</displayName>
    <displayShortName>Mail Server of SomeOne</displayShortName>

    <incomingServer type="pop3">
      <hostname>pop3.example.org</hostname>
      <port>995</port>
      <socketType>SSL</socketType>
      <username>%EMAILADDRESS%</username>
      <authentication>password-cleartext</authentication>

      <leaveMessagesOnServer>false</leaveMessagesOnServer>
      <downloadOnBiff>true</downloadOnBiff>
      <daysToLeaveMessagesOnServer>5</daysToLeaveMessagesOnServer>
    </incomingServer>

    <incomingServer type="imap">
      <hostname>imap.example.org</hostname>
      <port>993</port>
      <socketType>SSL</socketType>
      <username>%EMAILADDRESS%</username>
      <authentication>password-cleartext</authentication>
    </incomingServer>

    <outgoingServer type="smtp">
      <hostname>smtp.example.org</hostname>
      <port>465</port>
      <socketType>SSL</socketType>
      <username>%EMAILADDRESS%</username>
      <authentication>password-cleartext</authentication>
    </outgoingServer>

    <documentation url="https://webmail.example.org/">
      <descr lang="de">Allgemeine Beschreibung der Einstellungen</descr>
      <descr lang="en">Generic settings page</descr>
    </documentation>

  </emailProvider>
</clientConfig>

config-v1.1.phps

Apple devices not try to auto configure itself, but you can provide some information with a configuration profile ;)

This HTML form will ask the user for username and email address.

<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="robots" content="noindex,nofollow">
  <meta name="author" content="Tino Reichardt">
  <meta name="google" content="notranslate">

  <title>Configuration of Apple devices</title>

  <!--  jquery + bootstrap -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

  <!-- bootstrap form validator -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
</head>
<body>

<div class="container-fluid">
<div class="row">
    <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
        <form data-toggle="validator" method="POST" action="" novalidate="true">
            <fieldset>
                <h2>Configuration of Apple devices</h2>
                <hr class="colorgraph">
                <div class="form-group has-feedback has-success">
                  <div class="input-group">
                    <span class="input-group-addon"><i class="fa fa-user fa-fw"></i></span>
                    <input type="text" name="name" required="" class="form-control input-lg" placeholder="Name">
                  </div>
                  <span class="glyphicon form-control-feedback glyphicon-ok"></span>
                  <div class="help-block with-errors"></div>
                </div>
                <div class="form-group has-feedback has-success">
                  <div class="input-group">
                    <span class="input-group-addon"><i class="fa fa-envelope fa-fw"></i></span>
                    <input type="email" name="email" required="" class="form-control input-lg" placeholder="E-Mail Adresse" data-error="Bitte E-Mail Adresse überprüfen">
                  </div>
                  <span class="glyphicon form-control-feedback glyphicon-ok"></span>
                  <div class="help-block with-errors"></div>
                </div>
                <div class="form-group">
                    <input type="submit" class="btn btn-lg btn-success btn-block" value="Konfigurationsprofil anfordern">
                </div>
                <div class="form-group">
                <a target= "_blank" href="https://support.apple.com/guide/profile-manager/distribute-profiles-manually-pmdbd71ebc9/mac">Profile Manager User Guide</a> @ Apple.
                </div>
            </fieldset>
        </form>
    </div>
</div>
</div>

</body>
</html>

apple.html


And finally, the following PHP code will generate a .mobilconfig profile for apple devices like iPhone or iPad:

<?php

if (!isset($_POST['name']) || !isset($_POST['email'])) {
  readfile("apple.html");
  exit;
}

function UUIDv4() {
  $data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16);
  $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
  $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
  return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

$UUID1 = UUIDv4();
$UUID2 = UUIDv4();

$AccountName = $_POST['name'];
$MailAddress = $_POST['email'];

// Profilname
$MailServer    = "mail.example.org";
$DisplayProfil = "Mail Server of SomeOne";
$DisplayName   = "Mail for $AccountName";
$Description   = "Mail: $MailAddress";
$Identifier    = "$MailAddress";
$Organization  = "/TR ;-)";

$data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
    <key>PayloadContent</key>
    <array>
	<dict>
	    <key>EmailAccountDescription</key>
	    <string>$Description</string>
	    <key>EmailAccountName</key>
	    <string>$AccountName</string>
	    <key>EmailAccountType</key>
	    <string>EmailTypePOP</string>
	    <key>EmailAddress</key>
	    <string>$MailAddress</string>
	    <key>IncomingMailServerAuthentication</key>
	    <string>EmailAuthPassword</string>
	    <key>IncomingMailServerHostName</key>
	    <string>$MailServer</string>
	    <key>IncomingMailServerPortNumber</key>
	    <integer>995</integer>
	    <key>IncomingMailServerUseSSL</key>
	    <true/>
	    <key>IncomingMailServerUsername</key>
	    <string>$MailAddress</string>
	    <key>IncomingPassword</key>
	    <string></string>
	    <key>OutgoingMailServerAuthentication</key>
	    <string>EmailAuthPassword</string>
	    <key>OutgoingMailServerHostName</key>
	    <string>$MailServer</string>
	    <key>OutgoingMailServerPortNumber</key>
	    <integer>465</integer>
	    <key>OutgoingMailServerUseSSL</key>
	    <true/>
	    <key>OutgoingMailServerUsername</key>
	    <string>$MailAddress</string>
	    <key>OutgoingPasswordSameAsIncomingPassword</key>
	    <true/>
	    <key>PayloadDescription</key>
	    <string>$Description</string>
	    <key>PayloadDisplayName</key>
	    <string>$DisplayName</string>
	    <key>PayloadIdentifier</key>
	    <string>E-Mail $Identifier</string>
	    <key>PayloadOrganization</key>
	    <string>$Organization</string>
	    <key>PayloadType</key>
	    <string>com.apple.mail.managed</string>
	    <key>PayloadUUID</key>
	    <string>$UUID1</string>
	    <key>PayloadVersion</key>
	    <integer>1</integer>
	</dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>$DisplayProfil</string>
    <key>PayloadDescription</key>
    <string>$Description</string>
    <key>PayloadIdentifier</key>
    <string>$Identifier</string>
    <key>PayloadOrganization</key>
    <string>$Organization</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>$UUID2</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>";

/* generate plain config file */
$filenameIn  = "tmp/$UUID1.plain";
$filenameOut = "tmp/$UUID1.signed";
file_put_contents($filenameIn, $data);

/**
 * sign the config file
 * -> be carefull with the path of your private key (should not be reachable from the web)
 */
$cmd = "/usr/bin/openssl smime -sign";
$cmd .= " -signer   certs/cert.pem";
$cmd .= " -inkey    certs/privkey.pem";
$cmd .= " -certfile certs/chain.pem";
$cmd .= " -nodetach -outform der";
$cmd .= " -in $filenameIn -out $filenameOut";
exec($cmd, $out, $rv);

$Date = date('Y-m-d @ H:i:s');
/* this way, you can check, who will call you for assistance ;) */
file_put_contents("tmp/configs.log", "[$rv] $Date: $AccountName <$MailAddress>\n", FILE_APPEND);

if ($rv != 0) {
  echo "Sorry, but this service does not work currently (rv = $rv) :/";
  exit;
}

/* give the user the .mobilconfig file */
header('Content-Type: application/x-apple-aspen-config; chatset=utf-8');
header("Content-Disposition: attachment; filename=\"$MailAddress.mobileconfig\"");
readfile($filenameOut);

/* remove temporary files */
unlink($filenameIn);
unlink($filenameOut);

?>

apple.phps

Last modified on 2020-03-24 at 20:01