PluginProbe ʕ •ᴥ•ʔ
PiWeb Product Enquiry or product catalog for WooCommerce / 2.2.34.23
PiWeb Product Enquiry or product catalog for WooCommerce v2.2.34.23
2.2.34.43 2.2.34.42 2.2.34.41 2.2.34.40 2.2.34.39 trunk 2.2.22 2.2.26 2.2.33.16 2.2.33.17 2.2.33.19 2.2.33.20 2.2.33.21 2.2.33.22 2.2.33.23 2.2.33.24 2.2.33.26 2.2.33.27 2.2.33.29 2.2.33.30 2.2.33.31 2.2.33.32 2.2.33.33 2.2.33.34 2.2.33.36 2.2.33.37 2.2.33.39 2.2.33.40 2.2.33.41 2.2.33.42 2.2.33.43 2.2.33.44 2.2.33.46 2.2.33.47 2.2.33.49 2.2.34.0 2.2.34.1 2.2.34.10 2.2.34.11 2.2.34.12 2.2.34.13 2.2.34.14 2.2.34.16 2.2.34.17 2.2.34.19 2.2.34.2 2.2.34.20 2.2.34.21 2.2.34.22 2.2.34.23 2.2.34.24 2.2.34.26 2.2.34.27 2.2.34.29 2.2.34.3 2.2.34.30 2.2.34.31 2.2.34.32 2.2.34.33 2.2.34.34 2.2.34.36 2.2.34.37 2.2.34.4 2.2.34.6 2.2.34.7 2.2.34.9
enquiry-quotation-for-woocommerce / includes / class-php-captcha.php
enquiry-quotation-for-woocommerce / includes Last commit date
img 8 months ago ARIAL.TTF 8 months ago background.png 8 months ago banner-sample.php 8 months ago class-php-captcha.php 8 months ago class-pisol-enquiry-quotation-woocommerce-activator.php 8 months ago class-pisol-enquiry-quotation-woocommerce-deactivator.php 8 months ago class-pisol-enquiry-quotation-woocommerce-i18n.php 8 months ago class-pisol-enquiry-quotation-woocommerce-loader.php 8 months ago class-pisol-enquiry-quotation-woocommerce.php 8 months ago conflict-fixer.php 8 months ago includes.php 8 months ago index.php 8 months ago pisol.class.form.php 8 months ago pisol.class.promotion.php 8 months ago review-icon.svg 8 months ago review.php 8 months ago
class-php-captcha.php
403 lines
1 <?php
2
3 class PISOL_ENQ_CaptchaGenerator{
4
5 static $instance = null;
6
7 private $useGD = false;
8
9 private $width = 200;
10
11 private $height = 50;
12
13 private $fontPath = '';
14
15 private $captchaLength = 6;
16
17 static function get_instance() {
18 if ( is_null( self::$instance ) ) {
19 self::$instance = new self();
20 }
21 return self::$instance;
22 }
23
24 public function __construct() {
25
26
27 if(! self::captcha_enabled() ) return;
28
29 if (extension_loaded('gd')) {
30 $this->useGD = 'gd';
31 } elseif (extension_loaded('imagick')) {
32 $this->useGD = 'imagick';
33 }
34
35 if($this->useGD === false) {
36 add_action( 'admin_notices', function(){
37 ?>
38 <div class="error notice">
39 <p><?php esc_html_e( 'Image generation module not installed in your server, make sure to install GD or Imagick library for PHP to use Captcha for checkout page', 'pi-dcw' ); ?></p>
40 </div>
41 <?php
42 } );
43 return;
44 }
45
46 $this->fontPath = __DIR__.'/ARIAL.TTF';
47
48 $this->captchaLength = $this->get_captcha_length();
49
50 $this->width = $this->captcha_width();
51
52
53 add_action('pi_eqw_add_captcha_field', [$this, 'custom_checkout_captcha_field']);
54
55 add_action('wc_ajax_pi_enq_generate_captcha', [$this, 'send_generated_captcha_image']);
56 add_action('wc_ajax_pi_enq_refresh_captcha', [$this, 'refreshCaptcha']);
57
58 add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
59
60 }
61
62
63 public function custom_checkout_captcha_field() {
64
65 $placeholder = $this->get_captcha_placeholder();
66 $refresh_title = $this->get_refresh_captcha_title();
67
68 echo '<div id="pi_enq_captcha_container">';
69 echo '<div id="pi_enq_captcha">';
70 echo '<input type="text" name="captcha_field" id="captcha_field" class="input-text" required placeholder="'.esc_attr($placeholder).'">';
71 echo '<div class="captcha_image_container">';
72 echo '<img src="' . esc_url( site_url('?wc-ajax=pi_enq_generate_captcha') ) . '" alt="CAPTCHA" id="captcha_image">';
73 echo '</div>';
74 echo '<a href="#" id="refresh_captcha" title="'.esc_attr($refresh_title).'"><img src="'.esc_url(plugin_dir_url( __FILE__ ).'img/refresh.svg').'" id="captcha_refresh_icon">.</a>';
75 echo '</div>';
76 echo '<label id="captcha_field-error" class="error" for="captcha_field"></label>';
77 echo '</div>';
78 }
79
80
81
82 public function send_generated_captcha_image() {
83 $this->generate_captcha_image();
84 wp_die();
85 }
86
87 public function refreshCaptcha()
88 {
89 // Generate and return a new CAPTCHA image in base64 format
90 ob_start();
91 $this->generate_captcha_image();
92 $imageData = ob_get_contents();
93 ob_end_clean();
94 $data = 'data:image/png;base64,' . base64_encode($imageData);
95 // Escape the data URI before outputting to prevent XSS
96 echo esc_attr($data);
97 wp_die(); // Prevent further execution
98 }
99
100 private function generateCaptchaCode()
101 {
102 // we removed capital I and small l as they look similar in Arial font
103 $characters = $this->get_characters();
104 $captchaCode = '';
105 for ($i = 0; $i < $this->captchaLength; $i++) {
106 $captchaCode .= $characters[rand(0, strlen($characters) - 1)];
107 }
108
109 return $captchaCode;
110 }
111
112 private function generate_captcha_image() {
113 header('Content-type: image/png');
114
115 $captcha_string = $this->generateCaptchaCode();
116
117 WC()->session->set('captcha_code', $captcha_string);
118
119 if($this->useGD === 'gd') {
120 $this->generateCaptchaImageGD($captcha_string);
121 } elseif($this->useGD === 'imagick') {
122 $this->generateCaptchaImageImagick($captcha_string);
123 }
124 }
125
126 private function generateCaptchaImageGD($captchaCode)
127 {
128 // GD-based image generation
129 $image = imagecreatetruecolor($this->width, $this->height);
130 $bgColor = imagecolorallocate($image, 255, 255, 255); // White background
131 imagefill($image, 0, 0, $bgColor);
132
133 // Add noise (random lines)
134 for ($i = 0; $i < 10; $i++) {
135 $lineColor = imagecolorallocate($image, rand(100, 200), rand(100, 200), rand(100, 200));
136 imageline($image, rand(0, $this->width), rand(0, $this->height), rand(0, $this->width), rand(0, $this->height), $lineColor);
137 }
138
139 // Add the CAPTCHA text
140 $textColor = imagecolorallocate($image, 0, 0, 0); // Black text
141 $fontSize = 30;
142 $x = 10; // Starting x position
143 $character_spacing = 30;
144 for ($i = 0; $i < strlen($captchaCode); $i++) {
145 $angle = rand(-10, 10); // Random angle
146 $y = rand(30, 40); // Random y position
147 imagettftext($image, $fontSize, $angle, $x, $y, $textColor, $this->fontPath, $captchaCode[$i]);
148 $x += $character_spacing; // Increment x position
149 }
150
151 // Output image
152 header('Content-Type: image/png');
153 imagepng($image);
154 imagedestroy($image);
155 }
156
157 private function generateCaptchaImageImagick($captchaCode)
158 {
159 // Imagick-based image generation
160 $image = new Imagick();
161 $image->newImage($this->width, $this->height, new ImagickPixel('white'));
162
163 $this->addNoise($image);
164 $this->addCaptchaText($image, $captchaCode);
165 $image->swirlImage(20);
166
167 // Output binary image data safely. Use getImageBlob() to get raw
168 // image data and avoid casting the Imagick object which PHPCS flags.
169 $image->setImageFormat('png');
170 header('Content-Type: image/png');
171 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- binary image blob
172 echo $image->getImageBlob();
173
174 $image->clear();
175 $image->destroy();
176 }
177
178 private function addNoise(Imagick $image)
179 {
180 $draw = new ImagickDraw();
181 for ($i = 0; $i < 10; $i++) {
182 $draw->setStrokeColor(new ImagickPixel(sprintf('rgb(%d,%d,%d)', rand(100, 200), rand(100, 200), rand(100, 200))));
183 $draw->setStrokeWidth(1);
184 $draw->line(rand(0, $this->width), rand(0, $this->height), rand(0, $this->width), rand(0, $this->height));
185 }
186 $image->drawImage($draw);
187 }
188
189 private function addCaptchaText(Imagick $image, $captchaCode)
190 {
191 $draw = new ImagickDraw();
192 $draw->setFillColor(new ImagickPixel('black'));
193 $draw->setFont($this->fontPath);
194 $draw->setFontSize(30);
195
196 $x = 10;
197 $characterSpacing = 30;
198 for ($i = 0; $i < strlen($captchaCode); $i++) {
199 $angle = rand(-10, 10);
200 $y = rand(30, 40);
201 $draw->annotation($x, $y, $captchaCode[$i]);
202 $x += $characterSpacing;
203 }
204
205 $image->drawImage($draw);
206 }
207
208 public function enqueueScripts()
209 {
210
211 $script = "
212 jQuery(document).ready(function ($) {
213 $('body').on('click','#refresh_captcha', function (e) {
214 e.preventDefault();
215 jQuery('#pi_enq_captcha').addClass('loading');
216 $.get('" .home_url('?wc-ajax=pi_enq_refresh_captcha'). "', function (data) {
217 $('#captcha_image').attr('src', data); // Update image src with new data URL
218 jQuery('#pi_enq_captcha').removeClass('loading');
219 jQuery('#captcha_field').val(''); // Clear the input field
220 }).fail(function () {
221 console.error('Error refreshing CAPTCHA');
222 jQuery('#pi_enq_captcha').removeClass('loading');
223 });
224 });
225 });
226 ";
227
228 wp_add_inline_script('jquery', $script);
229
230 // Inline CSS for the loading spinner
231 $color_scheme = '#cccccc';
232 $color_scheme_error = '#ff0000';
233 $css = "
234 :root {
235 --captcha_color: $color_scheme;
236 --captcha_error_color: $color_scheme_error;
237 --captcha_border:5px;
238 }
239
240 #pi_enq_captcha_container{
241 display:block;
242 width:100%;
243 margin-bottom:20px;
244 margin-top:20px;
245 }
246
247 #pi_enq_captcha{
248 display:grid;
249 grid-template-columns: 1fr 200px 50px;
250 border:var(--captcha_border, 5px) solid var(--captcha_color, #ccc);
251 border-radius:6px;
252 max-width:600px;
253 }
254
255 @media (max-width: 600px) {
256 #pi_enq_captcha{
257 grid-template-columns: 1fr;
258 }
259
260 #captcha_field{
261 border-bottom:1px solid var(--captcha_color, #ccc) !important;
262 }
263 }
264
265 body:has([data-error-id='captcha-error']) #pi_enq_captcha{
266 border:var(--captcha_border, 5px) solid var(--captcha_error_color, #ff0000);
267 }
268
269 #pi_enq_captcha.loading{
270 opacity:0.5;
271 }
272
273 .captcha_image_container{
274 padding:3px;
275 text-align:center;
276 border-left:1px solid var(--captcha_color, #ccc);
277 background-color:#ffffff;
278 display:flex;
279 align-items:center;
280 }
281
282 #captcha_image{
283 margin:auto;
284 }
285
286 #captcha_refresh_icon{
287 width:30px;
288 }
289
290 #refresh_captcha{
291 cursor:pointer;
292 display:flex;
293 align-items:center;
294 justify-content:center;
295 background:var(--captcha_color, #ccc);
296 font-size:0px;
297 border-left:1px solid var(--captcha_color, #ccc);
298 }
299
300 body:has([data-error-id='captcha-error']) #refresh_captcha{
301 background:var(--captcha_error_color, #ff0000);
302 }
303
304
305 #captcha_field, #captcha_field:focus-visible, #captcha_field:focus{
306 outline: none;
307 border:none;
308 padding:10px;
309 }
310
311 #pi_enq_captcha_container{
312 grid-column:1/3;
313 }
314
315 #captcha_field-error:empty{
316 display:none;
317 }
318 ";
319 // Add custom CSS to the checkout page use dummy dependency
320 wp_register_style('pi-enq-captch-custom-inline-css', false);
321 wp_enqueue_style('pi-enq-captch-custom-inline-css');
322 wp_add_inline_style('pi-enq-captch-custom-inline-css', $css);
323 }
324
325 function get_captcha_placeholder() {
326 $placeholder = get_option('pi_eqw_captcha_placeholder', 'Enter the CAPTCHA');
327 return $placeholder;
328 }
329
330 function get_refresh_captcha_title() {
331 return __('Refresh the CAPTCHA','pisol-enquiry-quotation-woocommerce');
332 }
333
334 function get_captcha_length() {
335 $length = get_option('pi_eqw_captcha_length', 6);
336 $length = absint( apply_filters('pi_enq_captcha_length', $length));
337 return $length > 6 || $length < 1 ? 6 : $length;
338 }
339
340 function captcha_width() {
341 $character_length = $this->get_captcha_length();
342 $width = $character_length * 40;
343 return $width;
344 }
345
346 function get_characters() {
347 $type_of_string = get_option('pi_eqw_captcha_characters', 'mix');
348 if($type_of_string === 'mix') {
349 $characters = 'ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789';
350 }
351
352 if($type_of_string === 'numbers') {
353 $characters = '0123456789';
354 }
355
356 if($type_of_string === 'capital_letter') {
357 $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
358 }
359
360 if($type_of_string === 'small_letter') {
361 $characters = 'abcdefghijklmnopqrstuvwxyz';
362 }
363
364
365 return apply_filters('pi_enq_captcha_characters', $characters);
366 }
367
368 static function captcha_enabled()
369 {
370 $type = get_option('pi_eqw_captcha', '');
371
372 if ($type == 'captcha') {
373 return true;
374 }
375
376 return false;
377 }
378
379 static function image_library_available()
380 {
381 $instance = self::get_instance();
382 return $instance->useGD !== false;
383 }
384
385 static function validateCaptcha($userInput)
386 {
387 if(isset(WC()->session)){
388 $code = WC()->session->get( 'captcha_code');
389
390 if($userInput === $code){
391 return true;
392 }
393 }else{
394 error_log('WC session not found while validating captcha');
395 }
396
397 return false;
398 }
399 }
400
401 // Instantiate the CAPTCHA class
402 PISOL_ENQ_CaptchaGenerator::get_instance();
403