public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
Autoconfig harmonization
- use $autodiscover_config everywhere - non-443 HTTPS ports in autoconfig etc. - disabling POP service via SRV record - fix display name in Outlook IMAP autodiscover - allow multiple calls to TLSA generator and support Sieve STARTTLS - iOS mobileconfig generator
5abeb313
data/web/autoconfig.php | 47 ++++++++----
data/web/autodiscover.php | 54 +++++++------
data/web/inc/functions.inc.php | 24 +++---
data/web/inc/vars.inc.php | 31 ++++++--
data/web/mobileconfig.php | 167 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 267 insertions(+), 56 deletions(-)
create mode 100644 data/web/mobileconfig.php
Diff
diff --git a/data/web/autoconfig.php b/data/web/autoconfig.php
index d01bc724..523d6183 100644
--- a/data/web/autoconfig.php
+++ b/data/web/autoconfig.php
@@ -5,6 +5,16 @@ if (empty($mailcow_hostname)) {
exit();
}
+$domain_dot = strpos($_SERVER['HTTP_HOST'], '.');
+$domain_port = strpos($_SERVER['HTTP_HOST'], ':');
+if ($domain_port === FALSE) {
+ $domain = substr($_SERVER['HTTP_HOST'], $domain_dot+1);
+ $port = 443;
+} else {
+ $domain = substr($_SERVER['HTTP_HOST'], $domain_dot+1, $domain_port-$domain_dot-1);
+ $port = substr($_SERVER['HTTP_HOST'], $domain_port+1);
+}
+
header('Content-Type: application/xml');
?>
<?= '<?xml version="1.0"?>'; ?>
@@ -15,52 +25,59 @@ header('Content-Type: application/xml');
<displayShortName>mail server</displayShortName>
<incomingServer type="imap">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>993</port>
+ <hostname><?= $autodiscover_config['imap']['server']; ?></hostname>
+ <port><?= $autodiscover_config['imap']['port']; ?></port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
<incomingServer type="imap">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>143</port>
+ <hostname><?= $autodiscover_config['imap']['server']; ?></hostname>
+ <port><?= $autodiscover_config['imap']['tlsport']; ?></port>
<socketType>STARTTLS</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
+<?php
+$records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV); // check if POP3 is announced as "not provided" via SRV record
+if (count($records) == 0 || $records[0]['target'] != '') { ?>
<incomingServer type="pop3">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>995</port>
+ <hostname><?= $autodiscover_config['pop3']['server']; ?></hostname>
+ <port><?= $autodiscover_config['pop3']['port']; ?></port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
+<?php } ?>
+<?php
+$records = dns_get_record('_pop3._tcp.' . $domain, DNS_SRV); // check if POP3 is announced as "not provided" via SRV record
+if (count($records) == 0 || $records[0]['target'] != '') { ?>
<incomingServer type="pop3">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>110</port>
+ <hostname><?= $autodiscover_config['pop3']['server']; ?></hostname>
+ <port><?= $autodiscover_config['pop3']['tlsport']; ?></port>
<socketType>STARTTLS</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
+<?php } ?>
<outgoingServer type="smtp">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>465</port>
+ <hostname><?= $autodiscover_config['smtp']['server']; ?></hostname>
+ <port><?= $autodiscover_config['smtp']['port']; ?></port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</outgoingServer>
-
<outgoingServer type="smtp">
- <hostname><?= $mailcow_hostname; ?></hostname>
- <port>587</port>
+ <hostname><?= $autodiscover_config['smtp']['server']; ?></hostname>
+ <port><?= $autodiscover_config['smtp']['tlsport']; ?></port>
<socketType>STARTTLS</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</outgoingServer>
- <enable visiturl="https://<?= $mailcow_hostname; ?>/admin.php">
+ <enable visiturl="https://<?= $mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin.php">
<instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>
<instruction lang="de">Sollten Sie das Ihnen durch den Administrator vergebene Passwort noch nicht geändert haben, empfehlen wir dies nun zu tun. Auch ein altes Passwort sollte aus Sicherheitsgründen geändert werden.</instruction>
</enable>
@@ -68,6 +85,6 @@ header('Content-Type: application/xml');
</emailProvider>
<webMail>
- <loginPage url="https://<?= $mailcow_hostname; ?>/SOGo/" />
+ <loginPage url="https://<?= $mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/SOGo/" />
</webMail>
</clientConfig>
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
index fd8cd641..7f6081ae 100644
--- a/data/web/autodiscover.php
+++ b/data/web/autodiscover.php
@@ -12,13 +12,14 @@ error_reporting(0);
$data = trim(file_get_contents("php://input"));
// Desktop client needs IMAP, unless it's Outlook 2013 or higher on Windows
-if (strpos($data, 'autodiscover/outlook/responseschema')) { // desktop client
+if (strpos($data, 'autodiscover/outlook/responseschema') !== false) { // desktop client
$configuration['autodiscoverType'] = 'imap';
if ($configuration['useEASforOutlook'] == 'yes' &&
- // Office for macOS does not support EAS
- strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') === false &&
- // Outlook 2013 (version 15) or higher
- preg_match('/(Outlook|Office).+1[5-9]\./', $_SERVER['HTTP_USER_AGENT'])) {
+ // Office for macOS does not support EAS
+ strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') === false &&
+ // Outlook 2013 (version 15) or higher
+ preg_match('/(Outlook|Office).+1[5-9]\./', $_SERVER['HTTP_USER_AGENT'])
+ ) {
$configuration['autodiscoverType'] = 'activesync';
}
}
@@ -60,8 +61,28 @@ else {
<?php
exit(0);
}
- $discover = new SimpleXMLElement($data);
- $email = $discover->Request->EMailAddress;
+ try {
+ $discover = new SimpleXMLElement($data);
+ $email = $discover->Request->EMailAddress;
+ } catch (Exception $e) {
+ $email = $_SERVER['PHP_AUTH_USER'];
+ }
+
+ $username = trim($email);
+ try {
+ $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
+ $stmt->execute(array(':username' => $username));
+ $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+ }
+ catch(PDOException $e) {
+ die("Failed to determine name from SQL");
+ }
+ if (!empty($MailboxData['name'])) {
+ $displayname = utf8_encode($MailboxData['name']);
+ }
+ else {
+ $displayname = $email;
+ }
if ($configuration['autodiscoverType'] == 'imap') {
?>
@@ -96,13 +117,13 @@ else {
</Protocol>
<Protocol>
<Type>CalDAV</Type>
- <Server><?=$configuration['caldav']['server'];?>/SOGo/dav/<?=$email;?>/Calendar</Server>
+ <Server>https://<?=$configuration['caldav']['server'];?><?php if ($configuration['caldav']['port'] != 443) echo ':'.$configuration['caldav']['port']; ?>/SOGo/dav/<?=$email;?>/Calendar</Server>
<DomainRequired>off</DomainRequired>
<LoginName><?=$email;?></LoginName>
</Protocol>
<Protocol>
<Type>CardDAV</Type>
- <Server><?=$configuration['carddav']['server'];?>/SOGo/dav/<?=$email;?>/Contacts</Server>
+ <Server>https://<?=$configuration['carddav']['server'];?><?php if ($configuration['caldav']['port'] != 443) echo ':'.$configuration['carddav']['port']; ?>/SOGo/dav/<?=$email;?>/Contacts</Server>
<DomainRequired>off</DomainRequired>
<LoginName><?=$email;?></LoginName>
</Protocol>
@@ -111,21 +132,6 @@ else {
<?php
}
else if ($configuration['autodiscoverType'] == 'activesync') {
- $username = trim($email);
- try {
- $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
- $stmt->execute(array(':username' => $username));
- $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
- }
- catch(PDOException $e) {
- die("Failed to determine name from SQL");
- }
- if (!empty($MailboxData['name'])) {
- $displayname = utf8_encode($MailboxData['name']);
- }
- else {
- $displayname = $email;
- }
?>
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
<Culture>en:en</Culture>
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index b3420530..af93794b 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -62,17 +62,17 @@ function hasMailboxObjectAccess($username, $role, $object) {
}
return false;
}
+function pem_to_der($pem_key) {
+ // Need to remove BEGIN/END PUBLIC KEY
+ $lines = explode("\n", trim($pem_key));
+ unset($lines[count($lines)-1]);
+ unset($lines[0]);
+ return base64_decode(implode('', $lines));
+}
function generate_tlsa_digest($hostname, $port, $starttls = null) {
if (!is_valid_domain_name($hostname)) {
return "Not a valid hostname";
}
- function pem_to_der($pem_key) {
- // Need to remove BEGIN/END PUBLIC KEY
- $lines = explode("\n", trim($pem_key));
- unset($lines[count($lines)-1]);
- unset($lines[0]);
- return base64_decode(implode('', $lines));
- }
if (empty($starttls)) {
$context = stream_context_create(array("ssl" => array("capture_peer_cert" => true, 'verify_peer' => false, 'allow_self_signed' => true)));
@@ -88,20 +88,24 @@ function generate_tlsa_digest($hostname, $port, $starttls = null) {
return $error_nr . ': ' . $error_msg;
}
$banner = fread($stream, 512 );
- if (preg_match("/^220/i", $banner)) {
+ if (preg_match("/^220/i", $banner)) { // SMTP
fwrite($stream,"HELO tlsa.generator.local\r\n");
fread($stream, 512);
fwrite($stream,"STARTTLS\r\n");
fread($stream, 512);
}
- elseif (preg_match("/imap.+starttls/i", $banner)) {
+ elseif (preg_match("/imap.+starttls/i", $banner)) { // IMAP
fwrite($stream,"A1 STARTTLS\r\n");
fread($stream, 512);
}
- elseif (preg_match("/^\+OK/", $banner)) {
+ elseif (preg_match("/^\+OK/", $banner)) { // POP3
fwrite($stream,"STLS\r\n");
fread($stream, 512);
}
+ elseif (preg_match("/^OK/m", $banner)) { // Sieve
+ fwrite($stream,"STARTTLS\r\n");
+ fread($stream, 512);
+ }
else {
return 'Unknown banner: "' . htmlspecialchars(trim($banner)) . '"';
}
diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php
index a52442db..775af1e1 100644
--- a/data/web/inc/vars.inc.php
+++ b/data/web/inc/vars.inc.php
@@ -18,31 +18,48 @@ $database_name = getenv('DBNAME');
$mailcow_hostname = getenv('MAILCOW_HOSTNAME');
// Autodiscover settings
+$https_port = strpos($_SERVER['HTTP_HOST'], ':');
+if ($https_port === FALSE) {
+ $https_port = 443;
+} else {
+ $https_port = substr($_SERVER['HTTP_HOST'], $https_port+1);
+}
$autodiscover_config = array(
// Enable the autodiscover service for Outlook desktop clients
'useEASforOutlook' => 'yes',
// General autodiscover service type: "activesync" or "imap"
'autodiscoverType' => 'activesync',
- // Please don't use STARTTLS-enabled service ports here.
+ // Please don't use STARTTLS-enabled service ports in the "port" variable.
// The autodiscover service will always point to SMTPS and IMAPS (TLS-wrapped services).
+ // The autoconfig service will additionally announce the STARTTLS-enabled ports, specified in the "tlsport" variable.
'imap' => array(
'server' => $mailcow_hostname,
- 'port' => getenv('IMAPS_PORT'),
+ 'port' => array_pop(explode(':', getenv('IMAPS_PORT'))),
+ 'tlsport' => array_pop(explode(':', getenv('IMAP_PORT'))),
+ ),
+ 'pop3' => array(
+ 'server' => $mailcow_hostname,
+ 'port' => array_pop(explode(':', getenv('POPS_PORT'))),
+ 'tlsport' => array_pop(explode(':', getenv('POP_PORT'))),
),
'smtp' => array(
'server' => $mailcow_hostname,
- 'port' => getenv('SMTPS_PORT'),
+ 'port' => array_pop(explode(':', getenv('SMTPS_PORT'))),
+ 'tlsport' => array_pop(explode(':', getenv('SUBMISSION_PORT'))),
),
'activesync' => array(
- 'url' => 'https://'.$mailcow_hostname.'/Microsoft-Server-ActiveSync'
+ 'url' => 'https://'.$mailcow_hostname.($https_port == 443 ? '' : ':'.$https_port).'/Microsoft-Server-ActiveSync',
),
'caldav' => array(
- 'url' => 'https://'.$mailcow_hostname
+ 'server' => $mailcow_hostname,
+ 'port' => $https_port,
),
'carddav' => array(
- 'url' => 'https://'.$mailcow_hostname
- )
+ 'server' => $mailcow_hostname,
+ 'port' => $https_port,
+ ),
);
+unset($https_port);
// Where to go after adding and editing objects
// Can be "form" or "previous"
diff --git a/data/web/mobileconfig.php b/data/web/mobileconfig.php
new file mode 100644
index 00000000..198fa4d7
--- /dev/null
+++ b/data/web/mobileconfig.php
@@ -0,0 +1,167 @@
+<?php
+require_once 'inc/prerequisites.inc.php';
+
+if (empty($mailcow_hostname)) {
+ exit();
+}
+if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'user') {
+ header("Location: index.php");
+ die("This page is only available to logged-in users, not admins.");
+}
+
+header('Content-Type: application/x-apple-aspen-config');
+header('Content-Disposition: attachment; filename="Mailcow.mobileconfig"');
+
+$email = $_SESSION['mailcow_cc_username'];
+$domain = explode('@', $_SESSION['mailcow_cc_username'])[1];
+$identifier = implode('.', array_reverse(explode('.', $domain))) . '.iphoneprofile.mailcow';
+
+try {
+ $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
+ $stmt->execute(array(':username' => $email));
+ $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+}
+catch(PDOException $e) {
+ die("Failed to determine name from SQL");
+}
+if (!empty($MailboxData['name'])) {
+ $displayname = utf8_encode($MailboxData['name']);
+}
+else {
+ $displayname = $email;
+}
+
+echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
+?>
+<!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>CalDAVAccountDescription</key>
+ <string><?php echo $domain; ?></string>
+ <key>CalDAVHostName</key>
+ <string><?php echo $autodiscover_config['caldav']['server']; ?></string>
+ <key>CalDAVPort</key>
+ <real><?php echo $autodiscover_config['caldav']['port']; ?></real>
+ <key>CalDAVPrincipalURL</key>
+ <string>/SOGo/dav/<?php echo $email; ?></string>
+ <key>CalDAVUseSSL</key>
+ <true/>
+ <key>CalDAVUsername</key>
+ <string><?php echo $email; ?></string>
+ <key>PayloadDescription</key>
+ <string>Configures CalDAV account.</string>
+ <key>PayloadDisplayName</key>
+ <string>CalDAV (<?php echo $domain; ?>)</string>
+ <key>PayloadIdentifier</key>
+ <string><?php echo $identifier; ?>.CalDAV</string>
+ <key>PayloadOrganization</key>
+ <string></string>
+ <key>PayloadType</key>
+ <string>com.apple.caldav.account</string>
+ <key>PayloadUUID</key>
+ <string>FC898573-EBA8-48AF-93BD-BFA0C9778FA7</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ </dict>
+ <dict>
+ <key>EmailAccountDescription</key>
+ <string><?php echo $domain; ?></string>
+ <key>EmailAccountType</key>
+ <string>EmailTypeIMAP</string>
+ <key>EmailAccountName</key>
+ <string><?php echo $displayname; ?></string>
+ <key>EmailAddress</key>
+ <string><?php echo $email; ?></string>
+ <key>IncomingMailServerAuthentication</key>
+ <string>EmailAuthPassword</string>
+ <key>IncomingMailServerHostName</key>
+ <string><?php echo $autodiscover_config['imap']['server']; ?></string>
+ <key>IncomingMailServerPortNumber</key>
+ <integer><?php echo $autodiscover_config['imap']['port']; ?></integer>
+ <key>IncomingMailServerUseSSL</key>
+ <true/>
+ <key>IncomingMailServerUsername</key>
+ <string><?php echo $email; ?></string>
+ <key>OutgoingMailServerAuthentication</key>
+ <string>EmailAuthPassword</string>
+ <key>OutgoingMailServerHostName</key>
+ <string><?php echo $autodiscover_config['smtp']['server']; ?></string>
+ <key>OutgoingMailServerPortNumber</key>
+ <integer><?php echo $autodiscover_config['smtp']['port']; ?></integer>
+ <key>OutgoingMailServerUseSSL</key>
+ <true/>
+ <key>OutgoingMailServerUsername</key>
+ <string><?php echo $email; ?></string>
+ <key>OutgoingPasswordSameAsIncomingPassword</key>
+ <true/>
+ <key>PayloadDescription</key>
+ <string>Configures email account.</string>
+ <key>PayloadDisplayName</key>
+ <string>IMAP Account (<?php echo $domain; ?>)</string>
+ <key>PayloadIdentifier</key>
+ <string><?php echo $identifier; ?>.email</string>
+ <key>PayloadOrganization</key>
+ <string></string>
+ <key>PayloadType</key>
+ <string>com.apple.mail.managed</string>
+ <key>PayloadUUID</key>
+ <string>00294FBB-1016-413E-87B9-652D856D6875</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ <key>PreventAppSheet</key>
+ <false/>
+ <key>PreventMove</key>
+ <false/>
+ <key>SMIMEEnabled</key>
+ <false/>
+ </dict>
+ <dict>
+ <key>CardDAVAccountDescription</key>
+ <string><?php echo $domain; ?></string>
+ <key>CardDAVHostName</key>
+ <string><?php echo $autodiscover_config['carddav']['server']; ?></string>
+ <key>CardDAVPort</key>
+ <integer><?php echo $autodiscover_config['carddav']['port']; ?></integer>
+ <key>CardDAVPrincipalURL</key>
+ <string>/SOGo/dav/<?php echo $email; ?></string>
+ <key>CardDAVUseSSL</key>
+ <true/>
+ <key>CardDAVUsername</key>
+ <string><?php echo $email; ?></string>
+ <key>PayloadDescription</key>
+ <string>Configures CardDAV accounts</string>
+ <key>PayloadDisplayName</key>
+ <string>CardDAV (<?php echo $domain; ?>)</string>
+ <key>PayloadIdentifier</key>
+ <string><?php echo $identifier; ?>.carddav</string>
+ <key>PayloadOrganization</key>
+ <string></string>
+ <key>PayloadType</key>
+ <string>com.apple.carddav.account</string>
+ <key>PayloadUUID</key>
+ <string>0797EF2B-B1F1-4BC7-ABCD-4580862252B4</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ </dict>
+ </array>
+ <key>PayloadDescription</key>
+ <string>IMAP, CalDAV, CardDAV</string>
+ <key>PayloadDisplayName</key>
+ <string><?php echo $domain; ?> Mailcow</string>
+ <key>PayloadIdentifier</key>
+ <string><?php echo $identifier; ?></string>
+ <key>PayloadOrganization</key>
+ <string></string>
+ <key>PayloadRemovalDisallowed</key>
+ <false/>
+ <key>PayloadType</key>
+ <string>Configuration</string>
+ <key>PayloadUUID</key>
+ <string>5EE248C5-ACCB-42D8-9199-8F8ED08D5624</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+</dict>
+</plist>