aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Mail
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Mail')
-rw-r--r--lib/private/Mail/Attachment.php2
-rw-r--r--lib/private/Mail/EMailTemplate.php78
-rw-r--r--lib/private/Mail/Mailer.php65
-rw-r--r--lib/private/Mail/Message.php4
-rw-r--r--lib/private/Mail/Provider/Manager.php255
5 files changed, 360 insertions, 44 deletions
diff --git a/lib/private/Mail/Attachment.php b/lib/private/Mail/Attachment.php
index 8dede9714f8..2a5246c0019 100644
--- a/lib/private/Mail/Attachment.php
+++ b/lib/private/Mail/Attachment.php
@@ -22,7 +22,7 @@ class Attachment implements IAttachment {
private ?string $body,
private ?string $name,
private ?string $contentType,
- private ?string $path = null
+ private ?string $path = null,
) {
}
diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php
index 80740e14aca..a327109cc12 100644
--- a/lib/private/Mail/EMailTemplate.php
+++ b/lib/private/Mail/EMailTemplate.php
@@ -76,7 +76,7 @@ EOF;
<tbody>
<tr style="padding:0;text-align:left;vertical-align:top">
<center data-parsed="" style="background-color:%s;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px">
- <img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto">
+ <img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto"%s>
</center>
</tr>
</tbody>
@@ -190,32 +190,46 @@ EOF;
<tr style="padding:0;text-align:left;vertical-align:top">
<th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
<center data-parsed="" style="min-width:490px;width:100%%">
- <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:60px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;background-color:%1\$s;color:#fefefe;">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
+ <!--[if (gte mso 9)|(IE)]>
+ <table>
+ <tr>
+ <td>
+ <![endif]-->
+ <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;background-color:%1\$s;color:#fefefe;">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;border-radius:2px;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid %6\$s;text-decoration:none">%7\$s</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid %6\$s;text-decoration:none">%7\$s</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
- </tr>
- </table>
- <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
+ <td>
+ <![endif]-->
+ <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="%8\$s" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">%9\$s</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="%8\$s" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">%9\$s</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
+ <![endif]-->
</center>
</th>
<th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
@@ -243,13 +257,13 @@ EOF;
<tr style="padding:0;text-align:left;vertical-align:top">
<th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
<center data-parsed="" style="min-width:490px;width:100%%">
- <table class="button btn default primary float-center" style="Margin:0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0;max-height:60px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;color:#fefefe;background-color:%1\$s;">
+ <table class="button btn default primary float-center" style="Margin:0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0;border-radius:8px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;color:#fefefe;background-color:%1\$s;">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
<table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;border-radius:2px;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid %5\$s;text-decoration:none">%7\$s</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid %5\$s;text-decoration:none">%7\$s</a>
</td>
</tr>
</table>
@@ -308,6 +322,8 @@ EOF;
protected Defaults $themingDefaults,
protected IURLGenerator $urlGenerator,
protected IFactory $l10nFactory,
+ protected ?int $logoWidth,
+ protected ?int $logoHeight,
protected string $emailId,
protected array $data,
) {
@@ -330,15 +346,21 @@ EOF;
}
$this->headerAdded = true;
+ $logoSizeDimensions = '';
+ if ($this->logoWidth && $this->logoHeight) {
+ // Provide a logo size when we have the dimensions so that it displays nicely in Outlook
+ $logoSizeDimensions = ' width="' . $this->logoWidth . '" height="' . $this->logoHeight . '"';
+ }
+
$logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false));
- $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoUrl, $this->themingDefaults->getName()]);
+ $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoUrl, $this->themingDefaults->getName(), $logoSizeDimensions]);
}
/**
* Adds a heading to the email
*
* @param string|bool $plainTitle Title that is used in the plain text email
- * if empty the $title is used, if false none will be used
+ * if empty the $title is used, if false none will be used
*/
public function addHeading(string $title, $plainTitle = ''): void {
if ($this->footerAdded) {
@@ -371,7 +393,7 @@ EOF;
*
* @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
* @param string|bool $plainText Text that is used in the plain text email
- * if empty the $text is used, if false none will be used
+ * if empty the $text is used, if false none will be used
*/
public function addBodyText(string $text, $plainText = ''): void {
if ($this->footerAdded) {
@@ -398,9 +420,9 @@ EOF;
* @param string $metaInfo Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
* @param string $icon Absolute path, must be 16*16 pixels
* @param string|bool $plainText Text that is used in the plain text email
- * if empty or true the $text is used, if false none will be used
+ * if empty or true the $text is used, if false none will be used
* @param string|bool $plainMetaInfo Meta info that is used in the plain text email
- * if empty or true the $metaInfo is used, if false none will be used
+ * if empty or true the $metaInfo is used, if false none will be used
* @param integer $plainIndent plainIndent If > 0, Indent plainText by this amount.
* @since 12.0.0
*/
@@ -417,7 +439,7 @@ EOF;
if ($plainText === '' || $plainText === true) {
$plainText = $text;
$text = htmlspecialchars($text);
- $text = str_replace("\n", "<br/>", $text); // convert newlines to HTML breaks
+ $text = str_replace("\n", '<br/>', $text); // convert newlines to HTML breaks
}
if ($plainMetaInfo === '' || $plainMetaInfo === true) {
$plainMetaInfo = $metaInfo;
@@ -515,7 +537,7 @@ EOF;
$this->ensureBodyListClosed();
$color = $this->themingDefaults->getDefaultColorPrimary();
- $textColor = $this->themingDefaults->getTextColorPrimary();
+ $textColor = $this->themingDefaults->getDefaultTextColorPrimary();
$this->htmlBody .= vsprintf($this->buttonGroup, [$color, $color, $urlLeft, $color, $textColor, $textColor, $textLeft, $urlRight, $textRight]);
$this->plainBody .= PHP_EOL . $plainTextLeft . ': ' . $urlLeft . PHP_EOL;
@@ -528,7 +550,7 @@ EOF;
* @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
* @param string $url URL of button
* @param string|false $plainText Text of button in plain text version
- * if empty the $text is used, if false none will be used
+ * if empty the $text is used, if false none will be used
*
* @since 12.0.0
*/
@@ -546,7 +568,7 @@ EOF;
}
$color = $this->themingDefaults->getDefaultColorPrimary();
- $textColor = $this->themingDefaults->getTextColorPrimary();
+ $textColor = $this->themingDefaults->getDefaultTextColorPrimary();
$this->htmlBody .= vsprintf($this->button, [$color, $color, $url, $color, $textColor, $textColor, $text]);
if ($plainText !== false) {
diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php
index e866cbfdbbc..bdc4d6760e0 100644
--- a/lib/private/Mail/Mailer.php
+++ b/lib/private/Mail/Mailer.php
@@ -27,6 +27,7 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
use Symfony\Component\Mailer\MailerInterface;
+use Symfony\Component\Mailer\Transport\NullTransport;
use Symfony\Component\Mailer\Transport\SendmailTransport;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
@@ -52,16 +53,22 @@ use Symfony\Component\Mime\Exception\RfcComplianceException;
* @package OC\Mail
*/
class Mailer implements IMailer {
+ // Do not move this block or change it's content without contacting the release crew
+ public const DEFAULT_DIMENSIONS = '252x120';
+ // Do not move this block or change it's content without contacting the release crew
+
+ public const MAX_LOGO_SIZE = 105;
+
private ?MailerInterface $instance = null;
public function __construct(
- private IConfig $config,
- private LoggerInterface $logger,
- private Defaults $defaults,
- private IURLGenerator $urlGenerator,
- private IL10N $l10n,
+ private IConfig $config,
+ private LoggerInterface $logger,
+ private Defaults $defaults,
+ private IURLGenerator $urlGenerator,
+ private IL10N $l10n,
private IEventDispatcher $dispatcher,
- private IFactory $l10nFactory,
+ private IFactory $l10nFactory,
) {
}
@@ -97,6 +104,31 @@ class Mailer implements IMailer {
* @since 12.0.0
*/
public function createEMailTemplate(string $emailId, array $data = []): IEMailTemplate {
+ $logoDimensions = $this->config->getAppValue('theming', 'logoDimensions', self::DEFAULT_DIMENSIONS);
+ if (str_contains($logoDimensions, 'x')) {
+ [$width, $height] = explode('x', $logoDimensions);
+ $width = (int)$width;
+ $height = (int)$height;
+
+ if ($width > self::MAX_LOGO_SIZE || $height > self::MAX_LOGO_SIZE) {
+ if ($width === $height) {
+ $logoWidth = self::MAX_LOGO_SIZE;
+ $logoHeight = self::MAX_LOGO_SIZE;
+ } elseif ($width > $height) {
+ $logoWidth = self::MAX_LOGO_SIZE;
+ $logoHeight = (int)(($height / $width) * self::MAX_LOGO_SIZE);
+ } else {
+ $logoWidth = (int)(($width / $height) * self::MAX_LOGO_SIZE);
+ $logoHeight = self::MAX_LOGO_SIZE;
+ }
+ } else {
+ $logoWidth = $width;
+ $logoHeight = $height;
+ }
+ } else {
+ $logoWidth = $logoHeight = null;
+ }
+
$class = $this->config->getSystemValueString('mail_template_class', '');
if ($class !== '' && class_exists($class) && is_a($class, EMailTemplate::class, true)) {
@@ -104,6 +136,8 @@ class Mailer implements IMailer {
$this->defaults,
$this->urlGenerator,
$this->l10nFactory,
+ $logoWidth,
+ $logoHeight,
$emailId,
$data
);
@@ -113,6 +147,8 @@ class Mailer implements IMailer {
$this->defaults,
$this->urlGenerator,
$this->l10nFactory,
+ $logoWidth,
+ $logoHeight,
$emailId,
$data
);
@@ -196,9 +232,6 @@ class Mailer implements IMailer {
}
/**
- * @deprecated 26.0.0 Implicit validation is done in \OC\Mail\Message::setRecipients
- * via \Symfony\Component\Mime\Address::__construct
- *
* @param string $email Email address to be validated
* @return bool True if the mail address is valid, false otherwise
*/
@@ -220,9 +253,10 @@ class Mailer implements IMailer {
return $this->instance;
}
- $transport = null;
-
switch ($this->config->getSystemValueString('mail_smtpmode', 'smtp')) {
+ case 'null':
+ $transport = new NullTransport();
+ break;
case 'sendmail':
$transport = $this->getSendMailInstance();
break;
@@ -232,7 +266,9 @@ class Mailer implements IMailer {
break;
}
- return new SymfonyMailer($transport);
+ $this->instance = new SymfonyMailer($transport);
+
+ return $this->instance;
}
/**
@@ -299,8 +335,10 @@ class Mailer implements IMailer {
break;
default:
$sendmail = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath('sendmail');
- if ($sendmail === null) {
+ if ($sendmail === false) {
+ // fallback (though not sure what good it'll do)
$sendmail = '/usr/sbin/sendmail';
+ $this->logger->debug('sendmail binary search failed, using fallback ' . $sendmail, ['app' => 'core']);
}
$binaryPath = $sendmail;
break;
@@ -311,6 +349,7 @@ class Mailer implements IMailer {
default => ' -bs',
};
+ $this->logger->debug('Using sendmail binary: ' . $binaryPath, ['app' => 'core']);
return new SendmailTransport($binaryPath . $binaryParam, null, $this->logger);
}
}
diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php
index faeba469e20..523a4836760 100644
--- a/lib/private/Mail/Message.php
+++ b/lib/private/Mail/Message.php
@@ -316,7 +316,7 @@ class Message implements IMessage {
public function getAutoSubmitted(): string {
$headers = $this->symfonyEmail->getHeaders();
- return $headers->has(AutoSubmitted::HEADER) ?
- $headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
+ return $headers->has(AutoSubmitted::HEADER)
+ ? $headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
}
}
diff --git a/lib/private/Mail/Provider/Manager.php b/lib/private/Mail/Provider/Manager.php
new file mode 100644
index 00000000000..f162d30b834
--- /dev/null
+++ b/lib/private/Mail/Provider/Manager.php
@@ -0,0 +1,255 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Mail\Provider;
+
+use OC\AppFramework\Bootstrap\Coordinator;
+use OCP\Mail\Provider\IManager;
+use OCP\Mail\Provider\IProvider;
+use OCP\Mail\Provider\IService;
+use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
+use Throwable;
+
+class Manager implements IManager {
+
+ protected ?array $providersCollection = null;
+
+ public function __construct(
+ private Coordinator $coordinator,
+ private ContainerInterface $container,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ /**
+ * Determine if any mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return bool
+ */
+ public function has(): bool {
+
+ // return true if collection has any providers
+ return !empty($this->providers());
+
+ }
+
+ /**
+ * Retrieve a count of how many mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return int
+ */
+ public function count(): int {
+
+ // return count of providers in collection
+ return count($this->providers());
+
+ }
+
+ /**
+ * Retrieve which mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,string> collection of provider id and label ['jmap' => 'JMap Connector']
+ */
+ public function types(): array {
+
+ // construct types collection
+ $types = [];
+ // extract id and name from providers collection
+ foreach ($this->providers() as $entry) {
+ $types[$entry->id()] = $entry->label();
+ }
+ // return types collection
+ return $types;
+
+ }
+
+ /**
+ * Retrieve all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,IProvider> collection of provider id and object ['jmap' => IProviderObject]
+ */
+ public function providers(): array {
+
+ // evaluate if we already have a cached collection of providers and return the collection if we do
+ if (is_array($this->providersCollection)) {
+ return $this->providersCollection;
+ }
+ // retrieve server registration context
+ $context = $this->coordinator->getRegistrationContext();
+ // evaluate if registration context was returned
+ if ($context === null) {
+ return [];
+ }
+ // initilize cached collection
+ $this->providersCollection = [];
+ // iterate through all registered mail providers
+ foreach ($context->getMailProviders() as $entry) {
+ try {
+ /** @var IProvider $provider */
+ // object provider
+ $provider = $this->container->get($entry->getService());
+ // add provider to cache collection
+ $this->providersCollection[$provider->id()] = $provider;
+ } catch (Throwable $e) {
+ $this->logger->error(
+ 'Could not load mail provider ' . $entry->getService() . ': ' . $e->getMessage(),
+ ['exception' => $e]
+ );
+ }
+ }
+ // return mail provider collection
+ return $this->providersCollection;
+
+ }
+
+ /**
+ * Retrieve a provider with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $providerId provider id
+ *
+ * @return IProvider|null
+ */
+ public function findProviderById(string $providerId): ?IProvider {
+
+ // evaluate if we already have a cached collection of providers
+ if (!is_array($this->providersCollection)) {
+ $this->providers();
+ }
+
+ if (isset($this->providersCollection[$providerId])) {
+ return $this->providersCollection[$providerId];
+ }
+ // return null if provider was not found
+ return null;
+
+ }
+
+ /**
+ * Retrieve all services for all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ *
+ * @return array<string,array<string,IService>> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]]
+ */
+ public function services(string $userId): array {
+
+ // initilize collection
+ $services = [];
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $entry) {
+ // retrieve collection of services
+ $mailServices = $entry->listServices($userId);
+ // evaluate if mail services collection is not empty and add results to services collection
+ if (!empty($mailServices)) {
+ $services[$entry->id()] = $mailServices;
+ }
+ }
+ // return collection
+ return $services;
+
+ }
+
+ /**
+ * Retrieve a service with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $serviceId service id
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): ?IService {
+
+ // evaluate if provider id was specified
+ if ($providerId !== null) {
+ // find provider
+ $provider = $this->findProviderById($providerId);
+ // evaluate if provider was found
+ if ($provider instanceof IProvider) {
+ // find service with specific id
+ $service = $provider->findServiceById($userId, $serviceId);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ } else {
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $provider) {
+ // find service with specific id
+ $service = $provider->findServiceById($userId, $serviceId);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ }
+
+ // return null if no match was found
+ return null;
+
+ }
+
+ /**
+ * Retrieve a service for a specific mail address
+ * returns first service with specific primary address
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $address mail address (e.g. test@example.com)
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): ?IService {
+
+ // evaluate if provider id was specified
+ if ($providerId !== null) {
+ // find provider
+ $provider = $this->findProviderById($providerId);
+ // evaluate if provider was found
+ if ($provider instanceof IProvider) {
+ // find service with specific mail address
+ $service = $provider->findServiceByAddress($userId, $address);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ } else {
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $provider) {
+ // find service with specific mail address
+ $service = $provider->findServiceByAddress($userId, $address);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ }
+ // return null if no match was found
+ return null;
+
+ }
+}