PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/htdocs/wp-content/plugins/wp-mail-smtp/src/Providers/SMTPcom/Mailer.php

https://gitlab.com/VTTE/sitios-vtte
PHP | 460 lines | 241 code | 79 blank | 140 comment | 27 complexity | 2fd1249d6f8edc385796a03d59973804 MD5 | raw file
  1. <?php
  2. namespace WPMailSMTP\Providers\SMTPcom;
  3. use WPMailSMTP\Providers\MailerAbstract;
  4. use WPMailSMTP\WP;
  5. /**
  6. * Class Mailer for SMTP.com integration.
  7. *
  8. * @see https://www.smtp.com/smtp-api-documentation/ for the API documentation.
  9. *
  10. * @since 2.0.0
  11. */
  12. class Mailer extends MailerAbstract {
  13. /**
  14. * Which response code from HTTP provider is considered to be successful?
  15. *
  16. * @since 2.0.0
  17. *
  18. * @var int
  19. */
  20. protected $email_sent_code = 200;
  21. /**
  22. * URL to make an API request to.
  23. *
  24. * @since 2.0.0
  25. *
  26. * @var string
  27. */
  28. protected $url = 'https://api.smtp.com/v4/messages';
  29. /**
  30. * Mailer constructor.
  31. *
  32. * @since 2.0.0
  33. *
  34. * @param \WPMailSMTP\MailCatcher $phpmailer
  35. */
  36. public function __construct( $phpmailer ) {
  37. // We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
  38. parent::__construct( $phpmailer );
  39. // Set mailer specific headers.
  40. $this->set_header( 'Authorization', 'Bearer ' . $this->options->get( $this->mailer, 'api_key' ) );
  41. $this->set_header( 'Accept', 'application/json' );
  42. $this->set_header( 'content-type', 'application/json' );
  43. // Set mailer specific body parameters.
  44. $this->set_body_param(
  45. array(
  46. 'channel' => $this->options->get( $this->mailer, 'channel' ),
  47. )
  48. );
  49. }
  50. /**
  51. * Redefine the way email body is returned.
  52. * By default we are sending an array of data.
  53. * SMTP.com requires a JSON, so we encode the body.
  54. *
  55. * @since 2.0.0
  56. */
  57. public function get_body() {
  58. $body = parent::get_body();
  59. return wp_json_encode( $body );
  60. }
  61. /**
  62. * Define the FROM (name and email).
  63. *
  64. * @since 2.0.0
  65. *
  66. * @param string $email From Email address.
  67. * @param string $name From Name.
  68. */
  69. public function set_from( $email, $name = '' ) {
  70. if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
  71. return;
  72. }
  73. $from['address'] = $email;
  74. if ( ! empty( $name ) ) {
  75. $from['name'] = $name;
  76. }
  77. $this->set_body_param(
  78. array(
  79. 'originator' => array(
  80. 'from' => $from,
  81. ),
  82. )
  83. );
  84. }
  85. /**
  86. * Define the CC/BCC/TO (with names and emails).
  87. *
  88. * @since 2.0.0
  89. *
  90. * @param array $recipients
  91. */
  92. public function set_recipients( $recipients ) {
  93. if ( empty( $recipients ) ) {
  94. return;
  95. }
  96. // Allow only these recipient types.
  97. $allowed_types = array( 'to', 'cc', 'bcc' );
  98. $data = array();
  99. foreach ( $recipients as $type => $emails ) {
  100. if (
  101. ! in_array( $type, $allowed_types, true ) ||
  102. empty( $emails ) ||
  103. ! is_array( $emails )
  104. ) {
  105. continue;
  106. }
  107. $data[ $type ] = array();
  108. // Iterate over all emails for each type.
  109. // There might be multiple cc/to/bcc emails.
  110. foreach ( $emails as $email ) {
  111. $holder = array();
  112. $address = isset( $email[0] ) ? $email[0] : false;
  113. $name = isset( $email[1] ) ? $email[1] : false;
  114. if ( ! filter_var( $address, FILTER_VALIDATE_EMAIL ) ) {
  115. continue;
  116. }
  117. $holder['address'] = $address;
  118. if ( ! empty( $name ) ) {
  119. $holder['name'] = $name;
  120. }
  121. array_push( $data[ $type ], $holder );
  122. }
  123. }
  124. if ( ! empty( $data ) ) {
  125. $this->set_body_param(
  126. array(
  127. 'recipients' => $data,
  128. )
  129. );
  130. }
  131. }
  132. /**
  133. * Set the email content.
  134. *
  135. * @since 2.0.0
  136. *
  137. * @param array|string $content String when text/plain, array otherwise.
  138. */
  139. public function set_content( $content ) {
  140. if ( empty( $content ) ) {
  141. return;
  142. }
  143. $parts = array();
  144. if ( is_array( $content ) ) {
  145. $allowed = array( 'text', 'html' );
  146. foreach ( $content as $type => $body ) {
  147. if (
  148. ! in_array( $type, $allowed, true ) ||
  149. empty( $body )
  150. ) {
  151. continue;
  152. }
  153. $content_type = 'text/plain';
  154. $content_value = $body;
  155. if ( $type === 'html' ) {
  156. $content_type = 'text/html';
  157. }
  158. $parts[] = array(
  159. 'type' => $content_type,
  160. 'content' => $content_value,
  161. 'charset' => $this->phpmailer->CharSet,
  162. 'encoding' => $this->phpmailer->Encoding,
  163. );
  164. }
  165. } else {
  166. $content_type = 'text/html';
  167. $content_value = $content;
  168. if ( $this->phpmailer->ContentType === 'text/plain' ) {
  169. $content_type = 'text/plain';
  170. }
  171. $parts[] = array(
  172. 'type' => $content_type,
  173. 'content' => $content_value,
  174. 'charset' => $this->phpmailer->CharSet,
  175. 'encoding' => $this->phpmailer->Encoding,
  176. );
  177. }
  178. $this->set_body_param(
  179. array(
  180. 'body' => array(
  181. 'parts' => $parts,
  182. ),
  183. )
  184. );
  185. }
  186. /**
  187. * Redefine the way custom headers are processed for this mailer - they should be in body.
  188. *
  189. * @since 2.0.0
  190. *
  191. * @param array $headers
  192. */
  193. public function set_headers( $headers ) {
  194. foreach ( $headers as $header ) {
  195. $name = isset( $header[0] ) ? $header[0] : false;
  196. $value = isset( $header[1] ) ? $header[1] : false;
  197. $this->set_body_header( $name, $value );
  198. }
  199. // Add custom PHPMailer-specific header.
  200. $this->set_body_header( 'X-Mailer', 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
  201. }
  202. /**
  203. * This mailer supports email-related custom headers inside a body of the message.
  204. *
  205. * @since 2.0.0
  206. *
  207. * @param string $name
  208. * @param string $value
  209. */
  210. public function set_body_header( $name, $value ) {
  211. $name = sanitize_text_field( $name );
  212. if ( empty( $name ) ) {
  213. return;
  214. }
  215. $headers = isset( $this->body['custom_headers'] ) ? (array) $this->body['custom_headers'] : array();
  216. $headers[ $name ] = WP::sanitize_value( $value );
  217. $this->set_body_param(
  218. array(
  219. 'custom_headers' => $headers,
  220. )
  221. );
  222. }
  223. /**
  224. * SMTP.com accepts an array of attachments in body.attachments section of the JSON payload.
  225. *
  226. * @since 2.0.0
  227. *
  228. * @param array $attachments
  229. */
  230. public function set_attachments( $attachments ) {
  231. if ( empty( $attachments ) ) {
  232. return;
  233. }
  234. $data = array();
  235. foreach ( $attachments as $attachment ) {
  236. $file = false;
  237. /*
  238. * We are not using WP_Filesystem API as we can't reliably work with it.
  239. * It is not always available, same as credentials for FTP.
  240. */
  241. try {
  242. if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
  243. $file = file_get_contents( $attachment[0] ); // phpcs:ignore
  244. }
  245. }
  246. catch ( \Exception $e ) {
  247. $file = false;
  248. }
  249. if ( $file === false ) {
  250. continue;
  251. }
  252. $filetype = str_replace( ';', '', trim( $attachment[4] ) );
  253. $data[] = array(
  254. 'content' => base64_encode( $file ),
  255. 'type' => $filetype,
  256. 'encoding' => 'base64',
  257. 'filename' => empty( $attachment[2] ) ? 'file-' . wp_hash( microtime() ) . '.' . $filetype : trim( $attachment[2] ),
  258. 'disposition' => in_array( $attachment[6], array( 'inline', 'attachment' ), true ) ? $attachment[6] : 'attachment', // either inline or attachment.
  259. 'cid' => empty( $attachment[7] ) ? '' : trim( (string) $attachment[7] ),
  260. );
  261. }
  262. if ( ! empty( $data ) ) {
  263. $this->set_body_param(
  264. array(
  265. 'body' => array(
  266. 'attachments' => $data,
  267. ),
  268. )
  269. );
  270. }
  271. }
  272. /**
  273. * Set Reply-To part of the message.
  274. *
  275. * @since 2.0.0
  276. *
  277. * @param array $reply_to
  278. */
  279. public function set_reply_to( $reply_to ) {
  280. if ( empty( $reply_to ) ) {
  281. return;
  282. }
  283. $data = array();
  284. foreach ( $reply_to as $key => $emails ) {
  285. if (
  286. empty( $emails ) ||
  287. ! is_array( $emails )
  288. ) {
  289. continue;
  290. }
  291. $address = isset( $emails[0] ) ? $emails[0] : false;
  292. $name = isset( $emails[1] ) ? $emails[1] : false;
  293. if ( ! filter_var( $address, FILTER_VALIDATE_EMAIL ) ) {
  294. continue;
  295. }
  296. $data['address'] = $address;
  297. if ( ! empty( $name ) ) {
  298. $data['name'] = $name;
  299. }
  300. // Let the first valid email from the passed $reply_to serve as the reply_to parameter in STMP.com API.
  301. // Only one email address and name is allowed in the `reply_to` parameter in the SMTP.com API payload.
  302. break;
  303. }
  304. if ( ! empty( $data ) ) {
  305. $this->set_body_param(
  306. array(
  307. 'originator' => array(
  308. 'reply_to' => $data,
  309. ),
  310. )
  311. );
  312. }
  313. }
  314. /**
  315. * SMTP.com doesn't support return_path params.
  316. * So we do nothing.
  317. *
  318. * @since 2.0.0
  319. *
  320. * @param string $from_email
  321. */
  322. public function set_return_path( $from_email ) {}
  323. /**
  324. * Get a SMTP.com-specific response with a helpful error.
  325. *
  326. * SMTP.com API error response (non 200 error code responses) is:
  327. * {
  328. * "status": "fail",
  329. * "data": {
  330. * "error_key": "short error message",
  331. * }
  332. * }
  333. *
  334. * It's good to combine the error_key and the message together for the best error explanation.
  335. *
  336. * @since 2.0.0
  337. *
  338. * @return string
  339. */
  340. protected function get_response_error() {
  341. $body = (array) wp_remote_retrieve_body( $this->response );
  342. $error_text = array();
  343. if ( ! empty( $body['data'] ) ) {
  344. foreach ( (array) $body['data'] as $error_key => $error_message ) {
  345. $error_text[] = $error_key . ' - ' . $error_message;
  346. }
  347. }
  348. return implode( PHP_EOL, array_map( 'esc_textarea', $error_text ) );
  349. }
  350. /**
  351. * Get mailer debug information, that is helpful during support.
  352. *
  353. * @since 2.0.0
  354. *
  355. * @return string
  356. */
  357. public function get_debug_info() {
  358. $options = $this->options->get_group( $this->mailer );
  359. $text[] = '<strong>' . esc_html__( 'Api Key:', 'wp-mail-smtp' ) . '</strong> ' .
  360. ( ! empty( $options['api_key'] ) ? 'Yes' : 'No' );
  361. $text[] = '<strong>' . esc_html__( 'Channel:', 'wp-mail-smtp' ) . '</strong> ' .
  362. ( ! empty( $options['channel'] ) ? 'Yes' : 'No' );
  363. return implode( '<br>', $text );
  364. }
  365. /**
  366. * Whether the mailer has all its settings correctly set up and saved.
  367. *
  368. * This mailer is configured when `api_key` and `channel` settings are defined.
  369. *
  370. * @since 2.0.0
  371. *
  372. * @return bool
  373. */
  374. public function is_mailer_complete() {
  375. $options = $this->options->get_group( $this->mailer );
  376. if ( ! empty( $options['api_key'] ) && ! empty( $options['channel'] ) ) {
  377. return true;
  378. }
  379. return false;
  380. }
  381. }