PageRenderTime 37ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette.Addons/nSMTPMailer/nSMTPMailer/Commands/RcptCommand.php

https://code.google.com/
PHP | 283 lines | 105 code | 36 blank | 142 comment | 11 complexity | 64ba2aa5e5deeaf4cae9588d2cb0311d MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
  1. <?php
  2. namespace Nette\Mail\SMTPClient;
  3. /**
  4. * The command that adds a recipient to the mail message
  5. *
  6. * RFC DESCRIPTION:
  7. *
  8. * This command is used to identify an individual recipient of the mail
  9. * data; multiple recipients are specified by multiple use of this
  10. * command. The argument field contains a forward-path and may contain
  11. * optional parameters.
  12. *
  13. * The forward-path normally consists of the required destination
  14. * mailbox. Sending systems SHOULD not generate the optional list of
  15. * hosts known as a source route.
  16. *
  17. * 250 Requested mail action okay, completed
  18. * 251 User not local; will forward to <forward-path>
  19. *
  20. * 450 Requested mail action not taken: mailbox unavailable
  21. * (e.g., mailbox busy)
  22. * 451 Requested action aborted: local error in processing
  23. * 452 Requested action not taken: insufficient system storage
  24. *
  25. * 550 Requested action not taken: mailbox unavailable
  26. * (e.g., mailbox not found, no access, or command rejected
  27. * for policy reasons)
  28. * 551 User not local; please try <forward-path>
  29. * 552 Requested mail action aborted: exceeded storage allocation
  30. * 553 Requested action not taken: mailbox name not allowed
  31. * (e.g., mailbox syntax incorrect)
  32. * Copyright (c) 2009, Martin Pecka (peci1@seznam.cz)
  33. * All rights reserved.
  34. *
  35. * Redistribution and use in source and binary forms, with or without
  36. * modification, are permitted provided that the following conditions are met:
  37. * * Redistributions of source code must retain the above copyright
  38. * notice, this list of conditions and the following disclaimer.
  39. * * Redistributions in binary form must reproduce the above copyright
  40. * notice, this list of conditions and the following disclaimer in the
  41. * documentation and/or other materials provided with the distribution.
  42. * * Neither the name Martin Pecka nor the
  43. * names of contributors may be used to endorse or promote products
  44. * derived from this software without specific prior written permission.
  45. *
  46. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  47. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  48. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  49. * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  50. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  51. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  52. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  53. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  54. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  55. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  56. * @package nSMTPMailer
  57. * @version 1.0.0
  58. * @copyright (c) 2009 Martin Pecka (Clevis)
  59. * @author Martin Pecka <peci1@seznam.cz>
  60. * @license See license.txt
  61. */
  62. class /*Nette\Mail\SMTPClient\*/RcptCommand extends
  63. /*Nette\Mail\SMTPClient\*/BaseCommand
  64. {
  65. /** @var array of string|int Array of response code masks of codes that
  66. * should force the application to immediately close the connection
  67. * and quit
  68. * */
  69. protected $failResponses =
  70. array('450', '451', '452', '550', '551', '552', '553');
  71. protected $successResponses = array('250', '251');
  72. /** @var string Textual name of the command */
  73. protected $name = 'RCPT TO';
  74. /** @var string The command to send - if it is NULL, no command is sent
  75. * and the communicator just reads the server response */
  76. protected $command = 'RCPT TO: <%s>';
  77. /** @var array of array The recipients' email addresses
  78. * Structure of the array:
  79. * 'email' => The email address
  80. * 'retries' => The remaining # of retries to send
  81. * 'original_email' => The original email, if this is a forwarded
  82. * one */
  83. protected $recipients = array();
  84. /**
  85. * Execute the command, handle error states and throw InvalidStateException
  86. * if an unrecoverable error occurs. Should be overridden, but always call
  87. * parent::execute() !!!
  88. *
  89. * @return array of key=>val:
  90. * undelivered => Array of addresses the mail couldn't be delivered to
  91. * toDeliver => Array of internal format of the addresses not yet
  92. * tried to send the email to. If set, causes the client to repeat
  93. * the message sending procedure with these recipients (possible
  94. * infinte loop - be sure this array always gets smaller than
  95. * before)
  96. * recipientsCount => Number of the recipients the message will really
  97. * be sent to
  98. *
  99. * @throws InvalidStateException If an unrecoverable error occurs
  100. */
  101. public function execute()
  102. {
  103. $undeliveredRecipients = array();
  104. $recipientsCount = 0;
  105. while (count($this->recipients) >= 1) {
  106. //send the command and read the response
  107. parent::execute();
  108. $recipient = array_pop($this->recipients);
  109. switch ($this->response->getCode()) {
  110. case '250':
  111. case '251':
  112. if (isset($recipient['original_email'])) {
  113. //we finally delivered mail to a user that provided a
  114. //forward-address
  115. $key = array_search($recipient['original_email'],
  116. $undeliveredRecipients);
  117. if ($key !== FALSE)
  118. unset($undeliveredRecipients[$key]);
  119. }
  120. $recipientsCount++;
  121. break;
  122. case '450':
  123. case '451':
  124. //add the recipient to the begginning of the queue to try again
  125. //later
  126. if ($recipient['retries'] > 0) {
  127. $recipient['retries']--;
  128. array_unshift($this->recipients, $recipient);
  129. }
  130. break;
  131. /* Handle too much recipients by splitting the message to chunks
  132. *
  133. * RFC 821 [30] incorrectly listed the error where an SMTP server
  134. * exhausts its implementation limit on the number of RCPT commands
  135. * ("too many recipients") as having reply code 552. The correct
  136. * reply code for this condition is 452. Clients SHOULD treat a
  137. * 552 code in this case as a temporary, rather than permanent,
  138. * failure so the logic below works.
  139. */
  140. case '452':
  141. case '552':
  142. array_push($this->recipients, $recipient);
  143. $result['undelivered'] = $undeliveredRecipients;
  144. //this causes the client to append a new mail sending sequence
  145. //for the rest of recipients
  146. $result['toDeliver'] = $this->recipients;
  147. $result['recipientsCount'] = $recipientsCount;
  148. return $result;
  149. break; //unreachable
  150. case '550':
  151. case '553':
  152. $undeliveredRecipients[] = $recipient['email'];
  153. break;
  154. case '551':
  155. if (preg_match('/<\([^>]*\)>/', $this->response, $matches)) {
  156. $forward = array();
  157. $forward['email'] = $matches[1];
  158. $forward['retries'] = 2;
  159. if (isset($recipient['original_email'])) {
  160. $forward['original_email'] =
  161. $recipient['original_email'];
  162. } else {
  163. $forward['original_email'] = $recipient['email'];
  164. $undeliveredRecipients[] = $recipient['email'];
  165. }
  166. $this->recipients[] = $forward;
  167. }
  168. break;
  169. }
  170. }
  171. return array(
  172. 'undelivered' => $undeliveredRecipients,
  173. 'recipientsCount' => $recipientsCount,
  174. );
  175. }
  176. /**
  177. * Add a recipient's address
  178. *
  179. * @param string $recipient The recipient's address
  180. * @return void
  181. *
  182. * @throws InvalidArgumentException If the email address is invalid
  183. */
  184. public function addRecipient($recipient)
  185. {
  186. if (!$this->validateEmail($recipient))
  187. throw new InvalidArgumentException(
  188. 'The email address provided to RCPT TO was invalid.' .
  189. 'The address was: ' . $recipient);
  190. $this->recipients[] = array('email' => $recipient, 'retries' => 2);
  191. }
  192. /**
  193. * Add some recipients' addresses
  194. *
  195. * @param array of string $recipients The recipients' addresses
  196. *
  197. * @return array of string The recipients that have invalid address format
  198. */
  199. public function addRecipients(array $recipients)
  200. {
  201. $bad = array();
  202. if (!empty($recipients)) {
  203. foreach ($recipients as $recipient) {
  204. try {
  205. $this->addRecipient($recipient);
  206. } catch (InvalidArgumentException $e) {
  207. $bad[] = $recipient;
  208. }
  209. }
  210. }
  211. return $bad;
  212. }
  213. /**
  214. * Set the internal recipients array to the one given
  215. *
  216. * @remarks Used only for sending message chunks, do not use for
  217. * another purposes. The array must have the internal
  218. * structure.
  219. *
  220. * @param array of array $recipients
  221. * @return void
  222. */
  223. public function setRecipients(array $recipients)
  224. {
  225. $this->recipients = $recipients;
  226. }
  227. /**
  228. * Build the command string and return it (can be overriden)
  229. *
  230. * @return string The command to send to the server
  231. *
  232. * @throws InvalidStateException If no recipients are set
  233. */
  234. protected function buildCommand()
  235. {
  236. if (count($this->recipients) < 1)
  237. throw new InvalidStateException(
  238. 'Tried to send mail, but no recipients were set!');
  239. return sprintf($this->command,
  240. $this->recipients[count($this->recipients) - 1]['email']);
  241. }
  242. }
  243. /* ?> omitted intentionally */