You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Mailer.php 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2016, ownCloud, Inc.
  5. *
  6. * @author Arne Hamann <kontakt+github@arne.email>
  7. * @author Branko Kokanovic <branko@kokanovic.org>
  8. * @author Carsten Wiedmann <carsten_sttgt@gmx.de>
  9. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  10. * @author Jared Boone <jared.boone@gmail.com>
  11. * @author Joas Schilling <coding@schilljs.com>
  12. * @author Julius Härtl <jus@bitgrid.net>
  13. * @author kevin147147 <kevintamool@gmail.com>
  14. * @author Lukas Reschke <lukas@statuscode.ch>
  15. * @author Morris Jobke <hey@morrisjobke.de>
  16. * @author Roeland Jago Douma <roeland@famdouma.nl>
  17. * @author Tekhnee <info@tekhnee.org>
  18. *
  19. * @license AGPL-3.0
  20. *
  21. * This code is free software: you can redistribute it and/or modify
  22. * it under the terms of the GNU Affero General Public License, version 3,
  23. * as published by the Free Software Foundation.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU Affero General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU Affero General Public License, version 3,
  31. * along with this program. If not, see <http://www.gnu.org/licenses/>
  32. *
  33. */
  34. namespace OC\Mail;
  35. use Egulias\EmailValidator\EmailValidator;
  36. use Egulias\EmailValidator\Validation\RFCValidation;
  37. use OCP\Defaults;
  38. use OCP\EventDispatcher\IEventDispatcher;
  39. use OCP\IConfig;
  40. use OCP\IL10N;
  41. use OCP\ILogger;
  42. use OCP\IURLGenerator;
  43. use OCP\L10N\IFactory;
  44. use OCP\Mail\Events\BeforeMessageSent;
  45. use OCP\Mail\IAttachment;
  46. use OCP\Mail\IEMailTemplate;
  47. use OCP\Mail\IMailer;
  48. use OCP\Mail\IMessage;
  49. /**
  50. * Class Mailer provides some basic functions to create a mail message that can be used in combination with
  51. * \OC\Mail\Message.
  52. *
  53. * Example usage:
  54. *
  55. * $mailer = \OC::$server->getMailer();
  56. * $message = $mailer->createMessage();
  57. * $message->setSubject('Your Subject');
  58. * $message->setFrom(array('cloud@domain.org' => 'ownCloud Notifier'));
  59. * $message->setTo(array('recipient@domain.org' => 'Recipient'));
  60. * $message->setBody('The message text', 'text/html');
  61. * $mailer->send($message);
  62. *
  63. * This message can then be passed to send() of \OC\Mail\Mailer
  64. *
  65. * @package OC\Mail
  66. */
  67. class Mailer implements IMailer {
  68. /** @var \Swift_Mailer Cached mailer */
  69. private $instance = null;
  70. /** @var IConfig */
  71. private $config;
  72. /** @var ILogger */
  73. private $logger;
  74. /** @var Defaults */
  75. private $defaults;
  76. /** @var IURLGenerator */
  77. private $urlGenerator;
  78. /** @var IL10N */
  79. private $l10n;
  80. /** @var IEventDispatcher */
  81. private $dispatcher;
  82. /** @var IFactory */
  83. private $l10nFactory;
  84. /**
  85. * @param IConfig $config
  86. * @param ILogger $logger
  87. * @param Defaults $defaults
  88. * @param IURLGenerator $urlGenerator
  89. * @param IL10N $l10n
  90. * @param IEventDispatcher $dispatcher
  91. */
  92. public function __construct(IConfig $config,
  93. ILogger $logger,
  94. Defaults $defaults,
  95. IURLGenerator $urlGenerator,
  96. IL10N $l10n,
  97. IEventDispatcher $dispatcher,
  98. IFactory $l10nFactory) {
  99. $this->config = $config;
  100. $this->logger = $logger;
  101. $this->defaults = $defaults;
  102. $this->urlGenerator = $urlGenerator;
  103. $this->l10n = $l10n;
  104. $this->dispatcher = $dispatcher;
  105. $this->l10nFactory = $l10nFactory;
  106. }
  107. /**
  108. * Creates a new message object that can be passed to send()
  109. *
  110. * @return IMessage
  111. */
  112. public function createMessage(): IMessage {
  113. $plainTextOnly = $this->config->getSystemValue('mail_send_plaintext_only', false);
  114. return new Message(new \Swift_Message(), $plainTextOnly);
  115. }
  116. /**
  117. * @param string|null $data
  118. * @param string|null $filename
  119. * @param string|null $contentType
  120. * @return IAttachment
  121. * @since 13.0.0
  122. */
  123. public function createAttachment($data = null, $filename = null, $contentType = null): IAttachment {
  124. return new Attachment(new \Swift_Attachment($data, $filename, $contentType));
  125. }
  126. /**
  127. * @param string $path
  128. * @param string|null $contentType
  129. * @return IAttachment
  130. * @since 13.0.0
  131. */
  132. public function createAttachmentFromPath(string $path, $contentType = null): IAttachment {
  133. return new Attachment(\Swift_Attachment::fromPath($path, $contentType));
  134. }
  135. /**
  136. * Creates a new email template object
  137. *
  138. * @param string $emailId
  139. * @param array $data
  140. * @return IEMailTemplate
  141. * @since 12.0.0
  142. */
  143. public function createEMailTemplate(string $emailId, array $data = []): IEMailTemplate {
  144. $class = $this->config->getSystemValue('mail_template_class', '');
  145. if ($class !== '' && class_exists($class) && is_a($class, EMailTemplate::class, true)) {
  146. return new $class(
  147. $this->defaults,
  148. $this->urlGenerator,
  149. $this->l10nFactory,
  150. $emailId,
  151. $data
  152. );
  153. }
  154. return new EMailTemplate(
  155. $this->defaults,
  156. $this->urlGenerator,
  157. $this->l10nFactory,
  158. $emailId,
  159. $data
  160. );
  161. }
  162. /**
  163. * Send the specified message. Also sets the from address to the value defined in config.php
  164. * if no-one has been passed.
  165. *
  166. * @param IMessage|Message $message Message to send
  167. * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and
  168. * therefore should be considered
  169. * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address
  170. * has been supplied.)
  171. */
  172. public function send(IMessage $message): array {
  173. $debugMode = $this->config->getSystemValue('mail_smtpdebug', false);
  174. if (empty($message->getFrom())) {
  175. $message->setFrom([\OCP\Util::getDefaultEmailAddress('no-reply') => $this->defaults->getName()]);
  176. }
  177. $failedRecipients = [];
  178. $mailer = $this->getInstance();
  179. // Enable logger if debug mode is enabled
  180. if ($debugMode) {
  181. $mailLogger = new \Swift_Plugins_Loggers_ArrayLogger();
  182. $mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($mailLogger));
  183. }
  184. $this->dispatcher->dispatchTyped(new BeforeMessageSent($message));
  185. $mailer->send($message->getSwiftMessage(), $failedRecipients);
  186. // Debugging logging
  187. $logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
  188. $this->logger->debug($logMessage, ['app' => 'core']);
  189. if ($debugMode && isset($mailLogger)) {
  190. $this->logger->debug($mailLogger->dump(), ['app' => 'core']);
  191. }
  192. return $failedRecipients;
  193. }
  194. /**
  195. * Checks if an e-mail address is valid
  196. *
  197. * @param string $email Email address to be validated
  198. * @return bool True if the mail address is valid, false otherwise
  199. */
  200. public function validateMailAddress(string $email): bool {
  201. if ($email === '') {
  202. // Shortcut: empty addresses are never valid
  203. return false;
  204. }
  205. $validator = new EmailValidator();
  206. $validation = new RFCValidation();
  207. return $validator->isValid($this->convertEmail($email), $validation);
  208. }
  209. /**
  210. * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains
  211. *
  212. * FIXME: Remove this once SwiftMailer supports IDN
  213. *
  214. * @param string $email
  215. * @return string Converted mail address if `idn_to_ascii` exists
  216. */
  217. protected function convertEmail(string $email): string {
  218. if (!function_exists('idn_to_ascii') || !defined('INTL_IDNA_VARIANT_UTS46') || strpos($email, '@') === false) {
  219. return $email;
  220. }
  221. [$name, $domain] = explode('@', $email, 2);
  222. $domain = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
  223. return $name.'@'.$domain;
  224. }
  225. protected function getInstance(): \Swift_Mailer {
  226. if (!is_null($this->instance)) {
  227. return $this->instance;
  228. }
  229. $transport = null;
  230. switch ($this->config->getSystemValue('mail_smtpmode', 'smtp')) {
  231. case 'sendmail':
  232. $transport = $this->getSendMailInstance();
  233. break;
  234. case 'smtp':
  235. default:
  236. $transport = $this->getSmtpInstance();
  237. break;
  238. }
  239. return new \Swift_Mailer($transport);
  240. }
  241. /**
  242. * Returns the SMTP transport
  243. *
  244. * @return \Swift_SmtpTransport
  245. */
  246. protected function getSmtpInstance(): \Swift_SmtpTransport {
  247. $transport = new \Swift_SmtpTransport();
  248. $transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
  249. $transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1'));
  250. $transport->setPort($this->config->getSystemValue('mail_smtpport', 25));
  251. if ($this->config->getSystemValue('mail_smtpauth', false)) {
  252. $transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
  253. $transport->setPassword($this->config->getSystemValue('mail_smtppassword', ''));
  254. $transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN'));
  255. }
  256. $smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', '');
  257. if (!empty($smtpSecurity)) {
  258. $transport->setEncryption($smtpSecurity);
  259. }
  260. $streamingOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []);
  261. if (is_array($streamingOptions) && !empty($streamingOptions)) {
  262. $transport->setStreamOptions($streamingOptions);
  263. }
  264. $overwriteCliUrl = parse_url(
  265. $this->config->getSystemValueString('overwrite.cli.url', ''),
  266. PHP_URL_HOST
  267. );
  268. if (!empty($overwriteCliUrl)) {
  269. $transport->setLocalDomain($overwriteCliUrl);
  270. }
  271. return $transport;
  272. }
  273. /**
  274. * Returns the sendmail transport
  275. *
  276. * @return \Swift_SendmailTransport
  277. */
  278. protected function getSendMailInstance(): \Swift_SendmailTransport {
  279. switch ($this->config->getSystemValue('mail_smtpmode', 'smtp')) {
  280. case 'qmail':
  281. $binaryPath = '/var/qmail/bin/sendmail';
  282. break;
  283. default:
  284. $sendmail = \OC_Helper::findBinaryPath('sendmail');
  285. if ($sendmail === null) {
  286. $sendmail = '/usr/sbin/sendmail';
  287. }
  288. $binaryPath = $sendmail;
  289. break;
  290. }
  291. switch ($this->config->getSystemValue('mail_sendmailmode', 'smtp')) {
  292. case 'pipe':
  293. $binaryParam = ' -t';
  294. break;
  295. default:
  296. $binaryParam = ' -bs';
  297. break;
  298. }
  299. return new \Swift_SendmailTransport($binaryPath . $binaryParam);
  300. }
  301. }