submission.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <?php
  2. class WPCF7_Submission {
  3. private static $instance;
  4. private $contact_form;
  5. private $status = 'init';
  6. private $posted_data = array();
  7. private $uploaded_files = array();
  8. private $skip_mail = false;
  9. private $response = '';
  10. private $invalid_fields = array();
  11. private $meta = array();
  12. private $consent = array();
  13. private $spam_log = array();
  14. private function __construct() {}
  15. public static function get_instance( WPCF7_ContactForm $contact_form = null, $args = '' ) {
  16. $args = wp_parse_args( $args, array(
  17. 'skip_mail' => false,
  18. ) );
  19. if ( empty( self::$instance ) ) {
  20. if ( null == $contact_form ) {
  21. return null;
  22. }
  23. self::$instance = new self;
  24. self::$instance->contact_form = $contact_form;
  25. self::$instance->skip_mail = (bool) $args['skip_mail'];
  26. self::$instance->setup_posted_data();
  27. self::$instance->submit();
  28. } elseif ( null != $contact_form ) {
  29. return null;
  30. }
  31. return self::$instance;
  32. }
  33. public static function is_restful() {
  34. return defined( 'REST_REQUEST' ) && REST_REQUEST;
  35. }
  36. public function get_status() {
  37. return $this->status;
  38. }
  39. public function set_status( $status ) {
  40. if ( preg_match( '/^[a-z][0-9a-z_]+$/', $status ) ) {
  41. $this->status = $status;
  42. return true;
  43. }
  44. return false;
  45. }
  46. public function is( $status ) {
  47. return $this->status == $status;
  48. }
  49. public function get_response() {
  50. return $this->response;
  51. }
  52. public function set_response( $response ) {
  53. $this->response = $response;
  54. return true;
  55. }
  56. public function get_contact_form() {
  57. return $this->contact_form;
  58. }
  59. public function get_invalid_field( $name ) {
  60. if ( isset( $this->invalid_fields[$name] ) ) {
  61. return $this->invalid_fields[$name];
  62. } else {
  63. return false;
  64. }
  65. }
  66. public function get_invalid_fields() {
  67. return $this->invalid_fields;
  68. }
  69. public function get_posted_data( $name = '' ) {
  70. if ( ! empty( $name ) ) {
  71. if ( isset( $this->posted_data[$name] ) ) {
  72. return $this->posted_data[$name];
  73. } else {
  74. return null;
  75. }
  76. }
  77. return $this->posted_data;
  78. }
  79. private function setup_posted_data() {
  80. $posted_data = (array) $_POST;
  81. $posted_data = array_diff_key( $posted_data, array( '_wpnonce' => '' ) );
  82. $posted_data = $this->sanitize_posted_data( $posted_data );
  83. $tags = $this->contact_form->scan_form_tags();
  84. foreach ( (array) $tags as $tag ) {
  85. if ( empty( $tag->name ) ) {
  86. continue;
  87. }
  88. $type = $tag->type;
  89. $name = $tag->name;
  90. $pipes = $tag->pipes;
  91. $value_orig = $value = '';
  92. if ( isset( $posted_data[$name] ) ) {
  93. $value_orig = $value = $posted_data[$name];
  94. }
  95. if ( WPCF7_USE_PIPE
  96. and $pipes instanceof WPCF7_Pipes
  97. and ! $pipes->zero() ) {
  98. if ( is_array( $value_orig ) ) {
  99. $value = array();
  100. foreach ( $value_orig as $v ) {
  101. $value[] = $pipes->do_pipe( wp_unslash( $v ) );
  102. }
  103. } else {
  104. $value = $pipes->do_pipe( wp_unslash( $value_orig ) );
  105. }
  106. }
  107. $value = apply_filters( "wpcf7_posted_data_{$type}", $value,
  108. $value_orig, $tag );
  109. $posted_data[$name] = $value;
  110. if ( $tag->has_option( 'consent_for:storage' )
  111. and empty( $posted_data[$name] ) ) {
  112. $this->meta['do_not_store'] = true;
  113. }
  114. }
  115. $this->posted_data = apply_filters( 'wpcf7_posted_data', $posted_data );
  116. return $this->posted_data;
  117. }
  118. private function sanitize_posted_data( $value ) {
  119. if ( is_array( $value ) ) {
  120. $value = array_map( array( $this, 'sanitize_posted_data' ), $value );
  121. } elseif ( is_string( $value ) ) {
  122. $value = wp_check_invalid_utf8( $value );
  123. $value = wp_kses_no_null( $value );
  124. }
  125. return $value;
  126. }
  127. private function submit() {
  128. if ( ! $this->is( 'init' ) ) {
  129. return $this->status;
  130. }
  131. $this->meta = array_merge( $this->meta, array(
  132. 'remote_ip' => $this->get_remote_ip_addr(),
  133. 'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] )
  134. ? substr( $_SERVER['HTTP_USER_AGENT'], 0, 254 ) : '',
  135. 'url' => $this->get_request_url(),
  136. 'timestamp' => current_time( 'timestamp' ),
  137. 'unit_tag' =>
  138. isset( $_POST['_wpcf7_unit_tag'] ) ? $_POST['_wpcf7_unit_tag'] : '',
  139. 'container_post_id' => isset( $_POST['_wpcf7_container_post'] )
  140. ? (int) $_POST['_wpcf7_container_post'] : 0,
  141. 'current_user_id' => get_current_user_id(),
  142. ) );
  143. $contact_form = $this->contact_form;
  144. if ( $contact_form->is_true( 'do_not_store' ) ) {
  145. $this->meta['do_not_store'] = true;
  146. }
  147. if ( ! $this->validate() ) { // Validation error occured
  148. $this->set_status( 'validation_failed' );
  149. $this->set_response( $contact_form->message( 'validation_error' ) );
  150. } elseif ( ! $this->accepted() ) { // Not accepted terms
  151. $this->set_status( 'acceptance_missing' );
  152. $this->set_response( $contact_form->message( 'accept_terms' ) );
  153. } elseif ( $this->spam() ) { // Spam!
  154. $this->set_status( 'spam' );
  155. $this->set_response( $contact_form->message( 'spam' ) );
  156. } elseif ( ! $this->before_send_mail() ) {
  157. if ( 'init' == $this->get_status() ) {
  158. $this->set_status( 'aborted' );
  159. }
  160. if ( '' === $this->get_response() ) {
  161. $this->set_response( $contact_form->filter_message(
  162. __( "Sending mail has been aborted.", 'contact-form-7' ) )
  163. );
  164. }
  165. } elseif ( $this->mail() ) {
  166. $this->set_status( 'mail_sent' );
  167. $this->set_response( $contact_form->message( 'mail_sent_ok' ) );
  168. do_action( 'wpcf7_mail_sent', $contact_form );
  169. } else {
  170. $this->set_status( 'mail_failed' );
  171. $this->set_response( $contact_form->message( 'mail_sent_ng' ) );
  172. do_action( 'wpcf7_mail_failed', $contact_form );
  173. }
  174. $this->remove_uploaded_files();
  175. return $this->status;
  176. }
  177. private function get_remote_ip_addr() {
  178. $ip_addr = '';
  179. if ( isset( $_SERVER['REMOTE_ADDR'] )
  180. and WP_Http::is_ip_address( $_SERVER['REMOTE_ADDR'] ) ) {
  181. $ip_addr = $_SERVER['REMOTE_ADDR'];
  182. }
  183. return apply_filters( 'wpcf7_remote_ip_addr', $ip_addr );
  184. }
  185. private function get_request_url() {
  186. $home_url = untrailingslashit( home_url() );
  187. if ( self::is_restful() ) {
  188. $referer = isset( $_SERVER['HTTP_REFERER'] )
  189. ? trim( $_SERVER['HTTP_REFERER'] ) : '';
  190. if ( $referer
  191. and 0 === strpos( $referer, $home_url ) ) {
  192. return esc_url_raw( $referer );
  193. }
  194. }
  195. $url = preg_replace( '%(?<!:|/)/.*$%', '', $home_url )
  196. . wpcf7_get_request_uri();
  197. return $url;
  198. }
  199. private function validate() {
  200. if ( $this->invalid_fields ) {
  201. return false;
  202. }
  203. require_once WPCF7_PLUGIN_DIR . '/includes/validation.php';
  204. $result = new WPCF7_Validation();
  205. $tags = $this->contact_form->scan_form_tags();
  206. foreach ( $tags as $tag ) {
  207. $type = $tag->type;
  208. $result = apply_filters( "wpcf7_validate_{$type}", $result, $tag );
  209. }
  210. $result = apply_filters( 'wpcf7_validate', $result, $tags );
  211. $this->invalid_fields = $result->get_invalid_fields();
  212. return $result->is_valid();
  213. }
  214. private function accepted() {
  215. return apply_filters( 'wpcf7_acceptance', true, $this );
  216. }
  217. public function add_consent( $name, $conditions ) {
  218. $this->consent[$name] = $conditions;
  219. return true;
  220. }
  221. public function collect_consent() {
  222. return (array) $this->consent;
  223. }
  224. private function spam() {
  225. $spam = false;
  226. if ( $this->contact_form->is_true( 'subscribers_only' )
  227. and current_user_can( 'wpcf7_submit', $this->contact_form->id() ) ) {
  228. return $spam;
  229. }
  230. $user_agent = (string) $this->get_meta( 'user_agent' );
  231. if ( strlen( $user_agent ) < 2 ) {
  232. $spam = true;
  233. $this->add_spam_log( array(
  234. 'agent' => 'wpcf7',
  235. 'reason' => __( "User-Agent string is unnaturally short.", 'contact-form-7' ),
  236. ) );
  237. }
  238. if ( ! $this->verify_nonce() ) {
  239. $spam = true;
  240. $this->add_spam_log( array(
  241. 'agent' => 'wpcf7',
  242. 'reason' => __( "Submitted nonce is invalid.", 'contact-form-7' ),
  243. ) );
  244. }
  245. if ( $this->is_blacklisted() ) {
  246. $spam = true;
  247. $this->add_spam_log( array(
  248. 'agent' => 'wpcf7',
  249. 'reason' => __( "Blacklisted words are used.", 'contact-form-7' ),
  250. ) );
  251. }
  252. return apply_filters( 'wpcf7_spam', $spam );
  253. }
  254. public function add_spam_log( $args = '' ) {
  255. $args = wp_parse_args( $args, array(
  256. 'agent' => '',
  257. 'reason' => '',
  258. ) );
  259. $this->spam_log[] = $args;
  260. }
  261. public function get_spam_log() {
  262. return $this->spam_log;
  263. }
  264. private function verify_nonce() {
  265. if ( ! $this->contact_form->nonce_is_active() ) {
  266. return true;
  267. }
  268. return wpcf7_verify_nonce( $_POST['_wpnonce'] );
  269. }
  270. private function is_blacklisted() {
  271. $target = wpcf7_array_flatten( $this->posted_data );
  272. $target[] = $this->get_meta( 'remote_ip' );
  273. $target[] = $this->get_meta( 'user_agent' );
  274. $target = implode( "\n", $target );
  275. return (bool) apply_filters( 'wpcf7_submission_is_blacklisted',
  276. wpcf7_blacklist_check( $target ), $this );
  277. }
  278. /* Mail */
  279. private function before_send_mail() {
  280. $abort = false;
  281. do_action_ref_array( 'wpcf7_before_send_mail', array(
  282. $this->contact_form,
  283. &$abort,
  284. $this,
  285. ) );
  286. return ! $abort;
  287. }
  288. private function mail() {
  289. $contact_form = $this->contact_form;
  290. $skip_mail = apply_filters( 'wpcf7_skip_mail',
  291. $this->skip_mail, $contact_form );
  292. if ( $skip_mail ) {
  293. return true;
  294. }
  295. $result = WPCF7_Mail::send( $contact_form->prop( 'mail' ), 'mail' );
  296. if ( $result ) {
  297. $additional_mail = array();
  298. if ( $mail_2 = $contact_form->prop( 'mail_2' )
  299. and $mail_2['active'] ) {
  300. $additional_mail['mail_2'] = $mail_2;
  301. }
  302. $additional_mail = apply_filters( 'wpcf7_additional_mail',
  303. $additional_mail, $contact_form );
  304. foreach ( $additional_mail as $name => $template ) {
  305. WPCF7_Mail::send( $template, $name );
  306. }
  307. return true;
  308. }
  309. return false;
  310. }
  311. public function uploaded_files() {
  312. return $this->uploaded_files;
  313. }
  314. public function add_uploaded_file( $name, $file_path ) {
  315. $this->uploaded_files[$name] = $file_path;
  316. if ( empty( $this->posted_data[$name] ) ) {
  317. $this->posted_data[$name] = basename( $file_path );
  318. }
  319. }
  320. public function remove_uploaded_files() {
  321. foreach ( (array) $this->uploaded_files as $name => $path ) {
  322. wpcf7_rmdir_p( $path );
  323. if ( $dir = dirname( $path )
  324. and false !== ( $files = scandir( $dir ) )
  325. and ! array_diff( $files, array( '.', '..' ) ) ) {
  326. // remove parent dir if it's empty.
  327. rmdir( $dir );
  328. }
  329. }
  330. }
  331. public function get_meta( $name ) {
  332. if ( isset( $this->meta[$name] ) ) {
  333. return $this->meta[$name];
  334. }
  335. }
  336. }