diff --git a/admin/inc/class.admin_mail.inc.php b/admin/inc/class.admin_mail.inc.php index 242a95e9b2..741f98c26f 100644 --- a/admin/inc/class.admin_mail.inc.php +++ b/admin/inc/class.admin_mail.inc.php @@ -145,7 +145,7 @@ class admin_mail /** * List of domains know to not support Sieve * - * Used to switch Sieve off by default, thought users can allways try switching it on. + * Used to switch Sieve off by default, thought users can always try switching it on. * Testing not existing Sieve with google takes a long time, as ports are open, * but not answering ... * @@ -153,6 +153,23 @@ class admin_mail */ public static $no_sieve_blacklist = array('gmail.com', 'googlemail.com', 'outlook.office365.com'); + const ADD_CLIENT_TO_WELL_KNOWN = 'add-client-to-well-known'; + + /** + * Regular expressions to match domain in username to imap/smtp servers and oauth provider + * + * @var array[] email-regexp => [imap-host, smtp-host, oauth-provider, client-id, client-secret, scopes] pairs + */ + public static $oauth_domain_regexps = [ + '/@([^.]\.onmicrosoft\.com)$/i' => ['outlook.office365.com', 'smtp.office365.com', 'login.microsoftonline.com/$1', + 'e09fe57b-ffc5-496e-9ef8-3e6c7d628c09', 'Hd18Q~t-8_-ImvPFXlh8DSFjWKYyvpUTqURRJc7i', + 'https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access', + [self::ADD_CLIENT_TO_WELL_KNOWN => 'appid']], + '/@g(oogle)?mail\.com$/i' => ['imap.gmail.com', 'smtp.gmail.com', 'accounts.google.com', + '581021931838-unqjf9tivr9brnmo34rbsoj179ojp79p.apps.googleusercontent.com', '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', + 'https://mail.google.com/ https://www.googleapis.com/auth/userinfo.email'], + ]; + /** * Is current use a mail administrator / has run rights for EMailAdmin * @@ -206,10 +223,30 @@ class admin_mail ), $readonlys, $content, 2); } - const OFFICE365_CLIENT_ID = 'e09fe57b-ffc5-496e-9ef8-3e6c7d628c09'; - const OFFICE365_CLIENT_SECRET = 'Hd18Q~t-8_-ImvPFXlh8DSFjWKYyvpUTqURRJc7i'; - const OFFICE365_IMAP_HOST = 'outlook.office365.com'; - const OFFICE365_SMTP_HOST = 'smtp.office365.com'; + /** + * Find server config by email-domain incl. oauth data + * + * @param $email + * @return array|null + */ + public static function oauthDomainMatch($email) + { + foreach(self::$oauth_domain_regexps as $regexp => [$imap, $smtp, $provider, $client, $secret, $scopes, $extra]) + { + if (preg_match($regexp, $email, $matches)) + { + return [ + 'imap' => $imap, + 'smtp' => $smtp, + 'provider' => $provider ? 'https://'.strtr($provider, ['$1' => $matches[1] ?? null, '$2' => $matches[2] ?? null]): null, + 'client' => $client, + 'secret' => $secret, + 'scopes' => array_merge(['openid'], explode(' ', $scopes)), + ]+($extra ?? []); + } + } + return null; + } /** * Try to autoconfig an account @@ -245,18 +282,18 @@ class admin_mail ); } } - // Office 365 Mail - elseif (preg_match('/@[^.]+\.onmicrosoft\.com$/i', $content['acc_imap_username'])) + // supported oauth providers + elseif (($oauth = self::oauthDomainMatch($content['acc_imap_username']))) { - $content['output'] = lang('Using Office365 mail servers')."\n"; - list(, $domain) = explode('@', $content['acc_imap_username']); - $hosts[self::OFFICE365_IMAP_HOST] = array( - self::SSL_TLS => 993, - ); - $content['acc_smpt_host'] = self::OFFICE365_SMTP_HOST; + $content['output'] = lang('Using IMAP:%1, SMTP:%2, OAUTH:%3:', $oauth['imap'], $oauth['smtp'], $oauth['provider'])."\n"; + $hosts[$oauth['imap']] = true; + $content['acc_smpt_host'] = $oauth['smtp']; $content['acc_sieve_enabled'] = false; - $content['acc_oauth_provider_url'] = "https://login.microsoftonline.com/$domain"; - $content['acc_oauth_client_id'] = self::OFFICE365_CLIENT_ID; + $content['acc_oauth_provider_url'] = $oauth['provider']; + $content['acc_oauth_client_id'] = $oauth['client']; + $content['acc_oauth_client_secret'] = $oauth['secret']; + $content['acc_oauth_scopes'] = $oauth['scopes']; + $content[self::ADD_CLIENT_TO_WELL_KNOWN] = $oauth[self::ADD_CLIENT_TO_WELL_KNOWN] ?? null; } elseif (($ispdb = self::mozilla_ispdb($content['ident_email'])) && count($ispdb['imap'])) { @@ -286,6 +323,13 @@ class admin_mail $hosts = $this->guess_hosts($content['ident_email'], 'imap'); } + // check if support OAuth for that domain or we have a password + if (empty($oauth) && empty($content['acc_imap_password'])) + { + Etemplate::set_validation_error('acc_imap_password', lang('Field must not be empty!')); + $connected = false; + } + // iterate over all hosts and try to connect foreach(!isset($connected) ? $hosts : [] as $host => $data) { @@ -1471,31 +1515,21 @@ class admin_mail { if (empty($content['acc_oauth_client_secret'])) { - switch($content['acc_oauth_client_id']) - { - case self::OFFICE365_CLIENT_ID: - $content['acc_oauth_client_secret'] = self::OFFICE365_CLIENT_SECRET; - break; - default: - throw new Exception(lang("No OAuth client secret for provider '%1'!", $content['acc_oauth_provider_url'])); - } + throw new Exception(lang("No OAuth client secret for provider '%1'!", $content['acc_oauth_provider_url'])); } $oidc = new OpenIDConnectClient($content['acc_oauth_provider_url'], $content['acc_oauth_client_id'], $content['acc_oauth_client_secret']); // Office365 requires client-ID as appid GET parameter (https://github.com/jumbojett/OpenID-Connect-PHP/issues/190) - if ($content['acc_oauth_client_id'] = self::OFFICE365_CLIENT_ID) + if (!empty($content[self::ADD_CLIENT_TO_WELL_KNOWN])) { - $oidc->setWellKnownConfigParameters(['appid' => $content['acc_oauth_client_id']]); + $oidc->setWellKnownConfigParameters([$content[self::ADD_CLIENT_TO_WELL_KNOWN] => $content['acc_oauth_client_id']]); } // we need to use response_code=query / GET request to keep our session token! $oidc->setResponseTypes(['code']); // to be able to use query, not 'id_token' - $oidc->setAllowImplicitFlow(true); - $oidc->addAuthParam(['response_mode' => 'query']); // to keep cookie, not 'form_post' - - // ToDo: the last 3 are Office365 specific scopes - $oidc->addScope(['openid', 'email', 'profile', 'https://outlook.office.com/IMAP.AccessAsUser.All', 'https://outlook.office.com/SMTP.Send', 'https://outlook.office.com/User.Read']); + //$oidc->setAllowImplicitFlow(true); + $oidc->addScope($content['acc_oauth_scopes']); if (!empty($content['acc_oauth_access_token']) || !empty($content['acc_oauth_refresh_token'])) { diff --git a/admin/templates/default/mailwizard.xet b/admin/templates/default/mailwizard.xet index c5a192467d..b14357bc63 100644 --- a/admin/templates/default/mailwizard.xet +++ b/admin/templates/default/mailwizard.xet @@ -23,7 +23,7 @@ - + @@ -56,4 +56,4 @@ - + \ No newline at end of file