Completed
Push — master ( a06075...35d43e )
by Victor
43:29 queued 43:09
created

Mailer::getMailInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Lukas Reschke <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OC\Mail;
23
24
use Egulias\EmailValidator\EmailValidator;
25
use Egulias\EmailValidator\Validation\RFCValidation;
26
use OCP\IConfig;
27
use OCP\Mail\IMailer;
28
use OCP\ILogger;
29
30
/**
31
 * Class Mailer provides some basic functions to create a mail message that can be used in combination with
32
 * \OC\Mail\Message.
33
 *
34
 * Example usage:
35
 *
36
 * 	$mailer = \OC::$server->getMailer();
37
 * 	$message = $mailer->createMessage();
38
 * 	$message->setSubject('Your Subject');
39
 * 	$message->setFrom(array('[email protected]' => 'ownCloud Notifier');
40
 * 	$message->setTo(array('[email protected]' => 'Recipient');
41
 * 	$message->setBody('The message text');
42
 * 	$mailer->send($message);
43
 *
44
 * This message can then be passed to send() of \OC\Mail\Mailer
45
 *
46
 * @package OC\Mail
47
 */
48
class Mailer implements IMailer {
49
	/** @var \Swift_SmtpTransport|\Swift_SendmailTransport Cached transport */
50
	private $instance = null;
51
	/** @var IConfig */
52
	private $config;
53
	/** @var ILogger */
54
	private $logger;
55
	/** @var \OC_Defaults */
56
	private $defaults;
57
58
	/**
59
	 * @param IConfig $config
60
	 * @param ILogger $logger
61
	 * @param \OC_Defaults $defaults
62
	 */
63
	public function __construct(IConfig $config,
64
						 ILogger $logger,
65
						 \OC_Defaults $defaults) {
66
		$this->config = $config;
67
		$this->logger = $logger;
68
		$this->defaults = $defaults;
69
	}
70
71
	/**
72
	 * Creates a new message object that can be passed to send()
73
	 *
74
	 * @return Message
75
	 */
76
	public function createMessage() {
77
		return new Message(new \Swift_Message());
78
	}
79
80
	/**
81
	 * Send the specified message. Also sets the from address to the value defined in config.php
82
	 * if no-one has been passed.
83
	 *
84
	 * @param Message $message Message to send
85
	 * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and
86
	 * therefore should be considered
87
	 * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address
88
	 * has been supplied.)
89
	 */
90
	public function send(Message $message) {
91
		$debugMode = $this->config->getSystemValue('mail_smtpdebug', false);
92
93
		if (!\is_array($message->getFrom()) || \count($message->getFrom()) === 0) {
94
			$message->setFrom([\OCP\Util::getDefaultEmailAddress($this->defaults->getName())]);
95
		}
96
97
		$failedRecipients = [];
98
99
		$mailer = $this->getInstance();
100
101
		$mailer->send($message->getSwiftMessage(), $failedRecipients);
102
103
		$allRecipients = [];
104
		if (!empty($message->getTo())) {
105
			$allRecipients = \array_merge($allRecipients, $message->getTo());
106
		}
107
		if (!empty($message->getCc())) {
108
			$allRecipients = \array_merge($allRecipients, $message->getCc());
109
		}
110
		if (!empty($message->getBcc())) {
111
			$allRecipients = \array_merge($allRecipients, $message->getBcc());
112
		}
113
114
		// Debugging logging
115
		$logMessage = 'Sent mail from "{from}" to "{recipients}" with subject "{subject}"';
116
		$this->logger->debug($logMessage, [
117
			'app' => 'core',
118
			'from' => \json_encode($message->getFrom()),
119
			'recipients' => \json_encode($allRecipients),
120
			'subject' => $message->getSubject()
121
		]);
122
123
		return $failedRecipients;
124
	}
125
126
	/**
127
	 * Checks if an e-mail address is valid
128
	 *
129
	 * @param string $email Email address to be validated
130
	 * @return bool True if the mail address is valid, false otherwise
131
	 */
132
	public function validateMailAddress($email) {
133
		$validator = new EmailValidator();
134
		return $validator->isValid($this->convertEmail($email), new RFCValidation());
135
	}
136
137
	/**
138
	 * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains
139
	 *
140
	 * FIXME: Remove this once SwiftMailer supports IDN
141
	 *
142
	 * @param string $email
143
	 * @return string Converted mail address if `idn_to_ascii` exists
144
	 */
145
	protected function convertEmail($email) {
146
		if (!\function_exists('idn_to_ascii') || \strpos($email, '@') === false) {
147
			return $email;
148
		}
149
150
		list($name, $domain) = \explode('@', $email, 2);
151 View Code Duplication
		if (\defined('INTL_IDNA_VARIANT_UTS46')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
			$domain = \idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
153
		} else {
154
			$domain = \idn_to_ascii($domain);
155
		}
156
		return $name.'@'.$domain;
157
	}
158
159
	/**
160
	 * Returns whatever transport is configured within the config
161
	 *
162
	 * @return \Swift_SmtpTransport|\Swift_SendmailTransport
163
	 */
164
	protected function getInstance() {
165
		if ($this->instance !== null) {
166
			return $this->instance;
167
		}
168
169
		$mailMode = $this->config->getSystemValue('mail_smtpmode', 'php');
170
		if ($mailMode === 'smtp') {
171
			$instance = $this->getSmtpInstance();
172
		} else {
173
			// FIXME: Move into the return statement but requires proper testing
174
			//       for SMTP and mail as well. Thus not really doable for a
175
			//       minor release.
176
			$instance = new \Swift_Mailer($this->getSendMailInstance());
177
		}
178
179
		// Register plugins
180
181
		// Enable logger if debug mode is enabled
182
		if ($this->config->getSystemValue('mail_smtpdebug', false)) {
183
			$mailLogger = new \Swift_Plugins_Loggers_ArrayLogger();
184
			$instance->registerPlugin(new \Swift_Plugins_LoggerPlugin($mailLogger));
185
		}
186
187
		// Enable antiflood on smtp connection (defaults to 100 mails before reconnect)
188
		$instance->registerPlugin(new \Swift_Plugins_AntiFloodPlugin());
189
190
		$this->instance = $instance;
0 ignored issues
show
Documentation Bug introduced by
It seems like $instance can also be of type object<Swift_Mailer>. However, the property $instance is declared as type object<Swift_SmtpTranspo...wift_SendmailTransport>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
191
192
		return $this->instance;
193
	}
194
195
	/**
196
	 * Returns the SMTP transport
197
	 *
198
	 * @return \Swift_SmtpTransport
199
	 */
200
	protected function getSmtpInstance() {
201
		$transport = new \Swift_SmtpTransport();
202
		$transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
203
		$transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1'));
204
		$transport->setPort($this->config->getSystemValue('mail_smtpport', 25));
205
		if ($this->config->getSystemValue('mail_smtpauth', false)) {
206
			$transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
207
			$transport->setPassword($this->config->getSystemValue('mail_smtppassword', ''));
208
			$transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN'));
209
		}
210
		$smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', '');
211
		if (!empty($smtpSecurity)) {
212
			$transport->setEncryption($smtpSecurity);
213
		}
214
		$transport->start();
215
		return $transport;
216
	}
217
218
	/**
219
	 * Returns the sendmail transport
220
	 *
221
	 * @return \Swift_SendmailTransport
222
	 */
223
	protected function getSendMailInstance() {
224
		switch ($this->config->getSystemValue('mail_smtpmode', 'sendmail')) {
225
			case 'qmail':
226
				$binaryPath = '/var/qmail/bin/sendmail';
227
				break;
228
			default:
229
				$binaryPath = '/usr/sbin/sendmail';
230
				break;
231
		}
232
233
		return new \Swift_SendmailTransport($binaryPath . ' -bs');
234
	}
235
}
236