/vendor/zendframework/zend-mail/src/Transport/Smtp.php

https://github.com/tmccormi/openemr · PHP · 381 lines · 184 code · 42 blank · 155 comment · 20 complexity · 8ac268d7dc551684894fc477050455d2 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Mail\Transport;
  10. use Zend\Mail\Address;
  11. use Zend\Mail\Headers;
  12. use Zend\Mail\Message;
  13. use Zend\Mail\Protocol;
  14. use Zend\Mail\Protocol\Exception as ProtocolException;
  15. use Zend\ServiceManager\ServiceManager;
  16. /**
  17. * SMTP connection object
  18. *
  19. * Loads an instance of Zend\Mail\Protocol\Smtp and forwards smtp transactions
  20. */
  21. class Smtp implements TransportInterface
  22. {
  23. /**
  24. * @var SmtpOptions
  25. */
  26. protected $options;
  27. /**
  28. * @var Envelope|null
  29. */
  30. protected $envelope;
  31. /**
  32. * @var Protocol\Smtp
  33. */
  34. protected $connection;
  35. /**
  36. * @var bool
  37. */
  38. protected $autoDisconnect = true;
  39. /**
  40. * @var Protocol\SmtpPluginManager
  41. */
  42. protected $plugins;
  43. /**
  44. * Constructor.
  45. *
  46. * @param SmtpOptions $options Optional
  47. */
  48. public function __construct(SmtpOptions $options = null)
  49. {
  50. if (! $options instanceof SmtpOptions) {
  51. $options = new SmtpOptions();
  52. }
  53. $this->setOptions($options);
  54. }
  55. /**
  56. * Set options
  57. *
  58. * @param SmtpOptions $options
  59. * @return Smtp
  60. */
  61. public function setOptions(SmtpOptions $options)
  62. {
  63. $this->options = $options;
  64. return $this;
  65. }
  66. /**
  67. * Get options
  68. *
  69. * @return SmtpOptions
  70. */
  71. public function getOptions()
  72. {
  73. return $this->options;
  74. }
  75. /**
  76. * Set options
  77. *
  78. * @param Envelope $envelope
  79. */
  80. public function setEnvelope(Envelope $envelope)
  81. {
  82. $this->envelope = $envelope;
  83. }
  84. /**
  85. * Get envelope
  86. *
  87. * @return Envelope|null
  88. */
  89. public function getEnvelope()
  90. {
  91. return $this->envelope;
  92. }
  93. /**
  94. * Set plugin manager for obtaining SMTP protocol connection
  95. *
  96. * @param Protocol\SmtpPluginManager $plugins
  97. * @throws Exception\InvalidArgumentException
  98. * @return Smtp
  99. */
  100. public function setPluginManager(Protocol\SmtpPluginManager $plugins)
  101. {
  102. $this->plugins = $plugins;
  103. return $this;
  104. }
  105. /**
  106. * Get plugin manager for loading SMTP protocol connection
  107. *
  108. * @return Protocol\SmtpPluginManager
  109. */
  110. public function getPluginManager()
  111. {
  112. if (null === $this->plugins) {
  113. $this->setPluginManager(new Protocol\SmtpPluginManager(new ServiceManager()));
  114. }
  115. return $this->plugins;
  116. }
  117. /**
  118. * Set the automatic disconnection when destruct
  119. *
  120. * @param bool $flag
  121. * @return Smtp
  122. */
  123. public function setAutoDisconnect($flag)
  124. {
  125. $this->autoDisconnect = (bool) $flag;
  126. return $this;
  127. }
  128. /**
  129. * Get the automatic disconnection value
  130. *
  131. * @return bool
  132. */
  133. public function getAutoDisconnect()
  134. {
  135. return $this->autoDisconnect;
  136. }
  137. /**
  138. * Return an SMTP connection
  139. *
  140. * @param string $name
  141. * @param array|null $options
  142. * @return Protocol\Smtp
  143. */
  144. public function plugin($name, array $options = null)
  145. {
  146. return $this->getPluginManager()->get($name, $options);
  147. }
  148. /**
  149. * Class destructor to ensure all open connections are closed
  150. */
  151. public function __destruct()
  152. {
  153. if ($this->connection instanceof Protocol\Smtp) {
  154. try {
  155. $this->connection->quit();
  156. } catch (ProtocolException\ExceptionInterface $e) {
  157. // ignore
  158. }
  159. if ($this->autoDisconnect) {
  160. $this->connection->disconnect();
  161. }
  162. }
  163. }
  164. /**
  165. * Sets the connection protocol instance
  166. *
  167. * @param Protocol\AbstractProtocol $connection
  168. */
  169. public function setConnection(Protocol\AbstractProtocol $connection)
  170. {
  171. $this->connection = $connection;
  172. }
  173. /**
  174. * Gets the connection protocol instance
  175. *
  176. * @return Protocol\Smtp
  177. */
  178. public function getConnection()
  179. {
  180. return $this->connection;
  181. }
  182. /**
  183. * Disconnect the connection protocol instance
  184. *
  185. * @return void
  186. */
  187. public function disconnect()
  188. {
  189. if (! empty($this->connection) && ($this->connection instanceof Protocol\Smtp)) {
  190. $this->connection->disconnect();
  191. }
  192. }
  193. /**
  194. * Send an email via the SMTP connection protocol
  195. *
  196. * The connection via the protocol adapter is made just-in-time to allow a
  197. * developer to add a custom adapter if required before mail is sent.
  198. *
  199. * @param Message $message
  200. * @throws Exception\RuntimeException
  201. */
  202. public function send(Message $message)
  203. {
  204. // If sending multiple messages per session use existing adapter
  205. $connection = $this->getConnection();
  206. if (! ($connection instanceof Protocol\Smtp) || ! $connection->hasSession()) {
  207. $connection = $this->connect();
  208. } else {
  209. // Reset connection to ensure reliable transaction
  210. $connection->rset();
  211. }
  212. // Prepare message
  213. $from = $this->prepareFromAddress($message);
  214. $recipients = $this->prepareRecipients($message);
  215. $headers = $this->prepareHeaders($message);
  216. $body = $this->prepareBody($message);
  217. if ((count($recipients) == 0) && (! empty($headers) || ! empty($body))) {
  218. // Per RFC 2821 3.3 (page 18)
  219. throw new Exception\RuntimeException(
  220. sprintf(
  221. '%s transport expects at least one recipient if the message has at least one header or body',
  222. __CLASS__
  223. )
  224. );
  225. }
  226. // Set sender email address
  227. $connection->mail($from);
  228. // Set recipient forward paths
  229. foreach ($recipients as $recipient) {
  230. $connection->rcpt($recipient);
  231. }
  232. // Issue DATA command to client
  233. $connection->data($headers . Headers::EOL . $body);
  234. }
  235. /**
  236. * Retrieve email address for envelope FROM
  237. *
  238. * @param Message $message
  239. * @throws Exception\RuntimeException
  240. * @return string
  241. */
  242. protected function prepareFromAddress(Message $message)
  243. {
  244. if ($this->getEnvelope() && $this->getEnvelope()->getFrom()) {
  245. return $this->getEnvelope()->getFrom();
  246. }
  247. $sender = $message->getSender();
  248. if ($sender instanceof Address\AddressInterface) {
  249. return $sender->getEmail();
  250. }
  251. $from = $message->getFrom();
  252. if (! count($from)) {
  253. // Per RFC 2822 3.6
  254. throw new Exception\RuntimeException(sprintf(
  255. '%s transport expects either a Sender or at least one From address in the Message; none provided',
  256. __CLASS__
  257. ));
  258. }
  259. $from->rewind();
  260. $sender = $from->current();
  261. return $sender->getEmail();
  262. }
  263. /**
  264. * Prepare array of email address recipients
  265. *
  266. * @param Message $message
  267. * @return array
  268. */
  269. protected function prepareRecipients(Message $message)
  270. {
  271. if ($this->getEnvelope() && $this->getEnvelope()->getTo()) {
  272. return (array) $this->getEnvelope()->getTo();
  273. }
  274. $recipients = [];
  275. foreach ($message->getTo() as $address) {
  276. $recipients[] = $address->getEmail();
  277. }
  278. foreach ($message->getCc() as $address) {
  279. $recipients[] = $address->getEmail();
  280. }
  281. foreach ($message->getBcc() as $address) {
  282. $recipients[] = $address->getEmail();
  283. }
  284. $recipients = array_unique($recipients);
  285. return $recipients;
  286. }
  287. /**
  288. * Prepare header string from message
  289. *
  290. * @param Message $message
  291. * @return string
  292. */
  293. protected function prepareHeaders(Message $message)
  294. {
  295. $headers = clone $message->getHeaders();
  296. $headers->removeHeader('Bcc');
  297. return $headers->toString();
  298. }
  299. /**
  300. * Prepare body string from message
  301. *
  302. * @param Message $message
  303. * @return string
  304. */
  305. protected function prepareBody(Message $message)
  306. {
  307. return $message->getBodyText();
  308. }
  309. /**
  310. * Lazy load the connection
  311. *
  312. * @return Protocol\Smtp
  313. */
  314. protected function lazyLoadConnection()
  315. {
  316. // Check if authentication is required and determine required class
  317. $options = $this->getOptions();
  318. $config = $options->getConnectionConfig();
  319. $config['host'] = $options->getHost();
  320. $config['port'] = $options->getPort();
  321. $connection = $this->plugin($options->getConnectionClass(), $config);
  322. $this->connection = $connection;
  323. return $this->connect();
  324. }
  325. /**
  326. * Connect the connection, and pass it helo
  327. *
  328. * @return Protocol\Smtp
  329. */
  330. protected function connect()
  331. {
  332. if (! $this->connection instanceof Protocol\Smtp) {
  333. return $this->lazyLoadConnection();
  334. }
  335. $this->connection->connect();
  336. $this->connection->helo($this->getOptions()->getName());
  337. return $this->connection;
  338. }
  339. }