mail.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. class WPCF7_Mail {
  3. private static $current = null;
  4. private $name = '';
  5. private $locale = '';
  6. private $template = array();
  7. private $use_html = false;
  8. private $exclude_blank = false;
  9. public static function get_current() {
  10. return self::$current;
  11. }
  12. public static function send( $template, $name = '' ) {
  13. self::$current = new self( $name, $template );
  14. return self::$current->compose();
  15. }
  16. private function __construct( $name, $template ) {
  17. $this->name = trim( $name );
  18. $this->use_html = ! empty( $template['use_html'] );
  19. $this->exclude_blank = ! empty( $template['exclude_blank'] );
  20. $this->template = wp_parse_args( $template, array(
  21. 'subject' => '',
  22. 'sender' => '',
  23. 'body' => '',
  24. 'recipient' => '',
  25. 'additional_headers' => '',
  26. 'attachments' => '',
  27. ) );
  28. if ( $submission = WPCF7_Submission::get_instance() ) {
  29. $contact_form = $submission->get_contact_form();
  30. $this->locale = $contact_form->locale();
  31. }
  32. }
  33. public function name() {
  34. return $this->name;
  35. }
  36. public function get( $component, $replace_tags = false ) {
  37. $use_html = ( $this->use_html && 'body' == $component );
  38. $exclude_blank = ( $this->exclude_blank && 'body' == $component );
  39. $template = $this->template;
  40. $component = isset( $template[$component] ) ? $template[$component] : '';
  41. if ( $replace_tags ) {
  42. $component = $this->replace_tags( $component, array(
  43. 'html' => $use_html,
  44. 'exclude_blank' => $exclude_blank,
  45. ) );
  46. if ( $use_html
  47. and ! preg_match( '%<html[>\s].*</html>%is', $component ) ) {
  48. $component = $this->htmlize( $component );
  49. }
  50. }
  51. return $component;
  52. }
  53. private function htmlize( $body ) {
  54. if ( $this->locale ) {
  55. $lang_atts = sprintf( ' %s',
  56. wpcf7_format_atts( array(
  57. 'dir' => wpcf7_is_rtl( $this->locale ) ? 'rtl' : 'ltr',
  58. 'lang' => str_replace( '_', '-', $this->locale ),
  59. ) )
  60. );
  61. } else {
  62. $lang_atts = '';
  63. }
  64. $header = apply_filters( 'wpcf7_mail_html_header',
  65. '<!doctype html>
  66. <html xmlns="http://www.w3.org/1999/xhtml"' . $lang_atts . '>
  67. <head>
  68. <title>' . esc_html( $this->get( 'subject', true ) ) . '</title>
  69. </head>
  70. <body>
  71. ', $this );
  72. $footer = apply_filters( 'wpcf7_mail_html_footer',
  73. '</body>
  74. </html>', $this );
  75. $html = $header . wpautop( $body ) . $footer;
  76. return $html;
  77. }
  78. private function compose( $send = true ) {
  79. $components = array(
  80. 'subject' => $this->get( 'subject', true ),
  81. 'sender' => $this->get( 'sender', true ),
  82. 'body' => $this->get( 'body', true ),
  83. 'recipient' => $this->get( 'recipient', true ),
  84. 'additional_headers' => $this->get( 'additional_headers', true ),
  85. 'attachments' => $this->attachments(),
  86. );
  87. $components = apply_filters( 'wpcf7_mail_components',
  88. $components, wpcf7_get_current_contact_form(), $this );
  89. if ( ! $send ) {
  90. return $components;
  91. }
  92. $subject = wpcf7_strip_newline( $components['subject'] );
  93. $sender = wpcf7_strip_newline( $components['sender'] );
  94. $recipient = wpcf7_strip_newline( $components['recipient'] );
  95. $body = $components['body'];
  96. $additional_headers = trim( $components['additional_headers'] );
  97. $attachments = $components['attachments'];
  98. $headers = "From: $sender\n";
  99. if ( $this->use_html ) {
  100. $headers .= "Content-Type: text/html\n";
  101. $headers .= "X-WPCF7-Content-Type: text/html\n";
  102. } else {
  103. $headers .= "X-WPCF7-Content-Type: text/plain\n";
  104. }
  105. if ( $additional_headers ) {
  106. $headers .= $additional_headers . "\n";
  107. }
  108. return wp_mail( $recipient, $subject, $body, $headers, $attachments );
  109. }
  110. public function replace_tags( $content, $args = '' ) {
  111. if ( true === $args ) {
  112. $args = array( 'html' => true );
  113. }
  114. $args = wp_parse_args( $args, array(
  115. 'html' => false,
  116. 'exclude_blank' => false,
  117. ) );
  118. return wpcf7_mail_replace_tags( $content, $args );
  119. }
  120. private function attachments( $template = null ) {
  121. if ( ! $template ) {
  122. $template = $this->get( 'attachments' );
  123. }
  124. $attachments = array();
  125. if ( $submission = WPCF7_Submission::get_instance() ) {
  126. $uploaded_files = $submission->uploaded_files();
  127. foreach ( (array) $uploaded_files as $name => $path ) {
  128. if ( false !== strpos( $template, "[${name}]" )
  129. and ! empty( $path ) ) {
  130. $attachments[] = $path;
  131. }
  132. }
  133. }
  134. foreach ( explode( "\n", $template ) as $line ) {
  135. $line = trim( $line );
  136. if ( '[' == substr( $line, 0, 1 ) ) {
  137. continue;
  138. }
  139. $path = path_join( WP_CONTENT_DIR, $line );
  140. if ( ! wpcf7_is_file_path_in_content_dir( $path ) ) {
  141. // $path is out of WP_CONTENT_DIR
  142. continue;
  143. }
  144. if ( is_readable( $path )
  145. and is_file( $path ) ) {
  146. $attachments[] = $path;
  147. }
  148. }
  149. return $attachments;
  150. }
  151. }
  152. function wpcf7_mail_replace_tags( $content, $args = '' ) {
  153. $args = wp_parse_args( $args, array(
  154. 'html' => false,
  155. 'exclude_blank' => false,
  156. ) );
  157. if ( is_array( $content ) ) {
  158. foreach ( $content as $key => $value ) {
  159. $content[$key] = wpcf7_mail_replace_tags( $value, $args );
  160. }
  161. return $content;
  162. }
  163. $content = explode( "\n", $content );
  164. foreach ( $content as $num => $line ) {
  165. $line = new WPCF7_MailTaggedText( $line, $args );
  166. $replaced = $line->replace_tags();
  167. if ( $args['exclude_blank'] ) {
  168. $replaced_tags = $line->get_replaced_tags();
  169. if ( empty( $replaced_tags )
  170. or array_filter( $replaced_tags, 'strlen' ) ) {
  171. $content[$num] = $replaced;
  172. } else {
  173. unset( $content[$num] ); // Remove a line.
  174. }
  175. } else {
  176. $content[$num] = $replaced;
  177. }
  178. }
  179. $content = implode( "\n", $content );
  180. return $content;
  181. }
  182. add_action( 'phpmailer_init', 'wpcf7_phpmailer_init', 10, 1 );
  183. function wpcf7_phpmailer_init( $phpmailer ) {
  184. $custom_headers = $phpmailer->getCustomHeaders();
  185. $phpmailer->clearCustomHeaders();
  186. $wpcf7_content_type = false;
  187. foreach ( (array) $custom_headers as $custom_header ) {
  188. $name = $custom_header[0];
  189. $value = $custom_header[1];
  190. if ( 'X-WPCF7-Content-Type' === $name ) {
  191. $wpcf7_content_type = trim( $value );
  192. } else {
  193. $phpmailer->addCustomHeader( $name, $value );
  194. }
  195. }
  196. if ( 'text/html' === $wpcf7_content_type ) {
  197. $phpmailer->msgHTML( $phpmailer->Body );
  198. } elseif ( 'text/plain' === $wpcf7_content_type ) {
  199. $phpmailer->AltBody = '';
  200. }
  201. }
  202. class WPCF7_MailTaggedText {
  203. private $html = false;
  204. private $callback = null;
  205. private $content = '';
  206. private $replaced_tags = array();
  207. public function __construct( $content, $args = '' ) {
  208. $args = wp_parse_args( $args, array(
  209. 'html' => false,
  210. 'callback' => null,
  211. ) );
  212. $this->html = (bool) $args['html'];
  213. if ( null !== $args['callback']
  214. and is_callable( $args['callback'] ) ) {
  215. $this->callback = $args['callback'];
  216. } elseif ( $this->html ) {
  217. $this->callback = array( $this, 'replace_tags_callback_html' );
  218. } else {
  219. $this->callback = array( $this, 'replace_tags_callback' );
  220. }
  221. $this->content = $content;
  222. }
  223. public function get_replaced_tags() {
  224. return $this->replaced_tags;
  225. }
  226. public function replace_tags() {
  227. $regex = '/(\[?)\[[\t ]*'
  228. . '([a-zA-Z_][0-9a-zA-Z:._-]*)' // [2] = name
  229. . '((?:[\t ]+"[^"]*"|[\t ]+\'[^\']*\')*)' // [3] = values
  230. . '[\t ]*\](\]?)/';
  231. return preg_replace_callback( $regex, $this->callback, $this->content );
  232. }
  233. private function replace_tags_callback_html( $matches ) {
  234. return $this->replace_tags_callback( $matches, true );
  235. }
  236. private function replace_tags_callback( $matches, $html = false ) {
  237. // allow [[foo]] syntax for escaping a tag
  238. if ( $matches[1] == '['
  239. and $matches[4] == ']' ) {
  240. return substr( $matches[0], 1, -1 );
  241. }
  242. $tag = $matches[0];
  243. $tagname = $matches[2];
  244. $values = $matches[3];
  245. $mail_tag = new WPCF7_MailTag( $tag, $tagname, $values );
  246. $field_name = $mail_tag->field_name();
  247. $submission = WPCF7_Submission::get_instance();
  248. $submitted = $submission
  249. ? $submission->get_posted_data( $field_name )
  250. : null;
  251. if ( null !== $submitted ) {
  252. if ( $mail_tag->get_option( 'do_not_heat' ) ) {
  253. $submitted = isset( $_POST[$field_name] ) ? $_POST[$field_name] : '';
  254. }
  255. $replaced = $submitted;
  256. if ( $format = $mail_tag->get_option( 'format' ) ) {
  257. $replaced = $this->format( $replaced, $format );
  258. }
  259. $replaced = wpcf7_flat_join( $replaced );
  260. if ( $html ) {
  261. $replaced = esc_html( $replaced );
  262. $replaced = wptexturize( $replaced );
  263. }
  264. if ( $form_tag = $mail_tag->corresponding_form_tag() ) {
  265. $type = $form_tag->type;
  266. $replaced = apply_filters(
  267. "wpcf7_mail_tag_replaced_{$type}", $replaced,
  268. $submitted, $html, $mail_tag );
  269. }
  270. $replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced,
  271. $submitted, $html, $mail_tag );
  272. $replaced = wp_unslash( trim( $replaced ) );
  273. $this->replaced_tags[$tag] = $replaced;
  274. return $replaced;
  275. }
  276. $special = apply_filters( 'wpcf7_special_mail_tags', null,
  277. $mail_tag->tag_name(), $html, $mail_tag );
  278. if ( null !== $special ) {
  279. $this->replaced_tags[$tag] = $special;
  280. return $special;
  281. }
  282. return $tag;
  283. }
  284. public function format( $original, $format ) {
  285. $original = (array) $original;
  286. foreach ( $original as $key => $value ) {
  287. if ( preg_match( '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $value ) ) {
  288. $original[$key] = mysql2date( $format, $value );
  289. }
  290. }
  291. return $original;
  292. }
  293. }
  294. class WPCF7_MailTag {
  295. private $tag;
  296. private $tagname = '';
  297. private $name = '';
  298. private $options = array();
  299. private $values = array();
  300. private $form_tag = null;
  301. public function __construct( $tag, $tagname, $values ) {
  302. $this->tag = $tag;
  303. $this->name = $this->tagname = $tagname;
  304. $this->options = array(
  305. 'do_not_heat' => false,
  306. 'format' => '',
  307. );
  308. if ( ! empty( $values ) ) {
  309. preg_match_all( '/"[^"]*"|\'[^\']*\'/', $values, $matches );
  310. $this->values = wpcf7_strip_quote_deep( $matches[0] );
  311. }
  312. if ( preg_match( '/^_raw_(.+)$/', $tagname, $matches ) ) {
  313. $this->name = trim( $matches[1] );
  314. $this->options['do_not_heat'] = true;
  315. }
  316. if ( preg_match( '/^_format_(.+)$/', $tagname, $matches ) ) {
  317. $this->name = trim( $matches[1] );
  318. $this->options['format'] = $this->values[0];
  319. }
  320. }
  321. public function tag_name() {
  322. return $this->tagname;
  323. }
  324. public function field_name() {
  325. return $this->name;
  326. }
  327. public function get_option( $option ) {
  328. return $this->options[$option];
  329. }
  330. public function values() {
  331. return $this->values;
  332. }
  333. public function corresponding_form_tag() {
  334. if ( $this->form_tag instanceof WPCF7_FormTag ) {
  335. return $this->form_tag;
  336. }
  337. if ( $submission = WPCF7_Submission::get_instance() ) {
  338. $contact_form = $submission->get_contact_form();
  339. $tags = $contact_form->scan_form_tags( array(
  340. 'name' => $this->name,
  341. 'feature' => '! zero-controls-container',
  342. ) );
  343. if ( $tags ) {
  344. $this->form_tag = $tags[0];
  345. }
  346. }
  347. return $this->form_tag;
  348. }
  349. }