Cimy User Extra Fields - Version 2.6.2

Version Description

Download this release

Release Info

Developer Cimmo
Plugin Icon wp plugin Cimy User Extra Fields
Version 2.6.2
Comparing to
See all releases

Code changes from version 2.6.1 to 2.6.2

Files changed (59) hide show
  1. README_OFFICIAL.txt +4 -0
  2. cimy_uef_register.php +4 -4
  3. cimy_user_extra_fields.php +2 -2
  4. readme.txt +2 -2
  5. securimage/README.txt +53 -30
  6. securimage/WavFile.php +50 -15
  7. securimage/audio/.htaccess +1 -0
  8. securimage/audio/en/0.wav +0 -0
  9. securimage/audio/en/1.wav +0 -0
  10. securimage/audio/en/10.wav +0 -0
  11. securimage/audio/en/11.wav +0 -0
  12. securimage/audio/en/12.wav +0 -0
  13. securimage/audio/en/13.wav +0 -0
  14. securimage/audio/en/14.wav +0 -0
  15. securimage/audio/en/15.wav +0 -0
  16. securimage/audio/en/16.wav +0 -0
  17. securimage/audio/en/17.wav +0 -0
  18. securimage/audio/en/18.wav +0 -0
  19. securimage/audio/en/19.wav +0 -0
  20. securimage/audio/en/2.wav +0 -0
  21. securimage/audio/en/20.wav +0 -0
  22. securimage/audio/en/3.wav +0 -0
  23. securimage/audio/en/4.wav +0 -0
  24. securimage/audio/en/5.wav +0 -0
  25. securimage/audio/en/6.wav +0 -0
  26. securimage/audio/en/7.wav +0 -0
  27. securimage/audio/en/8.wav +0 -0
  28. securimage/audio/en/9.wav +0 -0
  29. securimage/audio/en/A.wav +0 -0
  30. securimage/audio/en/B.wav +0 -0
  31. securimage/audio/en/C.wav +0 -0
  32. securimage/audio/en/D.wav +0 -0
  33. securimage/audio/en/E.wav +0 -0
  34. securimage/audio/en/F.wav +0 -0
  35. securimage/audio/en/G.wav +0 -0
  36. securimage/audio/en/H.wav +0 -0
  37. securimage/audio/en/I.wav +0 -0
  38. securimage/audio/en/J.wav +0 -0
  39. securimage/audio/en/K.wav +0 -0
  40. securimage/audio/en/L.wav +0 -0
  41. securimage/audio/en/M.wav +0 -0
  42. securimage/audio/en/N.wav +0 -0
  43. securimage/audio/en/O.wav +0 -0
  44. securimage/audio/en/P.wav +0 -0
  45. securimage/audio/en/Q.wav +0 -0
  46. securimage/audio/en/R.wav +0 -0
  47. securimage/audio/en/S.wav +0 -0
  48. securimage/audio/en/T.wav +0 -0
  49. securimage/audio/en/U.wav +0 -0
  50. securimage/audio/en/V.wav +0 -0
  51. securimage/audio/en/W.wav +0 -0
  52. securimage/audio/en/X.wav +0 -0
  53. securimage/audio/en/Y.wav +0 -0
  54. securimage/audio/en/Z.wav +0 -0
  55. securimage/example_form.ajax.php +2 -2
  56. securimage/example_form.php +38 -27
  57. securimage/securimage.php +520 -102
  58. securimage/securimage_play.php +19 -1
  59. securimage/securimage_show.php +3 -1
README_OFFICIAL.txt CHANGED
@@ -631,6 +631,10 @@ A lot of times I cannot reproduce the problem and I need more details, so if you
631
 
632
 
633
  CHANGELOG:
 
 
 
 
634
  v2.6.1 - 30/09/2013
635
  - Added dropdowns for the year and the month selectors to the date picker
636
  - Added Min and Max date support for the new 'date' extra field (no backend support yet)
631
 
632
 
633
  CHANGELOG:
634
+ v2.6.2 - 18/04/2014
635
+ - Fixed date picker on registration page was conflicting with Google Chrome's one (thanks to Francesca)
636
+ - Updated Securimage Captcha to v3.5.2
637
+
638
  v2.6.1 - 30/09/2013
639
  - Added dropdowns for the year and the month selectors to the date picker
640
  - Added Min and Max date support for the new 'date' extra field (no backend support yet)
cimy_uef_register.php CHANGED
@@ -672,7 +672,7 @@ function cimy_uef_validate_username($valid, $username) {
672
  // show_type == 1 - search form, all fields are text, password fields are skipped
673
  // show_type == 2 - confirmation form, all fields are plain text, images can be cropped
674
  function cimy_registration_form($errors=null, $show_type=0) {
675
- global $wpdb, $start_cimy_uef_comment, $end_cimy_uef_comment, $rule_maxlen_needed, $fields_name_prefix, $wp_fields_name_prefix, $cuef_plugin_dir, $cimy_uef_file_types, $cimy_uef_textarea_types, $user_level, $cimy_uef_domain, $cimy_uef_file_images_types;
676
 
677
  if (cimy_is_at_least_wordpress35())
678
  cimy_switch_to_blog();
@@ -889,7 +889,7 @@ function cimy_registration_form($errors=null, $show_type=0) {
889
  $obj_class = ' class="'.$input_class.$obj_class.'"';
890
  $obj_name = ' name="'.$input_name.'"';
891
 
892
- if ($type == "picture-url")
893
  $obj_type = ' type="text"';
894
  else
895
  $obj_type = ' type="'.$type.'"';
@@ -1218,13 +1218,13 @@ function cimy_registration_form($errors=null, $show_type=0) {
1218
  else
1219
  $width = 278;
1220
  ?>
1221
- <div style="width: <?php echo $width; ?>px; float: left; height: 80px; vertical-align: text-top;">
1222
  <img id="captcha" align="left" style="padding-right: 5px; border: 0" src="<?php echo $cuef_securimage_webpath; ?>/securimage_show_captcha.php" alt="CAPTCHA Image" />
1223
  <object type="application/x-shockwave-flash" data="<?php echo $cuef_securimage_webpath; ?>/securimage_play.swf?audio_file=<?php echo $cuef_securimage_webpath; ?>/securimage_play.php&#038;bgColor1=#fff&#038;bgColor2=#fff&#038;iconColor=#777&#038;borderWidth=1&#038;borderColor=#000" height="19" width="19"><param name="movie" value="<?php echo $cuef_securimage_webpath; ?>/securimage_play.swf?audio_file=<?php echo $cuef_securimage_webpath; ?>/securimage_play.php&#038;bgColor1=#fff&#038;bgColor2=#fff&#038;iconColor=#777&#038;borderWidth=1&#038;borderColor=#000" /></object>
1224
  <br /><br /><br />
1225
  <a align="right"<?php if (!empty($obj_tabindex)) echo " tabindex=\"".$tabindex."\""; $tabindex++; ?> style="border-style: none" href="#" onclick="document.getElementById('captcha').src = '<?php echo $cuef_securimage_webpath; ?>/securimage_show_captcha.php?' + Math.random(); return false"><img src="<?php echo $cuef_securimage_webpath; ?>/images/refresh.png" alt="<?php _e("Change image", $cimy_uef_domain); ?>" border="0" onclick="this.blur()" align="bottom" height="19" width="19" /></a>
1226
  </div>
1227
- <div style="width: <?php echo $width; ?>px; float: left; height: 50px; vertical-align: bottom; padding: 5px;">
1228
  <?php _e("Insert the code:", $cimy_uef_domain); ?>&nbsp;<input type="text" name="securimage_response_field" size="12" maxlength="16"<?php if (!empty($obj_tabindex)) echo " tabindex=\"".$tabindex."\""; $tabindex++; ?> />
1229
  </div>
1230
  <?php
672
  // show_type == 1 - search form, all fields are text, password fields are skipped
673
  // show_type == 2 - confirmation form, all fields are plain text, images can be cropped
674
  function cimy_registration_form($errors=null, $show_type=0) {
675
+ global $wpdb, $start_cimy_uef_comment, $end_cimy_uef_comment, $rule_maxlen_needed, $fields_name_prefix, $wp_fields_name_prefix, $cuef_plugin_dir, $cimy_uef_file_types, $cimy_uef_textarea_types, $user_level, $cimy_uef_domain, $cimy_uef_file_images_types, $cimy_uef_text_types;
676
 
677
  if (cimy_is_at_least_wordpress35())
678
  cimy_switch_to_blog();
889
  $obj_class = ' class="'.$input_class.$obj_class.'"';
890
  $obj_name = ' name="'.$input_name.'"';
891
 
892
+ if (in_array($type, $cimy_uef_text_types))
893
  $obj_type = ' type="text"';
894
  else
895
  $obj_type = ' type="'.$type.'"';
1218
  else
1219
  $width = 278;
1220
  ?>
1221
+ <div style="width: <?php echo $width; ?>px; clear: both; height: 80px; vertical-align: text-top;">
1222
  <img id="captcha" align="left" style="padding-right: 5px; border: 0" src="<?php echo $cuef_securimage_webpath; ?>/securimage_show_captcha.php" alt="CAPTCHA Image" />
1223
  <object type="application/x-shockwave-flash" data="<?php echo $cuef_securimage_webpath; ?>/securimage_play.swf?audio_file=<?php echo $cuef_securimage_webpath; ?>/securimage_play.php&#038;bgColor1=#fff&#038;bgColor2=#fff&#038;iconColor=#777&#038;borderWidth=1&#038;borderColor=#000" height="19" width="19"><param name="movie" value="<?php echo $cuef_securimage_webpath; ?>/securimage_play.swf?audio_file=<?php echo $cuef_securimage_webpath; ?>/securimage_play.php&#038;bgColor1=#fff&#038;bgColor2=#fff&#038;iconColor=#777&#038;borderWidth=1&#038;borderColor=#000" /></object>
1224
  <br /><br /><br />
1225
  <a align="right"<?php if (!empty($obj_tabindex)) echo " tabindex=\"".$tabindex."\""; $tabindex++; ?> style="border-style: none" href="#" onclick="document.getElementById('captcha').src = '<?php echo $cuef_securimage_webpath; ?>/securimage_show_captcha.php?' + Math.random(); return false"><img src="<?php echo $cuef_securimage_webpath; ?>/images/refresh.png" alt="<?php _e("Change image", $cimy_uef_domain); ?>" border="0" onclick="this.blur()" align="bottom" height="19" width="19" /></a>
1226
  </div>
1227
+ <div style="width: <?php echo $width; ?>px; clear: both; height: 70px; vertical-align: bottom; padding: 5px;">
1228
  <?php _e("Insert the code:", $cimy_uef_domain); ?>&nbsp;<input type="text" name="securimage_response_field" size="12" maxlength="16"<?php if (!empty($obj_tabindex)) echo " tabindex=\"".$tabindex."\""; $tabindex++; ?> />
1229
  </div>
1230
  <?php
cimy_user_extra_fields.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Cimy User Extra Fields
4
  Plugin URI: http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/
5
  Description: Add some useful fields to registration and user's info
6
- Version: 2.6.1
7
  Author: Marco Cimmino
8
  Author URI: mailto:cimmino.marco@gmail.com
9
  License: GPL2
@@ -162,7 +162,7 @@ add_action('admin_init', 'cimy_uef_admin_init');
162
  add_action('init', 'cimy_uef_init');
163
 
164
  $cimy_uef_name = "Cimy User Extra Fields";
165
- $cimy_uef_version = "2.6.1";
166
  $cimy_uef_url = "http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/";
167
  $cimy_project_url = "http://www.marcocimmino.net/cimy-wordpress-plugins/support-the-cimy-project-paypal/";
168
 
3
  Plugin Name: Cimy User Extra Fields
4
  Plugin URI: http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/
5
  Description: Add some useful fields to registration and user's info
6
+ Version: 2.6.2
7
  Author: Marco Cimmino
8
  Author URI: mailto:cimmino.marco@gmail.com
9
  License: GPL2
162
  add_action('init', 'cimy_uef_init');
163
 
164
  $cimy_uef_name = "Cimy User Extra Fields";
165
+ $cimy_uef_version = "2.6.2";
166
  $cimy_uef_url = "http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/";
167
  $cimy_project_url = "http://www.marcocimmino.net/cimy-wordpress-plugins/support-the-cimy-project-paypal/";
168
 
readme.txt CHANGED
@@ -4,8 +4,8 @@ Donate link: http://www.marcocimmino.net/cimy-wordpress-plugins/support-the-cimy
4
  Website link: http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/
5
  Tags: cimy, admin, registration, profile, extra fields, avatar, gravatar, recaptcha, captcha
6
  Requires at least: 3.1
7
- Tested up to: 3.6
8
- Stable tag: 2.6.1
9
 
10
  Add some useful fields to registration and user's info
11
 
4
  Website link: http://www.marcocimmino.net/cimy-wordpress-plugins/cimy-user-extra-fields/
5
  Tags: cimy, admin, registration, profile, extra fields, avatar, gravatar, recaptcha, captcha
6
  Requires at least: 3.1
7
+ Tested up to: 3.9
8
+ Stable tag: 2.6.2
9
 
10
  Add some useful fields to registration and user's info
11
 
securimage/README.txt CHANGED
@@ -2,7 +2,9 @@ NAME:
2
 
3
  Securimage - A PHP class for creating captcha images and audio with many options.
4
 
5
- VERSION: 3.5.1
 
 
6
 
7
  AUTHOR:
8
 
@@ -19,6 +21,7 @@ DOCUMENTATION:
19
  be found at http://www.phpcaptcha.org/Securimage_Docs/
20
 
21
  REQUIREMENTS:
 
22
  PHP 5.2 or greater
23
  GD 2.0
24
  FreeType (Required, for TTF fonts)
@@ -27,15 +30,24 @@ REQUIREMENTS:
27
  SYNOPSIS:
28
 
29
  require_once 'securimage.php';
30
-
31
- $image = new Securimage();
32
-
33
- $image->show();
 
 
 
 
 
 
 
 
 
34
 
35
  // Code Validation
36
 
37
  $image = new Securimage();
38
- if ($image->check($_POST['code']) == true) {
39
  echo "Correct!";
40
  } else {
41
  echo "Sorry, wrong code.";
@@ -45,19 +57,26 @@ DESCRIPTION:
45
 
46
  What is Securimage?
47
 
48
- Securimage is a PHP class that is used to generate and validate CAPTCHA images.
49
- The classes uses an existing PHP session or creates its own if none is found to store the
50
- CAPTCHA code. Variables within the class are used to control the style and display of the image.
51
- The class supports TTF fonts and effects for strengthening the security of the image.
52
- An audible code can also be streamed to the browser for visually impared users.
53
-
 
 
 
 
 
 
54
 
55
  COPYRIGHT:
56
- Copyright (c) 2013 Drew Phillips
 
57
  All rights reserved.
58
 
59
- Redistribution and use in source and binary forms, with or without modification,
60
- are permitted provided that the following conditions are met:
61
 
62
  - Redistributions of source code must retain the above copyright notice,
63
  this list of conditions and the following disclaimer.
@@ -77,17 +96,18 @@ COPYRIGHT:
77
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
78
  POSSIBILITY OF SUCH DAMAGE.
79
 
80
- -----------------------------------------------------------------------------
81
- The WavFile.php class used in Securimage by Drew Phillips and Paul Voegler is
82
- used under the BSD License. See WavFile.php for details.
 
83
  Many thanks to Paul Voegler (http://www.voegler.eu/) for contributing to
84
  Securimage.
85
 
86
- -----------------------------------------------------------------------------
87
- Flash code created for Securimage by Age Bosma & Mario Romero (animario@hotmail.com)
88
  Many thanks for releasing this to the project!
89
 
90
- ------------------------------------------------------------------------------
91
  Portions of Securimage contain code from Han-Kwang Nienhuys' PHP captcha
92
 
93
  Han-Kwang Nienhuys' PHP captcha
@@ -99,10 +119,12 @@ COPYRIGHT:
99
  The original, unrestricted version can be obtained from
100
  http://www.lagom.nl/linux/hkcaptcha/
101
 
102
- -------------------------------------------------------------------------------
103
- AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller and is distributed as freeware
 
104
 
105
- Alte Haas Grotesk is a typeface that look like an helvetica printed in an old Muller-Brockmann Book.
 
106
 
107
  These fonts are freeware and can be distributed as long as they are
108
  together with this text file.
@@ -113,13 +135,15 @@ COPYRIGHT:
113
  www.yannlecoroller.com
114
  yann@lecoroller.com
115
 
116
- -------------------------------------------------------------------------------
117
- Portions of securimage_play.swf use the PopForge flash library for playing audio
 
118
 
119
  /**
120
  * Copyright(C) 2007 Andre Michelle and Joa Ebert
121
  *
122
- * PopForge is an ActionScript3 code sandbox developed by Andre Michelle and Joa Ebert
 
123
  * http://sandbox.popforge.de
124
  *
125
  * PopforgeAS3Audio is free software; you can redistribute it and/or modify
@@ -136,14 +160,14 @@ COPYRIGHT:
136
  * along with this program. If not, see <http://www.gnu.org/licenses/>
137
  */
138
 
139
- -------------------------------------------------------------------------------
140
  Some graphics used are from the Humility Icon Pack by WorLord
141
 
142
  License: GNU/GPL (http://findicons.com/pack/1723/humility)
143
  http://findicons.com/icon/192558/gnome_volume_control
144
  http://findicons.com/icon/192562/gtk_refresh
145
 
146
- -------------------------------------------------------------------------------
147
  Background noise sound files are from SoundJay.com
148
  http://www.soundjay.com/tos.html
149
 
@@ -178,4 +202,3 @@ COPYRIGHT:
178
  If you use the sound effects, please consider giving us a credit and
179
  linking back to us but it's not required.
180
 
181
-
2
 
3
  Securimage - A PHP class for creating captcha images and audio with many options.
4
 
5
+ VERSION:
6
+
7
+ 3.5.2
8
 
9
  AUTHOR:
10
 
21
  be found at http://www.phpcaptcha.org/Securimage_Docs/
22
 
23
  REQUIREMENTS:
24
+
25
  PHP 5.2 or greater
26
  GD 2.0
27
  FreeType (Required, for TTF fonts)
30
  SYNOPSIS:
31
 
32
  require_once 'securimage.php';
33
+
34
+ **Within your HTML form**
35
+
36
+ <form metod="post" action="">
37
+ .. form elements
38
+
39
+ <div>
40
+ <?php echo Securimage::getCaptchaHtml() ?>
41
+ </div>
42
+ </form>
43
+
44
+
45
+ **Within your PHP form processor**
46
 
47
  // Code Validation
48
 
49
  $image = new Securimage();
50
+ if ($image->check($_POST['captcha_code']) == true) {
51
  echo "Correct!";
52
  } else {
53
  echo "Sorry, wrong code.";
57
 
58
  What is Securimage?
59
 
60
+ Securimage is a PHP class that is used to generate and validate CAPTCHA
61
+ images.
62
+
63
+ The classes uses an existing PHP session or creates its own if
64
+ none is found to store the CAPTCHA code. In addition, a database can be
65
+ used instead of session storage.
66
+
67
+ Variables within the class are used to control the style and display of
68
+ the image. The class uses TTF fonts and effects for strengthening the
69
+ security of the image.
70
+
71
+ It also creates audible codes which are played for visually impared users.
72
 
73
  COPYRIGHT:
74
+
75
+ Copyright (c) 2014 Drew Phillips
76
  All rights reserved.
77
 
78
+ Redistribution and use in source and binary forms, with or without
79
+ modification, are permitted provided that the following conditions are met:
80
 
81
  - Redistributions of source code must retain the above copyright notice,
82
  this list of conditions and the following disclaimer.
96
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
97
  POSSIBILITY OF SUCH DAMAGE.
98
 
99
+ LICENSES:
100
+
101
+ The WavFile.php class used in Securimage by Drew Phillips and Paul Voegler
102
+ is used under the BSD License. See WavFile.php for details.
103
  Many thanks to Paul Voegler (http://www.voegler.eu/) for contributing to
104
  Securimage.
105
 
106
+ ---------------------------------------------------------------------------
107
+ Flash code created by Age Bosma & Mario Romero (animario@hotmail.com)
108
  Many thanks for releasing this to the project!
109
 
110
+ ---------------------------------------------------------------------------
111
  Portions of Securimage contain code from Han-Kwang Nienhuys' PHP captcha
112
 
113
  Han-Kwang Nienhuys' PHP captcha
119
  The original, unrestricted version can be obtained from
120
  http://www.lagom.nl/linux/hkcaptcha/
121
 
122
+ ---------------------------------------------------------------------------
123
+ AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller
124
+ and is distributed as freeware.
125
 
126
+ Alte Haas Grotesk is a typeface that look like an helvetica printed in an
127
+ old Muller-Brockmann Book.
128
 
129
  These fonts are freeware and can be distributed as long as they are
130
  together with this text file.
135
  www.yannlecoroller.com
136
  yann@lecoroller.com
137
 
138
+ ---------------------------------------------------------------------------
139
+ Portions of securimage_play.swf use the PopForge flash library for
140
+ playing audio
141
 
142
  /**
143
  * Copyright(C) 2007 Andre Michelle and Joa Ebert
144
  *
145
+ * PopForge is an ActionScript3 code sandbox developed by Andre Michelle
146
+ * and Joa Ebert
147
  * http://sandbox.popforge.de
148
  *
149
  * PopforgeAS3Audio is free software; you can redistribute it and/or modify
160
  * along with this program. If not, see <http://www.gnu.org/licenses/>
161
  */
162
 
163
+ --------------------------------------------------------------------------
164
  Some graphics used are from the Humility Icon Pack by WorLord
165
 
166
  License: GNU/GPL (http://findicons.com/pack/1723/humility)
167
  http://findicons.com/icon/192558/gnome_volume_control
168
  http://findicons.com/icon/192562/gtk_refresh
169
 
170
+ --------------------------------------------------------------------------
171
  Background noise sound files are from SoundJay.com
172
  http://www.soundjay.com/tos.html
173
 
202
  If you use the sound effects, please consider giving us a credit and
203
  linking back to us but it's not required.
204
 
 
securimage/WavFile.php CHANGED
@@ -6,7 +6,7 @@
6
  * Project: PHPWavUtils: Classes for creating, reading, and manipulating WAV files in PHP<br />
7
  * File: WavFile.php<br />
8
  *
9
- * Copyright (c) 2012, Drew Phillips
10
  * All rights reserved.
11
  *
12
  * Redistribution and use in source and binary forms, with or without modification,
@@ -36,12 +36,18 @@
36
  * @copyright 2012 Drew Phillips
37
  * @author Drew Phillips <drew@drew-phillips.com>
38
  * @author Paul Voegler <http://www.voegler.eu/>
39
- * @version 1.0 (October 2012)
40
  * @package PHPWavUtils
41
  * @license BSD License
42
  *
43
  * Changelog:
44
- *
 
 
 
 
 
 
45
  * 1.0 (10/2/2012)
46
  * - Fix insertSilence() creating invalid block size
47
  *
@@ -80,6 +86,9 @@ class WavFile
80
  /** @var int Filter flag for degrading audio data */
81
  const FILTER_DEGRADE = 0x04;
82
 
 
 
 
83
  /** @var int Maximum number of channels */
84
  const MAX_CHANNEL = 18;
85
 
@@ -194,6 +203,9 @@ class WavFile
194
  /** @var int Bytes per second */
195
  protected $_byteRate;
196
 
 
 
 
197
  /** @var string Binary string of samples */
198
  protected $_samples;
199
 
@@ -239,6 +251,7 @@ class WavFile
239
  $this->_blockAlign = 1;
240
  $this->_numBlocks = 0;
241
  $this->_byteRate = 8000;
 
242
  $this->_samples = '';
243
  $this->_fp = null;
244
 
@@ -774,6 +787,17 @@ class WavFile
774
  return $this;
775
  }
776
 
 
 
 
 
 
 
 
 
 
 
 
777
  public function getSamples() {
778
  return $this->_samples;
779
  }
@@ -832,7 +856,7 @@ class WavFile
832
  // Wave file methods
833
 
834
  /**
835
- * Construct a wav header from this object. Includes "fact" chunk in necessary.
836
  * http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
837
  *
838
  * @return string The RIFF header data.
@@ -1046,10 +1070,11 @@ class WavFile
1046
  throw new WavFormatException('Not wav format. "RIFF" signature missing.', 2);
1047
  }
1048
 
1049
- if ($actualSize - 8 < $RIFF['ChunkSize']) {
 
 
1050
  trigger_error('"RIFF" chunk size does not match actual file size. Found ' . $RIFF['ChunkSize'] . ', expected ' . ($actualSize - 8) . '.', E_USER_NOTICE);
1051
  $RIFF['ChunkSize'] = $actualSize - 8;
1052
- //throw new WavFormatException('"RIFF" chunk size does not match actual file size. Found ' . $RIFF['ChunkSize'] . ', expected ' . ($actualSize - 8) . '.', 3);
1053
  }
1054
 
1055
  if ($RIFF['Format'] != 0x57415645) { // "WAVE"
@@ -1098,14 +1123,12 @@ class WavFile
1098
  if ($blockAlign != $fmt['BlockAlign']) {
1099
  trigger_error('Invalid block align in "fmt " subchunk. Found ' . $fmt['BlockAlign'] . ', expected ' . $blockAlign . '.', E_USER_NOTICE);
1100
  $fmt['BlockAlign'] = $blockAlign;
1101
- //throw new WavFormatException('Invalid block align in "fmt " subchunk. Found ' . $fmt['BlockAlign'] . ', expected ' . $blockAlign . '.', 17);
1102
  }
1103
 
1104
  $byteRate = $fmt['SampleRate'] * $blockAlign;
1105
  if ($byteRate != $fmt['ByteRate']) {
1106
  trigger_error('Invalid average byte rate in "fmt " subchunk. Found ' . $fmt['ByteRate'] . ', expected ' . $byteRate . '.', E_USER_NOTICE);
1107
  $fmt['ByteRate'] = $byteRate;
1108
- //throw new WavFormatException('Invalid average byte rate in "fmt " subchunk. Found ' . $fmt['ByteRate'] . ', expected ' . $byteRate . '.', 18);
1109
  }
1110
 
1111
  $this->_fmtChunkSize = $fmt['SubchunkSize'];
@@ -1151,13 +1174,11 @@ class WavFile
1151
  if ($extensibleFmt['Size'] != 22) {
1152
  trigger_error('Invaid extension size in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
1153
  $extensibleFmt['Size'] = 22;
1154
- //throw new WavFormatException('Invaid extension size in EXTENSIBLE "fmt " subchunk.', 20);
1155
  }
1156
 
1157
  if ($extensibleFmt['ValidBitsPerSample'] != $fmt['BitsPerSample']) {
1158
  trigger_error('Invaid or unsupported valid bits per sample in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
1159
  $extensibleFmt['ValidBitsPerSample'] = $fmt['BitsPerSample'];
1160
- //throw new WavFormatException('Invaid or unsupported valid bits per sample in EXTENSIBLE "fmt " subchunk.', 21);
1161
  }
1162
 
1163
  if ($extensibleFmt['ChannelMask'] != 0) {
@@ -1171,7 +1192,6 @@ class WavFile
1171
  if ($n != $fmt['NumChannels'] || (((int)$extensibleFmt['ChannelMask'] | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
1172
  trigger_error('Invalid channel mask in EXTENSIBLE "fmt " subchunk. The number of channels does not match the number of locations in the mask.', E_USER_NOTICE);
1173
  $extensibleFmt['ChannelMask'] = 0;
1174
- //throw new WavFormatException('Invalid channel mask in EXTENSIBLE "fmt " subchunk. The number of channels does not match the number of locations in the mask.', 22);
1175
  }
1176
  }
1177
 
@@ -1234,10 +1254,11 @@ class WavFile
1234
 
1235
  // check "data" subchunk
1236
  $dataOffset = ftell($this->_fp);
1237
- if ($dataSubchunk['SubchunkSize'] < 0 || $actualSize - $dataOffset < $dataSubchunk['SubchunkSize']) {
1238
- trigger_error('Invalid "data" subchunk size.', E_USER_NOTICE);
 
 
1239
  $dataSubchunk['SubchunkSize'] = $actualSize - $dataOffset;
1240
- //throw new WavFormatException('Invalid "data" subchunk size.', 104);
1241
  }
1242
 
1243
  $this->_dataOffset = $dataOffset;
@@ -1257,7 +1278,6 @@ class WavFile
1257
  if ($factSubchunk['SampleLength'] != $numBlocks) {
1258
  trigger_error('Invalid sample length in "fact" subchunk.', E_USER_NOTICE);
1259
  $factSubchunk['SampleLength'] = $numBlocks;
1260
- //throw new WavFormatException('Invalid sample length in "fact" subchunk.', 105);
1261
  }
1262
 
1263
  $this->_factChunkSize = $factSubchunk['SubchunkSize'];
@@ -1557,6 +1577,7 @@ class WavFile
1557
  * ),
1558
  * WavFile::FILTER_NORMALIZE => 0.6, // (Required) Normalization of (mixed) audio samples - see threshold parameter for normalizeSample().
1559
  * WavFile::FILTER_DEGRADE => 0.9 // (Required) Introduce random noise. The quality relative to the amplitude. 1 = no noise, 0 = max. noise.
 
1560
  * ),
1561
  * 0, // (Optional) The block number of this WavFile to start with.
1562
  * null // (Optional) The number of blocks to process.
@@ -1626,6 +1647,16 @@ class WavFile
1626
  if ($degrade_quality >= 0 && $degrade_quality < 1) $filter_degrade = true;
1627
  }
1628
 
 
 
 
 
 
 
 
 
 
 
1629
 
1630
  // loop through all sample blocks
1631
  for ($block = 0; $block < $numBlocks; ++$block) {
@@ -1659,6 +1690,10 @@ class WavFile
1659
  $sampleFloat += rand(1000000 * ($degrade_quality - 1), 1000000 * (1 - $degrade_quality)) / 1000000;
1660
  }
1661
 
 
 
 
 
1662
 
1663
  // write current sample
1664
  $this->setSampleValue($sampleFloat, $currentBlock, $channel);
6
  * Project: PHPWavUtils: Classes for creating, reading, and manipulating WAV files in PHP<br />
7
  * File: WavFile.php<br />
8
  *
9
+ * Copyright (c) 2012 - 2014, Drew Phillips
10
  * All rights reserved.
11
  *
12
  * Redistribution and use in source and binary forms, with or without modification,
36
  * @copyright 2012 Drew Phillips
37
  * @author Drew Phillips <drew@drew-phillips.com>
38
  * @author Paul Voegler <http://www.voegler.eu/>
39
+ * @version 1.1 (Feb 2014)
40
  * @package PHPWavUtils
41
  * @license BSD License
42
  *
43
  * Changelog:
44
+ *
45
+ * 1.1 (02/8/2014)
46
+ * - Add method setIgnoreChunkSizes() to allow reading of wav data with bogus chunk sizes set.
47
+ * This allows streamed wav data to be processed where the chunk sizes were not known when
48
+ * writing the header. Instead calculates the chunk sizes automatically.
49
+ * - Add simple volume filter to attenuate or amplify the audio signal.
50
+ *
51
  * 1.0 (10/2/2012)
52
  * - Fix insertSilence() creating invalid block size
53
  *
86
  /** @var int Filter flag for degrading audio data */
87
  const FILTER_DEGRADE = 0x04;
88
 
89
+ /** @var int Filter flag for amplifying or attenuating audio data. */
90
+ const FILTER_VOLUME = 0x08;
91
+
92
  /** @var int Maximum number of channels */
93
  const MAX_CHANNEL = 18;
94
 
203
  /** @var int Bytes per second */
204
  protected $_byteRate;
205
 
206
+ /** @var bool Ignore chunk sizes when reading wav data (useful when reading data from a stream where chunk sizes contain dummy values) */
207
+ protected $_ignoreChunkSizes;
208
+
209
  /** @var string Binary string of samples */
210
  protected $_samples;
211
 
251
  $this->_blockAlign = 1;
252
  $this->_numBlocks = 0;
253
  $this->_byteRate = 8000;
254
+ $this->_ignoreChunkSizes = false;
255
  $this->_samples = '';
256
  $this->_fp = null;
257
 
787
  return $this;
788
  }
789
 
790
+ public function getIgnoreChunkSizes()
791
+ {
792
+ return $this->_ignoreChunkSizes;
793
+ }
794
+
795
+ public function setIgnoreChunkSizes($ignoreChunkSizes)
796
+ {
797
+ $this->_ignoreChunkSizes = (bool)$ignoreChunkSizes;
798
+ return $this;
799
+ }
800
+
801
  public function getSamples() {
802
  return $this->_samples;
803
  }
856
  // Wave file methods
857
 
858
  /**
859
+ * Construct a wav header from this object. Includes "fact" chunk if necessary.
860
  * http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
861
  *
862
  * @return string The RIFF header data.
1070
  throw new WavFormatException('Not wav format. "RIFF" signature missing.', 2);
1071
  }
1072
 
1073
+ if ($this->getIgnoreChunkSizes()) {
1074
+ $RIFF['ChunkSize'] = $actualSize - 8;
1075
+ } else if ($actualSize - 8 < $RIFF['ChunkSize']) {
1076
  trigger_error('"RIFF" chunk size does not match actual file size. Found ' . $RIFF['ChunkSize'] . ', expected ' . ($actualSize - 8) . '.', E_USER_NOTICE);
1077
  $RIFF['ChunkSize'] = $actualSize - 8;
 
1078
  }
1079
 
1080
  if ($RIFF['Format'] != 0x57415645) { // "WAVE"
1123
  if ($blockAlign != $fmt['BlockAlign']) {
1124
  trigger_error('Invalid block align in "fmt " subchunk. Found ' . $fmt['BlockAlign'] . ', expected ' . $blockAlign . '.', E_USER_NOTICE);
1125
  $fmt['BlockAlign'] = $blockAlign;
 
1126
  }
1127
 
1128
  $byteRate = $fmt['SampleRate'] * $blockAlign;
1129
  if ($byteRate != $fmt['ByteRate']) {
1130
  trigger_error('Invalid average byte rate in "fmt " subchunk. Found ' . $fmt['ByteRate'] . ', expected ' . $byteRate . '.', E_USER_NOTICE);
1131
  $fmt['ByteRate'] = $byteRate;
 
1132
  }
1133
 
1134
  $this->_fmtChunkSize = $fmt['SubchunkSize'];
1174
  if ($extensibleFmt['Size'] != 22) {
1175
  trigger_error('Invaid extension size in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
1176
  $extensibleFmt['Size'] = 22;
 
1177
  }
1178
 
1179
  if ($extensibleFmt['ValidBitsPerSample'] != $fmt['BitsPerSample']) {
1180
  trigger_error('Invaid or unsupported valid bits per sample in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
1181
  $extensibleFmt['ValidBitsPerSample'] = $fmt['BitsPerSample'];
 
1182
  }
1183
 
1184
  if ($extensibleFmt['ChannelMask'] != 0) {
1192
  if ($n != $fmt['NumChannels'] || (((int)$extensibleFmt['ChannelMask'] | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
1193
  trigger_error('Invalid channel mask in EXTENSIBLE "fmt " subchunk. The number of channels does not match the number of locations in the mask.', E_USER_NOTICE);
1194
  $extensibleFmt['ChannelMask'] = 0;
 
1195
  }
1196
  }
1197
 
1254
 
1255
  // check "data" subchunk
1256
  $dataOffset = ftell($this->_fp);
1257
+ if ($this->getIgnoreChunkSizes()) {
1258
+ $dataSubchunk['SubchunkSize'] = $actualSize - $dataOffset;
1259
+ } elseif ($dataSubchunk['SubchunkSize'] < 0 || $actualSize - $dataOffset < $dataSubchunk['SubchunkSize']) {
1260
+ trigger_error("Invalid \"data\" subchunk size (found {$dataSubchunk['SubchunkSize']}.", E_USER_NOTICE);
1261
  $dataSubchunk['SubchunkSize'] = $actualSize - $dataOffset;
 
1262
  }
1263
 
1264
  $this->_dataOffset = $dataOffset;
1278
  if ($factSubchunk['SampleLength'] != $numBlocks) {
1279
  trigger_error('Invalid sample length in "fact" subchunk.', E_USER_NOTICE);
1280
  $factSubchunk['SampleLength'] = $numBlocks;
 
1281
  }
1282
 
1283
  $this->_factChunkSize = $factSubchunk['SubchunkSize'];
1577
  * ),
1578
  * WavFile::FILTER_NORMALIZE => 0.6, // (Required) Normalization of (mixed) audio samples - see threshold parameter for normalizeSample().
1579
  * WavFile::FILTER_DEGRADE => 0.9 // (Required) Introduce random noise. The quality relative to the amplitude. 1 = no noise, 0 = max. noise.
1580
+ * WavFile::FILTER_VOLUME => 1.0 // (Required) Amplify or attenuate the audio signal. Beware of clipping when amplifying. Values range from >= 0 - <= 2. 1 = no change in volume; 0.5 = 50% reduction of volume; 1.5 = 150% increase in volume.
1581
  * ),
1582
  * 0, // (Optional) The block number of this WavFile to start with.
1583
  * null // (Optional) The number of blocks to process.
1647
  if ($degrade_quality >= 0 && $degrade_quality < 1) $filter_degrade = true;
1648
  }
1649
 
1650
+ $filter_vol = false;
1651
+ if (array_key_exists(self::FILTER_VOLUME, $filters)) {
1652
+ $volume_amount = @$filters[self::FILTER_VOLUME];
1653
+ if (is_null($volume_amount)) $volume_amount = 1;
1654
+
1655
+ if ($volume_amount >= 0 && $volume_amount <= 2 && $volume_amount != 1.0) {
1656
+ $filter_vol = true;
1657
+ }
1658
+ }
1659
+
1660
 
1661
  // loop through all sample blocks
1662
  for ($block = 0; $block < $numBlocks; ++$block) {
1690
  $sampleFloat += rand(1000000 * ($degrade_quality - 1), 1000000 * (1 - $degrade_quality)) / 1000000;
1691
  }
1692
 
1693
+ /************* VOLUME FILTER *******************/
1694
+ if ($filter_vol) {
1695
+ $sampleFloat *= $volume_amount;
1696
+ }
1697
 
1698
  // write current sample
1699
  $this->setSampleValue($sampleFloat, $currentBlock, $channel);
securimage/audio/.htaccess ADDED
@@ -0,0 +1 @@
 
1
+ deny from all
securimage/audio/en/0.wav CHANGED
Binary file
securimage/audio/en/1.wav CHANGED
Binary file
securimage/audio/en/10.wav CHANGED
Binary file
securimage/audio/en/11.wav CHANGED
Binary file
securimage/audio/en/12.wav CHANGED
Binary file
securimage/audio/en/13.wav CHANGED
Binary file
securimage/audio/en/14.wav CHANGED
Binary file
securimage/audio/en/15.wav CHANGED
Binary file
securimage/audio/en/16.wav CHANGED
Binary file
securimage/audio/en/17.wav CHANGED
Binary file
securimage/audio/en/18.wav CHANGED
Binary file
securimage/audio/en/19.wav CHANGED
Binary file
securimage/audio/en/2.wav CHANGED
Binary file
securimage/audio/en/20.wav CHANGED
Binary file
securimage/audio/en/3.wav CHANGED
Binary file
securimage/audio/en/4.wav CHANGED
Binary file
securimage/audio/en/5.wav CHANGED
Binary file
securimage/audio/en/6.wav CHANGED
Binary file
securimage/audio/en/7.wav CHANGED
Binary file
securimage/audio/en/8.wav CHANGED
Binary file
securimage/audio/en/9.wav CHANGED
Binary file
securimage/audio/en/A.wav CHANGED
Binary file
securimage/audio/en/B.wav CHANGED
Binary file
securimage/audio/en/C.wav CHANGED
Binary file
securimage/audio/en/D.wav CHANGED
Binary file
securimage/audio/en/E.wav CHANGED
Binary file
securimage/audio/en/F.wav CHANGED
Binary file
securimage/audio/en/G.wav CHANGED
Binary file
securimage/audio/en/H.wav CHANGED
Binary file
securimage/audio/en/I.wav CHANGED
Binary file
securimage/audio/en/J.wav CHANGED
Binary file
securimage/audio/en/K.wav CHANGED
Binary file
securimage/audio/en/L.wav CHANGED
Binary file
securimage/audio/en/M.wav CHANGED
Binary file
securimage/audio/en/N.wav CHANGED
Binary file
securimage/audio/en/O.wav CHANGED
Binary file
securimage/audio/en/P.wav CHANGED
Binary file
securimage/audio/en/Q.wav CHANGED
Binary file
securimage/audio/en/R.wav CHANGED
Binary file
securimage/audio/en/S.wav CHANGED
Binary file
securimage/audio/en/T.wav CHANGED
Binary file
securimage/audio/en/U.wav CHANGED
Binary file
securimage/audio/en/V.wav CHANGED
Binary file
securimage/audio/en/W.wav CHANGED
Binary file
securimage/audio/en/X.wav CHANGED
Binary file
securimage/audio/en/Y.wav CHANGED
Binary file
securimage/audio/en/Z.wav CHANGED
Binary file
securimage/example_form.ajax.php CHANGED
@@ -87,7 +87,7 @@ process_si_contact_form();
87
 
88
  function reloadCaptcha()
89
  {
90
- jQuery('#siimage').src = './securimage_show.php?sid=' + Math.random();
91
  }
92
 
93
  function processForm()
@@ -102,7 +102,7 @@ process_si_contact_form();
102
  jQuery('#success_message').show();
103
  jQuery('#contact_form')[0].reset();
104
  reloadCaptcha();
105
- setTimeout("jQuery('#success_message').fadeOut()", 30000);
106
  } else {
107
  alert("There was an error with your submission.\n\n" + data.message);
108
  }
87
 
88
  function reloadCaptcha()
89
  {
90
+ jQuery('#siimage').prop('src', './securimage_show.php?sid=' + Math.random());
91
  }
92
 
93
  function processForm()
102
  jQuery('#success_message').show();
103
  jQuery('#contact_form')[0].reset();
104
  reloadCaptcha();
105
+ setTimeout("jQuery('#success_message').fadeOut()", 12000);
106
  } else {
107
  alert("There was an error with your submission.\n\n" + data.message);
108
  }
securimage/example_form.php CHANGED
@@ -5,7 +5,7 @@ $GLOBALS['DEBUG_MODE'] = 1;
5
  // CHANGE TO 0 TO TURN OFF DEBUG MODE
6
  // IN DEBUG MODE, ONLY THE CAPTCHA CODE IS VALIDATED, AND NO EMAIL IS SENT
7
 
8
- $GLOBALS['ct_recipient'] = 'YOU@EXAMPLE.COM'; // Change to your email address!
9
  $GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
10
 
11
  ?>
@@ -16,8 +16,10 @@ $GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
16
  <title>Securimage Example Form</title>
17
  <style type="text/css">
18
  <!--
19
- .error { color: #f00; font-weight: bold; font-size: 1.2em; }
 
20
  .success { color: #00f; font-weight: bold; font-size: 1.2em; }
 
21
  fieldset { width: 90%; }
22
  legend { font-size: 24px; }
23
  .note { font-size: 18px;
@@ -39,44 +41,52 @@ $GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
39
  process_si_contact_form(); // Process the form, if it was submitted
40
 
41
  if (isset($_SESSION['ctform']['error']) && $_SESSION['ctform']['error'] == true): /* The last form submission had 1 or more errors */ ?>
42
- <span class="error">There was a problem with your submission. Errors are displayed below in red.</span><br /><br />
43
  <?php elseif (isset($_SESSION['ctform']['success']) && $_SESSION['ctform']['success'] == true): /* form was processed successfully */ ?>
44
- <span class="success">The captcha was correct and the message has been sent!</span><br /><br />
45
  <?php endif; ?>
46
 
47
  <form method="post" action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']) ?>" id="contact_form">
48
  <input type="hidden" name="do" value="contact" />
49
 
50
  <p>
51
- <strong>Name*:</strong>&nbsp; &nbsp;<?php echo @$_SESSION['ctform']['name_error'] ?><br />
 
52
  <input type="text" name="ct_name" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_name']) ?>" />
53
  </p>
54
 
55
  <p>
56
- <strong>Email*:</strong>&nbsp; &nbsp;<?php echo @$_SESSION['ctform']['email_error'] ?><br />
 
57
  <input type="text" name="ct_email" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_email']) ?>" />
58
  </p>
59
 
60
  <p>
61
- <strong>URL:</strong>&nbsp; &nbsp;<?php echo @$_SESSION['ctform']['URL_error'] ?><br />
 
62
  <input type="text" name="ct_URL" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_URL']) ?>" />
63
  </p>
64
 
65
  <p>
66
- <strong>Message*:</strong>&nbsp; &nbsp;<?php echo @$_SESSION['ctform']['message_error'] ?><br />
 
67
  <textarea name="ct_message" rows="12" cols="60"><?php echo htmlspecialchars(@$_SESSION['ctform']['ct_message']) ?></textarea>
68
  </p>
69
 
70
  <p>
71
- <img id="siimage" style="border: 1px solid #000; margin-right: 15px" src="./securimage_show.php?sid=<?php echo md5(uniqid()) ?>" alt="CAPTCHA Image" align="left" />
72
- <object type="application/x-shockwave-flash" data="./securimage_play.swf?bgcol=#ffffff&amp;icon_file=./images/audio_icon.png&amp;audio_file=./securimage_play.php" height="32" width="32">
73
- <param name="movie" value="./securimage_play.swf?bgcol=#ffffff&amp;icon_file=./images/audio_icon.png&amp;audio_file=./securimage_play.php" />
74
- </object>
75
- &nbsp;
76
- <a tabindex="-1" style="border-style: none;" href="#" title="Refresh Image" onclick="document.getElementById('siimage').src = './securimage_show.php?sid=' + Math.random(); this.blur(); return false"><img src="./images/refresh.png" alt="Reload Image" height="32" width="32" onclick="this.blur()" align="bottom" border="0" /></a><br />
77
- <strong>Enter Code*:</strong><br />
78
- <?php echo @$_SESSION['ctform']['captcha_error'] ?>
79
- <input type="text" name="ct_captcha" size="12" maxlength="16" />
 
 
 
 
80
  </p>
81
 
82
  <p>
@@ -135,7 +145,7 @@ function process_si_contact_form()
135
 
136
  if (strlen($message) < 20) {
137
  // message length too short
138
- $errors['message_error'] = 'Please enter a message';
139
  }
140
  }
141
 
@@ -154,22 +164,23 @@ function process_si_contact_form()
154
  // no errors, send the form
155
  $time = date('r');
156
  $message = "A message was submitted from the contact form. The following information was provided.<br /><br />"
157
- . "Name: $name<br />"
158
- . "Email: $email<br />"
159
- . "URL: $URL<br />"
160
- . "Message:<br />"
161
  . "<pre>$message</pre>"
162
- . "<br /><br />IP Address: {$_SERVER['REMOTE_ADDR']}<br />"
163
- . "Time: $time<br />"
164
- . "Browser: {$_SERVER['HTTP_USER_AGENT']}<br />";
165
 
166
  $message = wordwrap($message, 70);
167
 
168
  if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
169
  // send the message with mail()
170
- mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=ISO-8859-1\r\nMIME-Version: 1.0");
171
  }
172
 
 
173
  $_SESSION['ctform']['error'] = false; // no error with form
174
  $_SESSION['ctform']['success'] = true; // message sent
175
  } else {
@@ -181,7 +192,7 @@ function process_si_contact_form()
181
 
182
  foreach($errors as $key => $error) {
183
  // set up error messages to display with each field
184
- $_SESSION['ctform'][$key] = "<span style=\"font-weight: bold; color: #f00\">$error</span>";
185
  }
186
 
187
  $_SESSION['ctform']['error'] = true; // set error floag
5
  // CHANGE TO 0 TO TURN OFF DEBUG MODE
6
  // IN DEBUG MODE, ONLY THE CAPTCHA CODE IS VALIDATED, AND NO EMAIL IS SENT
7
 
8
+ $GLOBALS['ct_recipient'] = 'YOU@EXAMPLE.COM'; // Change to your email address! Make sure DEBUG_MODE above is 0 for mail to send!
9
  $GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
10
 
11
  ?>
16
  <title>Securimage Example Form</title>
17
  <style type="text/css">
18
  <!--
19
+ div.error { display: block; color: #f00; font-weight: bold; font-size: 1.2em; }
20
+ span.error { display: block; color: #f00; font-style: italic; }
21
  .success { color: #00f; font-weight: bold; font-size: 1.2em; }
22
+ form label { display: block; font-weight: bold; }
23
  fieldset { width: 90%; }
24
  legend { font-size: 24px; }
25
  .note { font-size: 18px;
41
  process_si_contact_form(); // Process the form, if it was submitted
42
 
43
  if (isset($_SESSION['ctform']['error']) && $_SESSION['ctform']['error'] == true): /* The last form submission had 1 or more errors */ ?>
44
+ <div class="error">There was a problem with your submission. Errors are displayed below in red.</div><br />
45
  <?php elseif (isset($_SESSION['ctform']['success']) && $_SESSION['ctform']['success'] == true): /* form was processed successfully */ ?>
46
+ <div class="success">The captcha was correct and the message has been sent! The captcha was solved in <?php echo $_SESSION['ctform']['timetosolve'] ?> seconds.</div><br />
47
  <?php endif; ?>
48
 
49
  <form method="post" action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']) ?>" id="contact_form">
50
  <input type="hidden" name="do" value="contact" />
51
 
52
  <p>
53
+ <label for="ct_name">Name*:</label>
54
+ <?php echo @$_SESSION['ctform']['name_error'] ?>
55
  <input type="text" name="ct_name" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_name']) ?>" />
56
  </p>
57
 
58
  <p>
59
+ <label for="ct_email">Email*:</label>
60
+ <?php echo @$_SESSION['ctform']['email_error'] ?>
61
  <input type="text" name="ct_email" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_email']) ?>" />
62
  </p>
63
 
64
  <p>
65
+ <label for="ct_URL">URL:</label>
66
+ <?php echo @$_SESSION['ctform']['URL_error'] ?>
67
  <input type="text" name="ct_URL" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_URL']) ?>" />
68
  </p>
69
 
70
  <p>
71
+ <label for="ct_message">Message*:</label>
72
+ <?php echo @$_SESSION['ctform']['message_error'] ?>
73
  <textarea name="ct_message" rows="12" cols="60"><?php echo htmlspecialchars(@$_SESSION['ctform']['ct_message']) ?></textarea>
74
  </p>
75
 
76
  <p>
77
+ <?php
78
+ // show captcha HTML using Securimage::getCaptchaHtml()
79
+ require_once 'securimage.php';
80
+ $options = array();
81
+ $options['input_name'] = 'ct_captcha'; // change name of input element for form post
82
+
83
+ if (!empty($_SESSION['ctform']['captcha_error'])) {
84
+ // error html to show in captcha output
85
+ $options['error_html'] = $_SESSION['ctform']['captcha_error'];
86
+ }
87
+
88
+ echo Securimage::getCaptchaHtml($options);
89
+ ?>
90
  </p>
91
 
92
  <p>
145
 
146
  if (strlen($message) < 20) {
147
  // message length too short
148
+ $errors['message_error'] = 'Your message must be longer than 20 characters';
149
  }
150
  }
151
 
164
  // no errors, send the form
165
  $time = date('r');
166
  $message = "A message was submitted from the contact form. The following information was provided.<br /><br />"
167
+ . "<em>Name: $name</em><br />"
168
+ . "<em>Email: $email</em><br />"
169
+ . "<em>URL: $URL</em><br />"
170
+ . "<em>Message:</em><br />"
171
  . "<pre>$message</pre>"
172
+ . "<br /><br /><em>IP Address:</em> {$_SERVER['REMOTE_ADDR']}<br />"
173
+ . "<em>Time:</em> $time<br />"
174
+ . "<em>Browser:</em> {$_SERVER['HTTP_USER_AGENT']}<br />";
175
 
176
  $message = wordwrap($message, 70);
177
 
178
  if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
179
  // send the message with mail()
180
+ mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=UTF-8\r\nMIME-Version: 1.0");
181
  }
182
 
183
+ $_SESSION['ctform']['timetosolve'] = $securimage->getTimeToSolve();
184
  $_SESSION['ctform']['error'] = false; // no error with form
185
  $_SESSION['ctform']['success'] = true; // message sent
186
  } else {
192
 
193
  foreach($errors as $key => $error) {
194
  // set up error messages to display with each field
195
+ $_SESSION['ctform'][$key] = "<span class=\"error\">$error</span>";
196
  }
197
 
198
  $_SESSION['ctform']['error'] = true; // set error floag
securimage/securimage.php CHANGED
@@ -6,7 +6,7 @@
6
  * Project: Securimage: A PHP class for creating and managing form CAPTCHA images<br />
7
  * File: securimage.php<br />
8
  *
9
- * Copyright (c) 2013, Drew Phillips
10
  * All rights reserved.
11
  *
12
  * Redistribution and use in source and binary forms, with or without modification,
@@ -39,9 +39,9 @@
39
  * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
40
  * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
41
  * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
42
- * @copyright 2013 Drew Phillips
43
  * @author Drew Phillips <drew@drew-phillips.com>
44
- * @version 3.5.1 (June 21, 2013)
45
  * @package Securimage
46
  *
47
  */
@@ -49,6 +49,20 @@
49
  /**
50
  ChangeLog
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  3.5.1
53
  - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>)
54
 
@@ -156,7 +170,7 @@
156
  /**
157
  * Securimage CAPTCHA Class.
158
  *
159
- * @version 3.5
160
  * @package Securimage
161
  * @subpackage classes
162
  * @author Drew Phillips <drew@drew-phillips.com>
@@ -234,6 +248,16 @@ class Securimage
234
  * @var int
235
  */
236
  public $image_height = 80;
 
 
 
 
 
 
 
 
 
 
237
  /**
238
  * The type of the image, default = png
239
  * @var int
@@ -289,7 +313,7 @@ class Securimage
289
  public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
290
  /**
291
  * How long in seconds a captcha remains valid, after this time it will not be accepted
292
- * @var unknown_type
293
  */
294
  public $expiry_time = 900;
295
 
@@ -357,6 +381,14 @@ class Securimage
357
  */
358
  public $use_database = false;
359
 
 
 
 
 
 
 
 
 
360
  /**
361
  * Database driver to use for database support.
362
  * Allowable values: 'mysql', 'pgsql', 'sqlite'.
@@ -470,6 +502,22 @@ class Securimage
470
  * </code>
471
  */
472
  public $audio_path;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  /**
474
  * The path to the directory containing audio files that will be selected
475
  * randomly and mixed with the captcha audio.
@@ -508,7 +556,7 @@ class Securimage
508
  * @since 3.0.4
509
  * @var float
510
  */
511
- public $audio_mix_normalization = 0.6;
512
  /**
513
  * Whether or not to degrade audio by introducing random noise (improves security of audio captcha)
514
  * Default: true
@@ -530,7 +578,7 @@ class Securimage
530
  * @since 3.0.3
531
  * @var float
532
  */
533
- public $audio_gap_max = 600;
534
 
535
  /**
536
  * Captcha ID if using static captcha
@@ -576,6 +624,13 @@ class Securimage
576
  */
577
  protected $captcha_code;
578
 
 
 
 
 
 
 
 
579
  /**
580
  * Flag that can be specified telling securimage not to call exit after generating a captcha image or audio file
581
  *
@@ -705,7 +760,7 @@ class Securimage
705
 
706
  if ($this->no_session != true) {
707
  // Initialize session or attach to existing
708
- if ( session_id() == '' ) { // no session has been started yet, which is needed for validation
709
  if (!is_null($this->session_name) && trim($this->session_name) != '') {
710
  session_name(trim($this->session_name)); // set session name if provided
711
  }
@@ -835,6 +890,162 @@ class Securimage
835
  return $this->correct_code;
836
  }
837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
  /**
839
  * Output a wav file of the captcha code to the browser
840
  *
@@ -853,13 +1064,13 @@ class Securimage
853
  try {
854
  $audio = $this->getAudibleCode();
855
  } catch (Exception $ex) {
856
- if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) {
857
- fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
858
- fclose($fp);
859
  }
860
 
861
  $audio = $this->audioError();
862
- }
863
 
864
  if ($this->canSendHeaders() || $this->send_headers == false) {
865
  if ($this->send_headers) {
@@ -890,52 +1101,58 @@ class Securimage
890
  if (!$this->no_exit) exit;
891
  }
892
 
893
- /**
894
- * Return the code from the session or sqlite database if used. If none exists yet, an empty string is returned
895
- *
896
- * @param $array bool True to receive an array containing the code and properties
897
- * @return array|string Array if $array = true, otherwise a string containing the code
898
- */
899
- public function getCode($array = false, $returnExisting = false)
900
- {
901
- $code = '';
902
  $time = 0;
903
  $disp = 'error';
904
 
905
  if ($returnExisting && strlen($this->code) > 0) {
906
  if ($array) {
907
- return array('code' => $this->code,
908
- 'display' => $this->code_display,
909
- 'code_display' => $this->code_display,
910
- 'time' => 0);
 
911
  } else {
912
  return $this->code;
913
  }
914
- }
915
-
916
- if ($this->no_session != true) {
917
- if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
918
- trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
919
- if ($this->isCodeExpired(
920
- $_SESSION['securimage_code_ctime'][$this->namespace]) == false) {
921
- $code = $_SESSION['securimage_code_value'][$this->namespace];
922
- $time = $_SESSION['securimage_code_ctime'][$this->namespace];
923
- $disp = $_SESSION['securimage_code_disp'] [$this->namespace];
924
- }
925
  }
926
  }
927
 
928
- if (empty($code) && $this->use_database) {
929
- // no code in session - may mean user has cookies turned off
930
- $this->openDatabase();
931
- $code = $this->getCodeFromDatabase();
932
- } else { /* no code stored in session or sqlite database, validation will fail */ }
933
-
934
- if ($array == true) {
935
- return array('code' => $code, 'ctime' => $time, 'display' => $disp);
936
- } else {
937
- return $code;
938
- }
 
 
 
 
 
939
  }
940
 
941
  /**
@@ -1189,12 +1406,17 @@ class Securimage
1189
  {
1190
  $width2 = $this->image_width * $this->iscale;
1191
  $height2 = $this->image_height * $this->iscale;
 
 
 
 
 
1192
 
1193
  if (!is_readable($this->ttf_file)) {
1194
  imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
1195
  } else {
1196
  if ($this->perturbation > 0) {
1197
- $font_size = $height2 * .4;
1198
  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1199
  $tx = $bb[4] - $bb[0];
1200
  $ty = $bb[5] - $bb[1];
@@ -1203,7 +1425,7 @@ class Securimage
1203
 
1204
  imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1205
  } else {
1206
- $font_size = $this->image_height * .4;
1207
  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1208
  $tx = $bb[4] - $bb[0];
1209
  $ty = $bb[5] - $bb[1];
@@ -1378,24 +1600,24 @@ class Securimage
1378
  header("Pragma: no-cache");
1379
  }
1380
 
1381
- switch ($this->image_type) {
1382
- case self::SI_IMAGE_JPEG:
1383
- if ($this->send_headers) header("Content-Type: image/jpeg");
1384
- imagejpeg($this->im, null, 90);
1385
- break;
1386
- case self::SI_IMAGE_GIF:
1387
- if ($this->send_headers) header("Content-Type: image/gif");
1388
- imagegif($this->im);
1389
- break;
1390
- default:
1391
- if ($this->send_headers) header("Content-Type: image/png");
1392
- imagepng($this->im);
1393
- break;
1394
  }
1395
- } else {
1396
- echo '<hr /><strong>'
1397
- .'Failed to generate captcha image, content has already been '
1398
- .'output.<br />This is most likely due to misconfiguration or '
1399
  .'a PHP error was sent to the browser.</strong>';
1400
  }
1401
 
@@ -1415,7 +1637,7 @@ class Securimage
1415
  $letters = array();
1416
  $code = $this->getCode(true, true);
1417
 
1418
- if ($code['code'] == '') {
1419
  if (strlen($this->display_value) > 0) {
1420
  $code = array('code' => $this->display_value, 'display' => $this->display_value);
1421
  } else {
@@ -1424,6 +1646,12 @@ class Securimage
1424
  }
1425
  }
1426
 
 
 
 
 
 
 
1427
  if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
1428
  $math = true;
1429
 
@@ -1520,13 +1748,24 @@ class Securimage
1520
  protected function validate()
1521
  {
1522
  if (!is_string($this->code) || strlen($this->code) == 0) {
1523
- $code = $this->getCode();
1524
  // returns stored code, or an empty string if no stored code was found
1525
  // checks the session and database if enabled
1526
  } else {
1527
  $code = $this->code;
1528
  }
1529
 
 
 
 
 
 
 
 
 
 
 
 
1530
  if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
1531
  // case sensitive was set from securimage_show.php but not in class
1532
  // the code saved in the session has capitals so set case sensitive to true
@@ -1548,6 +1787,7 @@ class Securimage
1548
  if ($code == $code_entered) {
1549
  $this->correct_code = true;
1550
  if ($this->no_session != true) {
 
1551
  $_SESSION['securimage_code_value'][$this->namespace] = '';
1552
  $_SESSION['securimage_code_ctime'][$this->namespace] = '';
1553
  }
@@ -1609,8 +1849,16 @@ class Securimage
1609
  $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
1610
 
1611
  if (!$success) {
1612
- $err = $stmt->errorInfo();
1613
- trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING);
 
 
 
 
 
 
 
 
1614
  }
1615
  }
1616
 
@@ -1649,18 +1897,21 @@ class Securimage
1649
  }
1650
  }
1651
 
1652
- $dsn = $this->getDsn();
1653
-
1654
  try {
1655
- $options = array();
 
 
1656
  $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
1657
  } catch (PDOException $pdoex) {
1658
  trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
1659
  return false;
 
 
 
1660
  }
1661
 
1662
  try {
1663
- if (!$this->checkTablesExist()) {
1664
  // create tables...
1665
  $this->createDatabaseTables();
1666
  }
@@ -1688,6 +1939,12 @@ class Securimage
1688
 
1689
  case self::SI_DRIVER_MYSQL:
1690
  case self::SI_DRIVER_PGSQL:
 
 
 
 
 
 
1691
  $dsn .= sprintf('host=%s;dbname=%s',
1692
  $this->database_host,
1693
  $this->database_name);
@@ -1807,8 +2064,7 @@ class Securimage
1807
  * Get a code from the sqlite database for ip address/captchaId.
1808
  *
1809
  * @return string|array Empty string if no code was found or has expired,
1810
- * otherwise returns the stored captcha code. If a captchaId is set, this
1811
- * returns an array with indices "code" and "code_disp"
1812
  */
1813
  protected function getCodeFromDatabase()
1814
  {
@@ -1835,13 +2091,11 @@ class Securimage
1835
  } else {
1836
  if ( ($row = $stmt->fetch()) !== false ) {
1837
  if (false == $this->isCodeExpired($row['created'])) {
1838
- if (Securimage::$_captchaId !== null) {
1839
- // return an array when using captchaId
1840
- $code = array('code' => $row['code'],
1841
- 'code_disp' => $row['code_display']);
1842
- } else {
1843
- $code = $row['code'];
1844
- }
1845
  }
1846
  }
1847
  }
@@ -1922,11 +2176,30 @@ class Securimage
1922
  $wavCaptcha = new WavFile();
1923
  $first = true; // reading first wav file
1924
 
 
 
 
 
1925
  foreach ($letters as $letter) {
1926
  $letter = strtoupper($letter);
1927
 
1928
  try {
1929
- $l = new WavFile($this->audio_path . '/' . $letter . '.wav');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1930
 
1931
  if ($first) {
1932
  // set sample rate, bits/sample, and # of channels for file based on first letter
@@ -1943,10 +2216,10 @@ class Securimage
1943
  if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
1944
  $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
1945
  }
1946
- } catch (Exception $ex) {
1947
  // failed to open file, or the wav file is broken or not supported
1948
  // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
1949
- throw $ex;
1950
  }
1951
  }
1952
 
@@ -1955,9 +2228,27 @@ class Securimage
1955
 
1956
  if ($this->audio_use_noise == true) {
1957
  // use background audio - find random file
1958
- $noiseFile = $this->getRandomNoiseFile();
1959
-
1960
- if ($noiseFile !== false && is_readable($noiseFile)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1961
  try {
1962
  $wavNoise = new WavFile($noiseFile, false);
1963
  } catch(Exception $ex) {
@@ -1966,7 +2257,9 @@ class Securimage
1966
 
1967
  // start at a random offset from the beginning of the wavfile
1968
  // in order to add more randomness
 
1969
  $randOffset = 0;
 
1970
  if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
1971
  $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
1972
  $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
@@ -1974,8 +2267,9 @@ class Securimage
1974
  $wavNoise->readWavData();
1975
  $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
1976
  }
 
1977
 
1978
-
1979
  $mixOpts = array('wav' => $wavNoise,
1980
  'loop' => true,
1981
  'blockOffset' => $randOffset);
@@ -2017,12 +2311,136 @@ class Securimage
2017
  if (sizeof($list) > 0) {
2018
  $file = $list[array_rand($list, 1)];
2019
  $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
 
 
2020
  }
2021
  }
2022
 
2023
  return $return;
2024
  }
2025
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2026
  /**
2027
  * Return a wav file saying there was an error generating file
2028
  *
@@ -2038,18 +2456,18 @@ class Securimage
2038
  *
2039
  * @return bool true if headers haven't been sent and no output/errors will break audio/images, false if unsafe
2040
  */
2041
- protected function canSendHeaders()
2042
- {
2043
- if (headers_sent()) {
2044
- // output has been flushed and headers have already been sent
2045
- return false;
2046
- } else if (strlen((string)ob_get_contents()) > 0) {
2047
- // headers haven't been sent, but there is data in the buffer that will break image and audio data
2048
- return false;
2049
- }
2050
-
2051
- return true;
2052
- }
2053
 
2054
  /**
2055
  * Return a random float between 0 and 0.9999
6
  * Project: Securimage: A PHP class for creating and managing form CAPTCHA images<br />
7
  * File: securimage.php<br />
8
  *
9
+ * Copyright (c) 2014, Drew Phillips
10
  * All rights reserved.
11
  *
12
  * Redistribution and use in source and binary forms, with or without modification,
39
  * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
40
  * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
41
  * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
42
+ * @copyright 2014 Drew Phillips
43
  * @author Drew Phillips <drew@drew-phillips.com>
44
+ * @version 3.5.2 (Feb 15, 2014)
45
  * @package Securimage
46
  *
47
  */
49
  /**
50
  ChangeLog
51
 
52
+ 3.5.2
53
+
54
+ - Add Securimage::getCaptchaHtml() for getting automatically generated captcha html code
55
+ - Option for using SoX to add effects to captcha audio to make identification by neural networks more difficult
56
+ - Add setNamespace() method
57
+ - Add getTimeToSolve() method
58
+ - Add session_status() check so session still starts if one had previously been opened and closed
59
+ - Add .htaccess file to audio directory to deny access; update audio files
60
+ - Option to skip checking of database tables during connection
61
+ - Add composer.json to package, submit to packagist
62
+ - Add font_ration variable to determine size of font (github.com/wilkor)
63
+ - Add hint if sqlite3 database is not writeable. Improve database error handling, add example database options to securimage_play.php
64
+ - Fixed issue regarding database storage and math captcha breaking audio output (github.com/SoftwareAndOutsourcing)
65
+
66
  3.5.1
67
  - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>)
68
 
170
  /**
171
  * Securimage CAPTCHA Class.
172
  *
173
+ * @version 3.5.2
174
  * @package Securimage
175
  * @subpackage classes
176
  * @author Drew Phillips <drew@drew-phillips.com>
248
  * @var int
249
  */
250
  public $image_height = 80;
251
+
252
+ /**
253
+ * Font size is calculated by image height and this ratio - leave blank for default ratio of 0.4.
254
+ * Valid range: 0.1 - 0.99.
255
+ * Depending on image_width, values > 0.6 are probably too large and values < 0.3 are too small.
256
+ *
257
+ * @var float
258
+ */
259
+ public $font_ratio;
260
+
261
  /**
262
  * The type of the image, default = png
263
  * @var int
313
  public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
314
  /**
315
  * How long in seconds a captcha remains valid, after this time it will not be accepted
316
+ * @var int
317
  */
318
  public $expiry_time = 900;
319
 
381
  */
382
  public $use_database = false;
383
 
384
+ /**
385
+ * Whether or not to skip checking if Securimage tables exist when using a database.
386
+ * Turn this to true once database functionality is working to improve performance.
387
+ *
388
+ * @var bool true to not check if captcha_codes tables are set up, false to check (and create if necessary)
389
+ */
390
+ public $skip_table_check = false;
391
+
392
  /**
393
  * Database driver to use for database support.
394
  * Allowable values: 'mysql', 'pgsql', 'sqlite'.
502
  * </code>
503
  */
504
  public $audio_path;
505
+
506
+ /**
507
+ * Use SoX (The Swiss Army knife of audio manipulation) for audio effects and processing
508
+ *
509
+ * @see Securimage::$sox_binary_path
510
+ * @var bool true to use SoX, false to use PHP
511
+ */
512
+ public $audio_use_sox = false;
513
+
514
+ /**
515
+ * The path to the SoX binary on your system
516
+ *
517
+ * @var string
518
+ */
519
+ public $sox_binary_path = '/usr/bin/sox';
520
+
521
  /**
522
  * The path to the directory containing audio files that will be selected
523
  * randomly and mixed with the captcha audio.
556
  * @since 3.0.4
557
  * @var float
558
  */
559
+ public $audio_mix_normalization = 0.8;
560
  /**
561
  * Whether or not to degrade audio by introducing random noise (improves security of audio captcha)
562
  * Default: true
578
  * @since 3.0.3
579
  * @var float
580
  */
581
+ public $audio_gap_max = 3000;
582
 
583
  /**
584
  * Captcha ID if using static captcha
624
  */
625
  protected $captcha_code;
626
 
627
+ /**
628
+ * Time (in seconds) that the captcha was solved in (correctly or incorrectly). This is from the time of code creation, to when validation was attempted.
629
+ *
630
+ * @var int
631
+ */
632
+ protected $_timeToSolve = 0;
633
+
634
  /**
635
  * Flag that can be specified telling securimage not to call exit after generating a captcha image or audio file
636
  *
760
 
761
  if ($this->no_session != true) {
762
  // Initialize session or attach to existing
763
+ if ( session_id() == '' || (function_exists('session_status') && PHP_SESSION_NONE == session_status()) ) { // no session has been started yet (or it was previousy closed), which is needed for validation
764
  if (!is_null($this->session_name) && trim($this->session_name) != '') {
765
  session_name(trim($this->session_name)); // set session name if provided
766
  }
890
  return $this->correct_code;
891
  }
892
 
893
+ /**
894
+ * Returns HTML for displaying the captcha image, audio button, and text input for forms
895
+ *
896
+ * @param array $options Array of options for modifying the HTML code. Accepted options are:
897
+ * 'securimage_path' => Optional: The URI to where securimage is installed (e.g. /securimage)
898
+ * 'image_id' => A string that sets the "id" attribute of the captcha image (default: captcha_image)
899
+ * 'image_alt_text' => The alt text of the captcha image (default: CAPTCHA Image)
900
+ * 'show_audio_button' => true/false Whether or not to show the audio button (default: true)
901
+ * 'show_refresh_button' => true/false Whether or not to show a button to refresh the image (default: true)
902
+ * 'show_text_input' => true/false Whether or not to show the text input for the captcha (default: true)
903
+ * 'refresh_alt_text' => Alt text for the refresh image (default: Refresh Image)
904
+ * 'refresh_title_text' => Title text for the refresh image link (default: Refresh Image)
905
+ * 'input_id' => A string that sets the "id" attribute of the captcha text input (default: captcha_code)
906
+ * 'input_name' => A string that sets the "name" attribute of the captcha text input (default: same as input_id)
907
+ * 'input_text' => A string that sets the text of the label for the captcha text input (default: Type the text:)
908
+ * 'input_attributes' => An array of additional HTML tag attributes to pass to the text input tag (default: empty)
909
+ * 'image_attributes' => An array of additional HTML tag attributes to pass to the captcha image tag (default: empty)
910
+ * 'namespace' => The optional captcha namespace to use for showing the image and playing back the audio. Namespaces are for using multiple captchas on the same page.
911
+ *
912
+ * @return string The generated HTML code for the captcha image
913
+ */
914
+ public static function getCaptchaHtml($options = array())
915
+ {
916
+ if (!isset($options['securimage_path'])) {
917
+ $docroot = (isset($_SERVER['DOCUMENT_ROOT'])) ? $_SERVER['DOCUMENT_ROOT'] : substr($_SERVER['SCRIPT_FILENAME'], 0, -strlen($_SERVER['SCRIPT_NAME']));
918
+ $docroot = realpath($docroot);
919
+ $sipath = dirname(__FILE__);
920
+ $securimage_path = str_replace($docroot, '', $sipath);
921
+ } else {
922
+ $securimage_path = $options['securimage_path'];
923
+ }
924
+
925
+ $image_id = (isset($options['image_id'])) ? $options['image_id'] : 'captcha_image';
926
+ $image_alt = (isset($options['image_alt_text'])) ? $options['image_alt_text'] : 'CAPTCHA Image';
927
+ $show_audio_btn = (isset($options['show_audio_button'])) ? (bool)$options['show_audio_button'] : true;
928
+ $show_refresh_btn = (isset($options['show_refresh_button'])) ? (bool)$options['show_refresh_button'] : true;
929
+ $show_input = (isset($options['show_text_input'])) ? (bool)$options['show_text_input'] : true;
930
+ $refresh_alt = (isset($options['refresh_alt_text'])) ? $options['refresh_alt_text'] : 'Refresh Image';
931
+ $refresh_title = (isset($options['refresh_title_text'])) ? $options['refresh_title_text'] : 'Refresh Image';
932
+ $input_text = (isset($options['input_text'])) ? $options['input_text'] : 'Type the text:';
933
+ $input_id = (isset($options['input_id'])) ? $options['input_id'] : 'captcha_code';
934
+ $input_name = (isset($options['input_name'])) ? $options['input_name'] : $input_id;
935
+ $input_attrs = (isset($options['input_attributes'])) ? $options['input_attributes'] : array();
936
+ $image_attrs = (isset($options['image_attributes'])) ? $options['image_attributes'] : array();
937
+ $error_html = (isset($options['error_html'])) ? $options['error_html'] : null;
938
+ $namespace = (isset($options['namespace'])) ? $options['namespace'] : '';
939
+
940
+ $rand = md5(uniqid($_SERVER['REMOTE_PORT'], true));
941
+ $securimage_path = rtrim($securimage_path, '/\\');
942
+
943
+ $image_attr = '';
944
+ if (!is_array($image_attrs)) $image_attrs = array();
945
+ if (!isset($image_attrs['align'])) $image_attrs['align'] = 'left';
946
+ $image_attrs['id'] = $image_id;
947
+
948
+ $show_path = $securimage_path . '/securimage_show.php?';
949
+ if (!empty($namespace)) {
950
+ $show_path .= sprintf('namespace=%s&', $namespace);
951
+ }
952
+ $image_attrs['src'] = $show_path . $rand;
953
+
954
+ $image_attrs['alt'] = $image_alt;
955
+
956
+ foreach($image_attrs as $name => $val) {
957
+ $image_attr .= sprintf('%s="%s" ', $name, htmlspecialchars($val));
958
+ }
959
+
960
+ $html = sprintf('<img %s/>', $image_attr);
961
+
962
+ if ($show_audio_btn) {
963
+ $swf_path = $securimage_path . '/securimage_play.swf';
964
+ $play_path = $securimage_path . '/securimage_play.php';
965
+ $icon_path = $securimage_path . '/images/audio_icon.png';
966
+
967
+ $html .= sprintf('<object type="application/x-shockwave-flash" data="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s" height="32" width="32">',
968
+ htmlspecialchars($swf_path),
969
+ '#ffffff',
970
+ htmlspecialchars($icon_path),
971
+ htmlspecialchars($play_path)
972
+ );
973
+
974
+ $html .= sprintf('<param name="movie" value="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s" />',
975
+ htmlspecialchars($swf_path),
976
+ '#ffffff',
977
+ htmlspecialchars($icon_path),
978
+ $play_path
979
+ );
980
+
981
+ $html .= '</object><br />';
982
+ }
983
+
984
+ if ($show_refresh_btn) {
985
+ $icon_path = $securimage_path . '/images/refresh.png';
986
+ $img_tag = sprintf('<img height="32" width="32" src="%s" alt="%s" onclick="this.blur()" align="bottom" border="0" />',
987
+ htmlspecialchars($icon_path), htmlspecialchars($refresh_alt));
988
+
989
+ $html .= sprintf('<a tabindex="-1" style="border: 0" href="#" title="%s" onclick="document.getElementById(\'%s\').src = \'%s\' + Math.random(); this.blur(); return false">%s</a><br />',
990
+ htmlspecialchars($refresh_title),
991
+ $image_id,
992
+ $show_path,
993
+ $img_tag
994
+ );
995
+ }
996
+
997
+ $html .= '<div style="clear: both"></div>';
998
+
999
+ $html .= sprintf('<label for="%s">%s</label> ',
1000
+ htmlspecialchars($input_id),
1001
+ htmlspecialchars($input_text));
1002
+
1003
+ if (!empty($error_html)) {
1004
+ $html .= $error_html;
1005
+ }
1006
+
1007
+ $input_attr = '';
1008
+ if (!is_array($input_attrs)) $input_attrs = array();
1009
+ $input_attrs['type'] = 'text';
1010
+ $input_attrs['name'] = $input_name;
1011
+ $input_attrs['id'] = $input_id;
1012
+
1013
+ foreach($input_attrs as $name => $val) {
1014
+ $input_attr .= sprintf('%s="%s" ', $name, htmlspecialchars($val));
1015
+ }
1016
+
1017
+ $html .= sprintf('<input %s/>', $input_attr);
1018
+
1019
+ return $html;
1020
+ }
1021
+
1022
+ /**
1023
+ * Get the time in seconds that it took to solve the captcha.
1024
+ *
1025
+ * @return int The time in seconds from when the code was created, to when it was solved
1026
+ */
1027
+ public function getTimeToSolve()
1028
+ {
1029
+ return $this->_timeToSolve;
1030
+ }
1031
+
1032
+ /**
1033
+ * Set the namespace for the captcha being stored in the session or database.
1034
+ *
1035
+ * @param string $namespace Namespace value, String consisting of characters "a-zA-Z0-9_-"
1036
+ */
1037
+ public function setNamespace($namespace)
1038
+ {
1039
+ $namespace = preg_replace('/[^a-z0-9-_]/i', '', $namespace);
1040
+ $namespace = substr($namespace, 0, 64);
1041
+
1042
+ if (!empty($namespace)) {
1043
+ $this->namespace = $namespace;
1044
+ } else {
1045
+ $this->namespace = 'default';
1046
+ }
1047
+ }
1048
+
1049
  /**
1050
  * Output a wav file of the captcha code to the browser
1051
  *
1064
  try {
1065
  $audio = $this->getAudibleCode();
1066
  } catch (Exception $ex) {
1067
+ if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) {
1068
+ fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
1069
+ fclose($fp);
1070
  }
1071
 
1072
  $audio = $this->audioError();
1073
+ }
1074
 
1075
  if ($this->canSendHeaders() || $this->send_headers == false) {
1076
  if ($this->send_headers) {
1101
  if (!$this->no_exit) exit;
1102
  }
1103
 
1104
+ /**
1105
+ * Return the code from the session or sqlite database if used. If none exists yet, an empty string is returned
1106
+ *
1107
+ * @param $array bool True to receive an array containing the code and properties
1108
+ * @return array|string Array if $array = true, otherwise a string containing the code
1109
+ */
1110
+ public function getCode($array = false, $returnExisting = false)
1111
+ {
1112
+ $code = array();
1113
  $time = 0;
1114
  $disp = 'error';
1115
 
1116
  if ($returnExisting && strlen($this->code) > 0) {
1117
  if ($array) {
1118
+ return array(
1119
+ 'code' => $this->code,
1120
+ 'display' => $this->code_display,
1121
+ 'code_display' => $this->code_display,
1122
+ 'time' => 0);
1123
  } else {
1124
  return $this->code;
1125
  }
1126
+ }
1127
+
1128
+ if ($this->no_session != true) {
1129
+ if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
1130
+ trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
1131
+ if ($this->isCodeExpired(
1132
+ $_SESSION['securimage_code_ctime'][$this->namespace]) == false) {
1133
+ $code['code'] = $_SESSION['securimage_code_value'][$this->namespace];
1134
+ $code['time'] = $_SESSION['securimage_code_ctime'][$this->namespace];
1135
+ $code['display'] = $_SESSION['securimage_code_disp'] [$this->namespace];
1136
+ }
1137
  }
1138
  }
1139
 
1140
+ if (empty($code) && $this->use_database) {
1141
+ // no code in session - may mean user has cookies turned off
1142
+ $this->openDatabase();
1143
+ $code = $this->getCodeFromDatabase();
1144
+
1145
+ if (!empty($code)) {
1146
+ $code['display'] = $code['code_disp'];
1147
+ unset($code['code_disp']);
1148
+ }
1149
+ } else { /* no code stored in session or sqlite database, validation will fail */ }
1150
+
1151
+ if ($array == true) {
1152
+ return $code;
1153
+ } else {
1154
+ return $code['code'];
1155
+ }
1156
  }
1157
 
1158
  /**
1406
  {
1407
  $width2 = $this->image_width * $this->iscale;
1408
  $height2 = $this->image_height * $this->iscale;
1409
+ $ratio = ($this->font_ratio) ? $this->font_ratio : 0.4;
1410
+
1411
+ if ((float)$ratio < 0.1 || (float)$ratio >= 1) {
1412
+ $ratio = 0.4;
1413
+ }
1414
 
1415
  if (!is_readable($this->ttf_file)) {
1416
  imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
1417
  } else {
1418
  if ($this->perturbation > 0) {
1419
+ $font_size = $height2 * $ratio;
1420
  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1421
  $tx = $bb[4] - $bb[0];
1422
  $ty = $bb[5] - $bb[1];
1425
 
1426
  imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1427
  } else {
1428
+ $font_size = $this->image_height * $ratio;
1429
  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1430
  $tx = $bb[4] - $bb[0];
1431
  $ty = $bb[5] - $bb[1];
1600
  header("Pragma: no-cache");
1601
  }
1602
 
1603
+ switch ($this->image_type) {
1604
+ case self::SI_IMAGE_JPEG:
1605
+ if ($this->send_headers) header("Content-Type: image/jpeg");
1606
+ imagejpeg($this->im, null, 90);
1607
+ break;
1608
+ case self::SI_IMAGE_GIF:
1609
+ if ($this->send_headers) header("Content-Type: image/gif");
1610
+ imagegif($this->im);
1611
+ break;
1612
+ default:
1613
+ if ($this->send_headers) header("Content-Type: image/png");
1614
+ imagepng($this->im);
1615
+ break;
1616
  }
1617
+ } else {
1618
+ echo '<hr /><strong>'
1619
+ .'Failed to generate captcha image, content has already been '
1620
+ .'output.<br />This is most likely due to misconfiguration or '
1621
  .'a PHP error was sent to the browser.</strong>';
1622
  }
1623
 
1637
  $letters = array();
1638
  $code = $this->getCode(true, true);
1639
 
1640
+ if (empty($code) || $code['code'] == '') {
1641
  if (strlen($this->display_value) > 0) {
1642
  $code = array('code' => $this->display_value, 'display' => $this->display_value);
1643
  } else {
1646
  }
1647
  }
1648
 
1649
+ if (empty($code)) {
1650
+ $error = 'Failed to get audible code (are database settings correct?). Check the error log for details';
1651
+ trigger_error($error, E_USER_WARNING);
1652
+ throw new Exception($error);
1653
+ }
1654
+
1655
  if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
1656
  $math = true;
1657
 
1748
  protected function validate()
1749
  {
1750
  if (!is_string($this->code) || strlen($this->code) == 0) {
1751
+ $code = $this->getCode(true);
1752
  // returns stored code, or an empty string if no stored code was found
1753
  // checks the session and database if enabled
1754
  } else {
1755
  $code = $this->code;
1756
  }
1757
 
1758
+ if (is_array($code)) {
1759
+ if (!empty($code)) {
1760
+ $ctime = $code['time'];
1761
+ $code = $code['code'];
1762
+
1763
+ $this->_timeToSolve = time() - $ctime;
1764
+ } else {
1765
+ $code = '';
1766
+ }
1767
+ }
1768
+
1769
  if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
1770
  // case sensitive was set from securimage_show.php but not in class
1771
  // the code saved in the session has capitals so set case sensitive to true
1787
  if ($code == $code_entered) {
1788
  $this->correct_code = true;
1789
  if ($this->no_session != true) {
1790
+ $_SESSION['securimage_code_disp'] [$this->namespace] = '';
1791
  $_SESSION['securimage_code_value'][$this->namespace] = '';
1792
  $_SESSION['securimage_code_ctime'][$this->namespace] = '';
1793
  }
1849
  $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
1850
 
1851
  if (!$success) {
1852
+ $err = $stmt->errorInfo();
1853
+ $error = "Failed to insert code into database. {$err[1]}: {$err[2]}.";
1854
+
1855
+ if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
1856
+ $err14 = ($err[1] == 14);
1857
+ if ($err14) $error .= sprintf(" Ensure database directory and file are writeable by user '%s' (%d).",
1858
+ get_current_user(), getmyuid());
1859
+ }
1860
+
1861
+ trigger_error($error, E_USER_WARNING);
1862
  }
1863
  }
1864
 
1897
  }
1898
  }
1899
 
 
 
1900
  try {
1901
+ $dsn = $this->getDsn();
1902
+
1903
+ $options = array();
1904
  $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
1905
  } catch (PDOException $pdoex) {
1906
  trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
1907
  return false;
1908
+ } catch (Exception $ex) {
1909
+ trigger_error($ex->getMessage(), E_USER_WARNING);
1910
+ return false;
1911
  }
1912
 
1913
  try {
1914
+ if (!$this->skip_table_check && !$this->checkTablesExist()) {
1915
  // create tables...
1916
  $this->createDatabaseTables();
1917
  }
1939
 
1940
  case self::SI_DRIVER_MYSQL:
1941
  case self::SI_DRIVER_PGSQL:
1942
+ if (empty($this->database_host)) {
1943
+ throw new Exception('Securimage::database_host is not set');
1944
+ } else if (empty($this->database_name)) {
1945
+ throw new Exception('Securimage::database_name is not set');
1946
+ }
1947
+
1948
  $dsn .= sprintf('host=%s;dbname=%s',
1949
  $this->database_host,
1950
  $this->database_name);
2064
  * Get a code from the sqlite database for ip address/captchaId.
2065
  *
2066
  * @return string|array Empty string if no code was found or has expired,
2067
+ * otherwise returns array of code information.
 
2068
  */
2069
  protected function getCodeFromDatabase()
2070
  {
2091
  } else {
2092
  if ( ($row = $stmt->fetch()) !== false ) {
2093
  if (false == $this->isCodeExpired($row['created'])) {
2094
+ $code = array(
2095
+ 'code' => $row['code'],
2096
+ 'code_disp' => $row['code_display'],
2097
+ 'time' => $row['created'],
2098
+ );
 
 
2099
  }
2100
  }
2101
  }
2176
  $wavCaptcha = new WavFile();
2177
  $first = true; // reading first wav file
2178
 
2179
+ if ($this->audio_use_sox && !is_executable($this->sox_binary_path)) {
2180
+ throw new Exception("Path to SoX binary is incorrect or not executable");
2181
+ }
2182
+
2183
  foreach ($letters as $letter) {
2184
  $letter = strtoupper($letter);
2185
 
2186
  try {
2187
+ $letter_file = realpath($this->audio_path) . DIRECTORY_SEPARATOR . $letter . '.wav';
2188
+
2189
+ if ($this->audio_use_sox) {
2190
+ $sox_cmd = sprintf("%s %s -t wav - %s",
2191
+ $this->sox_binary_path,
2192
+ $letter_file,
2193
+ $this->getSoxEffectChain());
2194
+
2195
+ $data = `$sox_cmd`;
2196
+
2197
+ $l = new WavFile();
2198
+ $l->setIgnoreChunkSizes(true);
2199
+ $l->setWavData($data);
2200
+ } else {
2201
+ $l = new WavFile($letter_file);
2202
+ }
2203
 
2204
  if ($first) {
2205
  // set sample rate, bits/sample, and # of channels for file based on first letter
2216
  if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
2217
  $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
2218
  }
2219
+ } catch (Exception $ex) {
2220
  // failed to open file, or the wav file is broken or not supported
2221
  // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
2222
+ throw new Exception("Error generating audio captcha on letter '$letter': " . $ex->getMessage());
2223
  }
2224
  }
2225
 
2228
 
2229
  if ($this->audio_use_noise == true) {
2230
  // use background audio - find random file
2231
+ $wavNoise = false;
2232
+ $randOffset = 0;
2233
+
2234
+ /*
2235
+ // uncomment to try experimental SoX noise generation.
2236
+ // warning: sounds may be considered annoying
2237
+ if ($this->audio_use_sox) {
2238
+ $duration = $wavCaptcha->getDataSize() / ($wavCaptcha->getBitsPerSample() / 8) /
2239
+ $wavCaptcha->getNumChannels() / $wavCaptcha->getSampleRate();
2240
+ $duration = round($duration, 2);
2241
+ $wavNoise = new WavFile();
2242
+ $wavNoise->setIgnoreChunkSizes(true);
2243
+ $noiseData = $this->getSoxNoiseData($duration,
2244
+ $wavCaptcha->getNumChannels(),
2245
+ $wavCaptcha->getSampleRate(),
2246
+ $wavCaptcha->getBitsPerSample());
2247
+ $wavNoise->setWavData($noiseData, true);
2248
+
2249
+ } else
2250
+ */
2251
+ if ( ($noiseFile = $this->getRandomNoiseFile()) !== false) {
2252
  try {
2253
  $wavNoise = new WavFile($noiseFile, false);
2254
  } catch(Exception $ex) {
2257
 
2258
  // start at a random offset from the beginning of the wavfile
2259
  // in order to add more randomness
2260
+
2261
  $randOffset = 0;
2262
+
2263
  if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
2264
  $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
2265
  $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
2267
  $wavNoise->readWavData();
2268
  $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
2269
  }
2270
+ }
2271
 
2272
+ if ($wavNoise !== false) {
2273
  $mixOpts = array('wav' => $wavNoise,
2274
  'loop' => true,
2275
  'blockOffset' => $randOffset);
2311
  if (sizeof($list) > 0) {
2312
  $file = $list[array_rand($list, 1)];
2313
  $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
2314
+
2315
+ if (!is_readable($return)) $return = false;
2316
  }
2317
  }
2318
 
2319
  return $return;
2320
  }
2321
 
2322
+ protected function getSoxEffectChain($numEffects = 2)
2323
+ {
2324
+ $effectsList = array('bend', 'chorus', 'overdrive', 'pitch', 'reverb', 'tempo', 'tremolo');
2325
+ $effects = array_rand($effectsList, $numEffects);
2326
+ $outEffects = array();
2327
+
2328
+ if (!is_array($effects)) $effects = array($effects);
2329
+
2330
+ foreach($effects as $effect) {
2331
+ $effect = $effectsList[$effect];
2332
+
2333
+ switch($effect)
2334
+ {
2335
+ case 'bend':
2336
+ $delay = mt_rand(0, 15) / 100.0;
2337
+ $cents = mt_rand(-120, 120);
2338
+ $dur = mt_rand(75, 400) / 100.0;
2339
+ $outEffects[] = "$effect $delay,$cents,$dur";
2340
+ break;
2341
+
2342
+ case 'chorus':
2343
+ $gainIn = mt_rand(75, 90) / 100.0;
2344
+ $gainOut = mt_rand(70, 95) / 100.0;
2345
+ $chorStr = "$effect $gainIn $gainOut";
2346
+
2347
+ for ($i = 0; $i < mt_rand(2, 3); ++$i) {
2348
+ $delay = mt_rand(20, 100);
2349
+ $decay = mt_rand(10, 100) / 100.0;
2350
+ $speed = mt_rand(20, 50) / 100.0;
2351
+ $depth = mt_rand(150, 250) / 100.0;
2352
+
2353
+ $chorStr .= " $delay $decay $speed $depth -s";
2354
+ }
2355
+
2356
+ $outEffects[] = $chorStr;
2357
+ break;
2358
+
2359
+ case 'overdrive':
2360
+