Skip to content

Commit 4f135aa

Browse files
Merge pull request #614 from CleanTalk/ee-shotrcode-to-exclude.ag
ee-shotrcode-to-exclude.ag
2 parents d6607c8 + 52b7b99 commit 4f135aa

15 files changed

Lines changed: 1249 additions & 586 deletions

lib/Cleantalk/Antispam/EmailEncoder.php renamed to lib/Cleantalk/Antispam/EmailEncoder/EmailEncoder.php

Lines changed: 354 additions & 579 deletions
Large diffs are not rendered by default.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace Cleantalk\Antispam\EmailEncoder;
4+
5+
/**
6+
* Exclusions to use on content during modification chunks.
7+
*/
8+
class EmailEncoderHelper
9+
{
10+
/**
11+
* Attribute names to skip content encoding contains them. Keep arrays of tag=>[attributes].
12+
* @var array[]
13+
*/
14+
private $attribute_exclusions_signs = array(
15+
'input' => array('placeholder', 'value'),
16+
'img' => array('alt', 'title'),
17+
'div' => array('data-et-multi-view'),
18+
);
19+
20+
/**
21+
* Checking if the string contains mailto: link
22+
*
23+
* @param string $string
24+
*
25+
* @return bool
26+
*/
27+
public function isMailto($string)
28+
{
29+
return strpos($string, 'mailto:') !== false;
30+
}
31+
32+
/**
33+
* Checking if the string contains tel: link
34+
*
35+
* @param string $string
36+
*
37+
* @return bool
38+
*/
39+
public function isTelTag($string)
40+
{
41+
return strpos($string, 'tel:') !== false;
42+
}
43+
44+
/**
45+
* Checking if the string contains mailto: link
46+
*
47+
* @param array $match
48+
* @param string $content
49+
*
50+
* @return bool
51+
*/
52+
public function isMailtoAdditionalCopy($match, $content)
53+
{
54+
$position = isset($match[1]) ? (int)$match[1] : null;
55+
56+
if (null === $position) {
57+
return false;
58+
}
59+
60+
$cc_position = strrpos(substr($content, 0, $position), 'cc=');
61+
if ( $cc_position !== false && $cc_position + 3 == $position ) {
62+
return true;
63+
}
64+
65+
$bcc_position = strrpos(substr($content, 0, $position), 'bcc=');
66+
if ( $bcc_position !== false && $bcc_position + 4 == $position ) {
67+
return true;
68+
}
69+
70+
return false;
71+
}
72+
73+
/**
74+
* Checking if email in link
75+
*
76+
* @param array $matches
77+
* @param string $content
78+
*
79+
* @return bool
80+
*/
81+
public function isEmailInLink($matches, $content)
82+
{
83+
$email = isset($matches[0]) && is_string($matches[0]) ? $matches[0] : null;
84+
$position = isset($matches[1]) ? (int)$matches[1] : null;
85+
86+
if (null === $position || null === $email) {
87+
return false;
88+
}
89+
90+
$href_position = strrpos(substr($content, 0, $position), 'href=');
91+
92+
if ( $href_position !== false && $href_position + 6 == $position ) {
93+
return true;
94+
}
95+
96+
return strpos($email, 'mailto:') !== false;
97+
}
98+
99+
/**
100+
* Check if the given email is inside a script tag
101+
* @param string $email The email to check
102+
* @param string $content The full content
103+
* @return bool
104+
*/
105+
public function isInsideScriptTag($email, $content)
106+
{
107+
// Find position of the email in content
108+
$pos = strpos($content, $email);
109+
if ($pos === false) {
110+
return false;
111+
}
112+
113+
// Find the last script opening tag before the email
114+
$last_script_start = strrpos(substr($content, 0, $pos), '<script');
115+
if ($last_script_start === false) {
116+
return false;
117+
}
118+
119+
// Find the first script closing tag after the last opening tag
120+
$script_end = strpos($content, '</script>', $last_script_start);
121+
if ($script_end === false) {
122+
return false;
123+
}
124+
125+
// The email is inside a script tag if its position is between the opening and closing tags
126+
return ($pos > $last_script_start && $pos < $script_end);
127+
}
128+
129+
/**
130+
* Check if email is placed in the tag that has attributes of exclusions.
131+
* @param string $email_match - email
132+
* @param string $temp_content - email
133+
* @return bool
134+
*/
135+
public function hasAttributeExclusions($email_match, $temp_content)
136+
{
137+
$email_match = preg_quote($email_match);
138+
foreach ( $this->attribute_exclusions_signs as $tag => $array_of_attributes ) {
139+
foreach ( $array_of_attributes as $attribute ) {
140+
//do not remove IDE highlighted unnecessary escape!
141+
$pattern = '/<'
142+
. $tag
143+
. '+\s+[^>]*\b'
144+
. $attribute
145+
. '=((\\\')|")?[^"]*\b'
146+
. $email_match
147+
. '\b[^"]*((\\\')|")?"[^>]*>/';
148+
preg_match($pattern, $temp_content, $attr_match);
149+
if ( !empty($attr_match) ) {
150+
return true;
151+
}
152+
}
153+
}
154+
return false;
155+
}
156+
}

lib/Cleantalk/Antispam/Encoder.php renamed to lib/Cleantalk/Antispam/EmailEncoder/Encoder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Cleantalk\Antispam;
3+
namespace Cleantalk\Antispam\EmailEncoder;
44

55
class Encoder
66
{
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
<?php
2+
3+
namespace Cleantalk\Antispam\EmailEncoder;
4+
5+
use Cleantalk\ApbctWP\State;
6+
use Cleantalk\ApbctWP\Variables\Cookie;
7+
use Cleantalk\ApbctWP\Variables\Post;
8+
use Cleantalk\ApbctWP\Variables\Server;
9+
use Cleantalk\Common\TT;
10+
11+
class ExclusionsService
12+
{
13+
/**
14+
* Keep arrays of exclusion signs in the array
15+
* @var array
16+
*/
17+
private $content_exclusions_signs = array(
18+
//divi contact forms additional emails
19+
array('_unique_id', 'et_pb_contact_form'),
20+
//divi builder contact forms
21+
array('_builder_version', 'custom_contact_email'),
22+
//ninja form noscript content exclusion
23+
array('ninja-forms-noscript-message'),
24+
//enfold theme contact form content exclusion - this fired during buffer interception
25+
array('av_contact', 'email', 'from_email'),
26+
// Stylish Cost Calculator
27+
array('scc-form-field-item'),
28+
// Exclusion of maps from leaflet
29+
array('leaflet'),
30+
// prevent broking elementor swiper gallery
31+
array('class', 'elementor-swiper', 'elementor-testimonial', 'swiper-pagination'),
32+
// ics-calendar
33+
array('ics_calendar'),
34+
);
35+
36+
/**
37+
* @return string|false
38+
* @psalm-suppress PossiblyUnusedReturnValue
39+
*/
40+
public function doSkipBeforeAnything()
41+
{
42+
global $apbct;
43+
if ( $this->byAccessKeyFail($apbct) ) {
44+
return 'byAccessKeyFail';
45+
}
46+
47+
return false;
48+
}
49+
50+
/**
51+
* @return string|false
52+
* @psalm-suppress PossiblyUnusedReturnValue
53+
*/
54+
public function doSkipBeforeModifyingHooksAdded()
55+
{
56+
global $apbct;
57+
58+
if ( $this->byPluginSetting($apbct) ) {
59+
return 'byPluginSetting';
60+
}
61+
62+
// Excluded request
63+
if ( $this->byServerVars() ) {
64+
return 'byServerVars';
65+
}
66+
67+
return false;
68+
}
69+
70+
/**
71+
* @param $content
72+
*
73+
* @return string|false
74+
* @psalm-suppress PossiblyUnusedReturnValue
75+
* @psalm-suppress PossiblyUnusedMethod
76+
*/
77+
public function doReturnContentBeforeModify($content)
78+
{
79+
global $apbct;
80+
81+
if ( !(bool)$apbct->settings['data__email_decoder_encode_email_addresses'] && !(bool)$apbct->settings['data__email_decoder_encode_phone_numbers'] ) {
82+
return 'globallyDisabledBothEncoding';
83+
}
84+
85+
if ( $this->byLoggedIn() ) {
86+
return 'byLoggedIn';
87+
}
88+
89+
//skip empty or invalid content
90+
if ( $this->byEmptyContent($content) ) {
91+
return 'byEmptyContent';
92+
}
93+
94+
if ( $this->byUrlOnHooks() ) {
95+
return 'byUrlOnHooks';
96+
}
97+
98+
if ( $this->byContentSigns($content) ) {
99+
return 'byContentSigns';
100+
}
101+
102+
return false;
103+
}
104+
105+
/**
106+
* Excluded requests
107+
* @return bool
108+
*/
109+
private function byServerVars()
110+
{
111+
// Excluded request by alt cookie
112+
$apbct_email_encoder_passed = Cookie::get('apbct_email_encoder_passed');
113+
if ( $apbct_email_encoder_passed === apbct_get_email_encoder_pass_key() ) {
114+
return true;
115+
}
116+
117+
if (
118+
apbct_is_plugin_active('ultimate-member/ultimate-member.php') &&
119+
isset($_POST['um_request']) &&
120+
array_key_exists('REQUEST_METHOD', $_SERVER) &&
121+
strtoupper($_SERVER['REQUEST_METHOD']) === 'POST' &&
122+
empty(Post::get('encodedEmail'))
123+
) {
124+
return true;
125+
}
126+
127+
return false;
128+
}
129+
130+
/**
131+
* @param State $apbct
132+
*
133+
* @return bool
134+
*/
135+
private function byPluginSetting($apbct)
136+
{
137+
return ! $apbct->settings['data__email_decoder'];
138+
}
139+
140+
/**
141+
* @param State $apbct
142+
*
143+
* @return bool
144+
*/
145+
private function byAccessKeyFail($apbct)
146+
{
147+
return !$apbct->key_is_ok || ! apbct_api_key__is_correct();
148+
}
149+
150+
/**
151+
* Check content if it contains exclusions from exclusion list
152+
* @param $content - content to check
153+
* @return bool - true if exclusions found, else - false
154+
*/
155+
private function byContentSigns($content)
156+
{
157+
if ( is_array($this->content_exclusions_signs) ) {
158+
foreach ( array_values($this->content_exclusions_signs) as $_signs_array => $signs ) {
159+
//process each of subarrays of signs
160+
$signs_found_count = 0;
161+
if ( isset($signs) && is_array($signs) ) {
162+
//chek all the signs in the sub-array
163+
foreach ( $signs as $sign ) {
164+
if ( is_string($sign) ) {
165+
if ( strpos($content, $sign) === false ) {
166+
continue;
167+
} else {
168+
$signs_found_count++;
169+
}
170+
}
171+
}
172+
//if each of signs in the sub-array are found return true
173+
if ( $signs_found_count === count($signs) ) {
174+
if (in_array('et_pb_contact_form', $signs) && !is_admin()) {
175+
return false;
176+
}
177+
return true;
178+
}
179+
}
180+
}
181+
}
182+
//no signs found
183+
return false;
184+
}
185+
186+
/**
187+
* @param string $content
188+
*
189+
* @return bool
190+
*/
191+
private function byEmptyContent($content)
192+
{
193+
//skip empty or invalid content
194+
return empty($content) || !is_string($content);
195+
}
196+
197+
/**
198+
* @return bool
199+
*/
200+
private function byLoggedIn()
201+
{
202+
return apbct_is_user_logged_in() && !apbct_is_in_uri('options-general.php?page=cleantalk');
203+
}
204+
205+
/**
206+
* Skip encoder run on hooks.
207+
*
208+
* 1. Applies filter "apbct_hook_skip_email_encoder_on_url_list" to get modified list of URI chunks that needs to skip.
209+
* @return bool
210+
*/
211+
private function byUrlOnHooks()
212+
{
213+
$skip_encode = false;
214+
$url_chunk_list = array();
215+
216+
// Apply filter "apbct_hook_skip_email_encoder_on_url_list" to get the URI chunk list.
217+
$url_chunk_list = apply_filters('apbct_skip_email_encoder_on_uri_chunk_list', $url_chunk_list);
218+
219+
if ( !empty($url_chunk_list) && is_array($url_chunk_list) ) {
220+
foreach ($url_chunk_list as $chunk) {
221+
if (is_string($chunk) && strpos(TT::toString(Server::get('REQUEST_URI')), $chunk) !== false) {
222+
$skip_encode = true;
223+
break;
224+
}
225+
}
226+
}
227+
228+
return $skip_encode;
229+
}
230+
}

0 commit comments

Comments
 (0)