27 * A port of the freeCap captcha engine to Enano. |
27 * A port of the freeCap captcha engine to Enano. |
28 */ |
28 */ |
29 |
29 |
30 class captcha_engine_freecap extends captcha_base |
30 class captcha_engine_freecap extends captcha_base |
31 { |
31 { |
32 |
32 |
33 var $site_tags = array(); |
33 var $site_tags = array(); |
34 var $tag_pos = 0; |
34 var $tag_pos = 0; |
35 var $rand_func = "mt_rand"; |
35 var $rand_func = "mt_rand"; |
36 var $seed_func = "mt_srand"; |
36 var $seed_func = "mt_srand"; |
37 var $hash_func = "sha1"; |
37 var $hash_func = "sha1"; |
38 var $output = "png"; |
38 var $output = "png"; |
39 var $use_dict = false; |
39 var $use_dict = false; |
40 var $dict_location = ""; |
40 var $dict_location = ""; |
41 var $max_word_length = 7; |
41 var $max_word_length = 7; |
42 var $col_type = 1; |
42 var $col_type = 1; |
43 var $max_attempts = 20; |
43 var $max_attempts = 20; |
44 var $font_locations = Array(); |
44 var $font_locations = Array(); |
45 var $bg_type = 3; |
45 var $bg_type = 3; |
46 var $blur_bg = false; |
46 var $blur_bg = false; |
47 var $bg_images = Array(); |
47 var $bg_images = Array(); |
48 var $merge_type = 0; |
48 var $merge_type = 0; |
49 var $morph_bg = true; |
49 var $morph_bg = true; |
50 var $im, $im2, $im3; |
50 var $im, $im2, $im3; |
51 var $font_size = 36; |
51 var $font_size = 36; |
52 var $debug = false; |
52 var $debug = false; |
53 |
53 |
54 function __construct($s, $r = false) |
54 function __construct($s, $r = false) |
55 { |
55 { |
56 parent::__construct($s, $r); |
56 parent::__construct($s, $r); |
57 |
57 |
58 // try to avoid the 'free p*rn' method of CAPTCHA circumvention |
58 // try to avoid the 'free p*rn' method of CAPTCHA circumvention |
59 // see en.wikipedia.org/wiki/CAPTCHA for more info |
59 // see en.wikipedia.org/wiki/CAPTCHA for more info |
60 // $this->site_tags[0] = "To avoid spam, please do NOT enter the text if"; |
60 // $this->site_tags[0] = "To avoid spam, please do NOT enter the text if"; |
61 // $this->site_tags[1] = "this site is not puremango.co.uk"; |
61 // $this->site_tags[1] = "this site is not puremango.co.uk"; |
62 // or more simply: |
62 // or more simply: |
63 // $site_tags[0] = "for use only on puremango.co.uk"; |
63 // $site_tags[0] = "for use only on puremango.co.uk"; |
64 // reword or add lines as you please |
64 // reword or add lines as you please |
65 // or if you don't want any text: |
65 // or if you don't want any text: |
66 $this->site_tags = array(); |
66 $this->site_tags = array(); |
67 |
67 |
68 // where to write the above: |
68 // where to write the above: |
69 // 0=top |
69 // 0=top |
70 // 1=bottom |
70 // 1=bottom |
71 // 2=both |
71 // 2=both |
72 $this->tag_pos = 1; |
72 $this->tag_pos = 1; |
73 |
73 |
74 // functions to call for random number generation |
74 // functions to call for random number generation |
75 // mt_rand produces 'better' random numbers |
75 // mt_rand produces 'better' random numbers |
76 // but if your server doesn't support it, it's fine to use rand instead |
76 // but if your server doesn't support it, it's fine to use rand instead |
77 $this->rand_func = "mt_rand"; |
77 $this->rand_func = "mt_rand"; |
78 $this->seed_func = "mt_srand"; |
78 $this->seed_func = "mt_srand"; |
79 |
79 |
80 // which type of hash to use? |
80 // which type of hash to use? |
81 // possible values: "sha1", "md5", "crc32" |
81 // possible values: "sha1", "md5", "crc32" |
82 // sha1 supported by PHP4.3.0+ |
82 // sha1 supported by PHP4.3.0+ |
83 // md5 supported by PHP3+ |
83 // md5 supported by PHP3+ |
84 // crc32 supported by PHP4.0.1+ |
84 // crc32 supported by PHP4.0.1+ |
85 $this->hash_func = $this->session_fetch('hash_func', 'sha1'); |
85 $this->hash_func = $this->session_fetch('hash_func', 'sha1'); |
86 // store in session so can validate in form processor |
86 // store in session so can validate in form processor |
87 |
87 |
88 // image type: |
88 // image type: |
89 // possible values: "jpg", "png", "gif" |
89 // possible values: "jpg", "png", "gif" |
90 // jpg doesn't support transparency (transparent bg option ends up white) |
90 // jpg doesn't support transparency (transparent bg option ends up white) |
91 // png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html) |
91 // png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html) |
92 // gif may not be supported by your GD Lib. |
92 // gif may not be supported by your GD Lib. |
93 $this->output = "png"; |
93 $this->output = "png"; |
94 |
94 |
95 // 0=generate pseudo-random string, true=use dictionary |
95 // 0=generate pseudo-random string, true=use dictionary |
96 // dictionary is easier to recognise |
96 // dictionary is easier to recognise |
97 // - both for humans and computers, so use random string if you're paranoid. |
97 // - both for humans and computers, so use random string if you're paranoid. |
98 $this->use_dict = false; |
98 $this->use_dict = false; |
99 // if your server is NOT set up to deny web access to files beginning ".ht" |
99 // if your server is NOT set up to deny web access to files beginning ".ht" |
100 // then you should ensure the dictionary file is kept outside the web directory |
100 // then you should ensure the dictionary file is kept outside the web directory |
101 // eg: if www.foo.com/index.html points to c:\website\www\index.html |
101 // eg: if www.foo.com/index.html points to c:\website\www\index.html |
102 // then the dictionary should be placed in c:\website\dict.txt |
102 // then the dictionary should be placed in c:\website\dict.txt |
103 // test your server's config by trying to access the dictionary through a web browser |
103 // test your server's config by trying to access the dictionary through a web browser |
104 // you should NOT be able to view the contents. |
104 // you should NOT be able to view the contents. |
105 // can leave this blank if not using dictionary |
105 // can leave this blank if not using dictionary |
106 $this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php"; |
106 $this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php"; |
107 |
107 |
108 // used to calculate image width, and for non-dictionary word generation |
108 // used to calculate image width, and for non-dictionary word generation |
109 $this->max_word_length = 7; |
109 $this->max_word_length = 7; |
110 |
110 |
111 // text colour |
111 // text colour |
112 // 0=one random colour for all letters |
112 // 0=one random colour for all letters |
113 // 1=different random colour for each letter |
113 // 1=different random colour for each letter |
114 $this->col_type = 1; |
114 $this->col_type = 1; |
115 |
115 |
116 // maximum times a user can refresh the image |
116 // maximum times a user can refresh the image |
117 // on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble. |
117 // on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble. |
118 // further notes re: BF attacks in "avoid brute force attacks" section, below |
118 // further notes re: BF attacks in "avoid brute force attacks" section, below |
119 // on the other hand, those attempting OCR will find the ability to request new images |
119 // on the other hand, those attempting OCR will find the ability to request new images |
120 // very useful; if they can't crack one, just grab an easier target... |
120 // very useful; if they can't crack one, just grab an easier target... |
121 // for the ultra-paranoid, setting it to <5 will still work for most users |
121 // for the ultra-paranoid, setting it to <5 will still work for most users |
122 $this->max_attempts = 20; |
122 $this->max_attempts = 20; |
123 |
123 |
124 // list of fonts to use |
124 // list of fonts to use |
125 // font size should be around 35 pixels wide for each character. |
125 // font size should be around 35 pixels wide for each character. |
126 // you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts |
126 // you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts |
127 // There are other programs to can create GD fonts, but my script allows a greater |
127 // There are other programs to can create GD fonts, but my script allows a greater |
128 // degree of control over exactly how wide each character is, and is therefore |
128 // degree of control over exactly how wide each character is, and is therefore |
129 // recommended for 'special' uses. For normal use of GD fonts, |
129 // recommended for 'special' uses. For normal use of GD fonts, |
130 // the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD |
130 // the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD |
131 |
131 |
132 // the fonts included with freeCap *only* include lowercase alphabetic characters |
132 // the fonts included with freeCap *only* include lowercase alphabetic characters |
133 // so are not suitable for most other uses |
133 // so are not suitable for most other uses |
134 // to increase security, you really should add other fonts |
134 // to increase security, you really should add other fonts |
135 $this->font_locations = Array( |
135 $this->font_locations = Array( |
136 //ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf", |
136 //ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf", |
137 //ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf", |
137 //ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf", |
138 //ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf", |
138 //ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf", |
139 //ENANO_ROOT . "/includes/captcha/fonts/.ttf", |
139 //ENANO_ROOT . "/includes/captcha/fonts/.ttf", |
140 //ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf" |
140 //ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf" |
141 ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf", |
141 ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf", |
142 ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf", |
142 ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf", |
143 ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf", |
143 ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf", |
144 ); |
144 ); |
145 |
145 |
146 // background: |
146 // background: |
147 // 0=transparent (if jpg, white) |
147 // 0=transparent (if jpg, white) |
148 // 1=white bg with grid |
148 // 1=white bg with grid |
149 // 2=white bg with squiggles |
149 // 2=white bg with squiggles |
150 // 3=morphed image blocks |
150 // 3=morphed image blocks |
151 // 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts) |
151 // 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts) |
152 // many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing |
152 // many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing |
153 // for jpgs, 'transparent' is white |
153 // for jpgs, 'transparent' is white |
154 $this->bg_type = 3; |
154 $this->bg_type = 3; |
155 // should we blur the background? (looks nicer, makes text easier to read, takes longer) |
155 // should we blur the background? (looks nicer, makes text easier to read, takes longer) |
156 $this->blur_bg = false; |
156 $this->blur_bg = false; |
157 |
157 |
158 // for bg_type 3, which images should we use? |
158 // for bg_type 3, which images should we use? |
159 // if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them) |
159 // if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them) |
160 $this->bg_images = Array( |
160 $this->bg_images = Array( |
161 ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg", |
161 ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg", |
162 ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg", |
162 ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg", |
163 ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg", |
163 ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg", |
164 ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg", |
164 ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg", |
165 ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg" |
165 ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg" |
166 ); |
166 ); |
167 |
167 |
168 // for non-transparent backgrounds only: |
168 // for non-transparent backgrounds only: |
169 // if 0, merges CAPTCHA with bg |
169 // if 0, merges CAPTCHA with bg |
170 // if 1, write CAPTCHA over bg |
170 // if 1, write CAPTCHA over bg |
171 $this->merge_type = 0; |
171 $this->merge_type = 0; |
172 // should we morph the bg? (recommend yes, but takes a little longer to compute) |
172 // should we morph the bg? (recommend yes, but takes a little longer to compute) |
173 $this->morph_bg = true; |
173 $this->morph_bg = true; |
174 |
174 |
175 // you shouldn't need to edit anything below this, but it's extensively commented if you do want to play |
175 // you shouldn't need to edit anything below this, but it's extensively commented if you do want to play |
176 // have fun, and email me with ideas, or improvements to the code (very interested in speed improvements) |
176 // have fun, and email me with ideas, or improvements to the code (very interested in speed improvements) |
177 // hope this script saves some spam :-) |
177 // hope this script saves some spam :-) |
178 } |
178 } |
179 |
179 |
180 ////////////////////////////////////////////////////// |
180 ////////////////////////////////////////////////////// |
181 ////// Functions: |
181 ////// Functions: |
182 ////////////////////////////////////////////////////// |
182 ////////////////////////////////////////////////////// |
183 function make_seed() { |
183 function make_seed() { |
184 // from http://php.net/srand |
184 // from http://php.net/srand |
185 list($usec, $sec) = explode(' ', microtime()); |
185 list($usec, $sec) = explode(' ', microtime()); |
186 return (float) $sec + ((float) $usec * 100000); |
186 return (float) $sec + ((float) $usec * 100000); |
187 } |
187 } |
188 |
188 |
189 function rand_color() { |
189 function rand_color() { |
190 $rf =& $this->rand_func; |
190 $rf =& $this->rand_func; |
191 if($this->bg_type==3) |
191 if($this->bg_type==3) |
192 { |
192 { |
193 // needs darker colour.. |
193 // needs darker colour.. |
194 return $rf(10,100); |
194 return $rf(10,100); |
195 } else { |
195 } else { |
196 return $rf(60,170); |
196 return $rf(60,170); |
197 } |
197 } |
198 } |
198 } |
199 |
199 |
200 function myImageBlur($im) |
200 function myImageBlur($im) |
201 { |
201 { |
202 // w00t. my very own blur function |
202 // w00t. my very own blur function |
203 // in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-) |
203 // in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-) |
204 |
204 |
205 $width = imagesx($im); |
205 $width = imagesx($im); |
206 $height = imagesy($im); |
206 $height = imagesy($im); |
207 |
207 |
208 $temp_im = ImageCreateTrueColor($width,$height); |
208 $temp_im = ImageCreateTrueColor($width,$height); |
209 $bg = ImageColorAllocate($temp_im,150,150,150); |
209 $bg = ImageColorAllocate($temp_im,150,150,150); |
210 |
210 |
211 // preserves transparency if in orig image |
211 // preserves transparency if in orig image |
212 ImageColorTransparent($temp_im,$bg); |
212 ImageColorTransparent($temp_im,$bg); |
213 |
213 |
214 // fill bg |
214 // fill bg |
215 ImageFill($temp_im,0,0,$bg); |
215 ImageFill($temp_im,0,0,$bg); |
216 |
216 |
217 // anything higher than 3 makes it totally unreadable |
217 // anything higher than 3 makes it totally unreadable |
218 // might be useful in a 'real' blur function, though (ie blurring pictures not text) |
218 // might be useful in a 'real' blur function, though (ie blurring pictures not text) |
219 $distance = 1; |
219 $distance = 1; |
220 // use $distance=30 to have multiple copies of the word. not sure if this is useful. |
220 // use $distance=30 to have multiple copies of the word. not sure if this is useful. |
221 |
221 |
222 // blur by merging with itself at different x/y offsets: |
222 // blur by merging with itself at different x/y offsets: |
223 ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70); |
223 ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70); |
224 ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70); |
224 ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70); |
225 ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70); |
225 ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70); |
226 ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70); |
226 ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70); |
227 // remove temp image |
227 // remove temp image |
228 ImageDestroy($temp_im); |
228 ImageDestroy($temp_im); |
229 |
229 |
230 return $im; |
230 return $im; |
231 } |
231 } |
232 |
232 |
233 function sendImage($pic) |
233 function sendImage($pic) |
234 { |
234 { |
235 // output image with appropriate headers |
235 // output image with appropriate headers |
236 global $output,$im,$im2,$im3; |
236 global $output,$im,$im2,$im3; |
237 // ENANO - obfuscation technique disabled |
237 // ENANO - obfuscation technique disabled |
238 // (this is for ethical reasons - ask dan at enanocms.org for information on why) |
238 // (this is for ethical reasons - ask dan at enanocms.org for information on why) |
239 // Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary |
239 // Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary |
240 // header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs=")); |
240 // header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs=")); |
241 |
241 |
242 if ( $this->debug ) |
242 if ( $this->debug ) |
243 { |
243 { |
244 $x = imagesx($pic) - 70; |
244 $x = imagesx($pic) - 70; |
245 $y = imagesy($pic) - 20; |
245 $y = imagesy($pic) - 20; |
246 |
246 |
247 $code = $this->get_code(); |
247 $code = $this->get_code(); |
248 $red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72); |
248 $red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72); |
249 ImageString($pic, 5, $x, $y, $code, $red); |
249 ImageString($pic, 5, $x, $y, $code, $red); |
250 ImageString($pic, 5, 5, $y, "[debug mode]", $red); |
250 ImageString($pic, 5, 5, $y, "[debug mode]", $red); |
251 } |
251 } |
252 |
252 |
253 switch($this->output) |
253 switch($this->output) |
254 { |
254 { |
255 // add other cases as desired |
255 // add other cases as desired |
256 case "jpg": |
256 case "jpg": |
257 header("Content-Type: image/jpeg"); |
257 header("Content-Type: image/jpeg"); |
258 ImageJPEG($pic); |
258 ImageJPEG($pic); |
259 break; |
259 break; |
260 case "gif": |
260 case "gif": |
261 header("Content-Type: image/gif"); |
261 header("Content-Type: image/gif"); |
262 ImageGIF($pic); |
262 ImageGIF($pic); |
263 break; |
263 break; |
264 case "png": |
264 case "png": |
265 default: |
265 default: |
266 header("Content-Type: image/png"); |
266 header("Content-Type: image/png"); |
267 ImagePNG($pic); |
267 ImagePNG($pic); |
268 break; |
268 break; |
269 } |
269 } |
270 |
270 |
271 // kill GD images (removes from memory) |
271 // kill GD images (removes from memory) |
272 ImageDestroy($this->im); |
272 ImageDestroy($this->im); |
273 ImageDestroy($this->im2); |
273 ImageDestroy($this->im2); |
274 ImageDestroy($pic); |
274 ImageDestroy($pic); |
275 if(!empty($this->im3)) |
275 if(!empty($this->im3)) |
276 { |
276 { |
277 ImageDestroy($this->im3); |
277 ImageDestroy($this->im3); |
278 } |
278 } |
279 exit(); |
279 exit(); |
280 } |
280 } |
281 |
281 |
282 function make_image() |
282 function make_image() |
283 { |
283 { |
284 ////////////////////////////////////////////////////// |
284 ////////////////////////////////////////////////////// |
285 ////// Create Images + initialise a few things |
285 ////// Create Images + initialise a few things |
286 ////////////////////////////////////////////////////// |
286 ////////////////////////////////////////////////////// |
287 |
287 |
288 // seed random number generator |
288 // seed random number generator |
289 // PHP 4.2.0+ doesn't need this, but lower versions will |
289 // PHP 4.2.0+ doesn't need this, but lower versions will |
290 $this->seed_func($this->make_seed()); |
290 $this->seed_func($this->make_seed()); |
291 |
291 |
292 // how faded should the bg be? (100=totally gone, 0=bright as the day) |
292 // how faded should the bg be? (100=totally gone, 0=bright as the day) |
293 // to test how much protection the bg noise gives, take a screenshot of the freeCap image |
293 // to test how much protection the bg noise gives, take a screenshot of the freeCap image |
294 // and take it into a photo editor. play with contrast and brightness. |
294 // and take it into a photo editor. play with contrast and brightness. |
295 // If you can remove most of the bg, then it's not a good enough percentage |
295 // If you can remove most of the bg, then it's not a good enough percentage |
296 switch($this->bg_type) |
296 switch($this->bg_type) |
297 { |
297 { |
298 case 0: |
298 case 0: |
299 break; |
299 break; |
300 case 1: |
300 case 1: |
301 case 2: |
301 case 2: |
302 $bg_fade_pct = 65; |
302 $bg_fade_pct = 65; |
303 break; |
303 break; |
304 case 3: |
304 case 3: |
305 $bg_fade_pct = 50; |
305 $bg_fade_pct = 50; |
306 break; |
306 break; |
307 } |
307 } |
308 // slightly randomise the bg fade |
308 // slightly randomise the bg fade |
309 $bg_fade_pct += $this->rand_func(-2,2); |
309 $bg_fade_pct += $this->rand_func(-2,2); |
310 |
310 |
311 // read each font and get font character widths |
311 // read each font and get font character widths |
312 // $font_widths = Array(); |
312 // $font_widths = Array(); |
313 // for($i=0 ; $i<sizeof($this->font_locations) ; $i++) |
313 // for($i=0 ; $i<sizeof($this->font_locations) ; $i++) |
314 // { |
314 // { |
315 // $handle = fopen($this->font_locations[$i],"r"); |
315 // $handle = fopen($this->font_locations[$i],"r"); |
316 // // read header of GD font, up to char width |
316 // // read header of GD font, up to char width |
317 // $c_wid = fread($handle,15); |
317 // $c_wid = fread($handle,15); |
318 // $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11}); |
318 // $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11}); |
319 // fclose($handle); |
319 // fclose($handle); |
320 // } |
320 // } |
321 |
321 |
322 // modify image width depending on maximum possible length of word |
322 // modify image width depending on maximum possible length of word |
323 // you shouldn't need to use words > 6 chars in length really. |
323 // you shouldn't need to use words > 6 chars in length really. |
324 $width = ($this->max_word_length*($this->font_size+10)+75); |
324 $width = ($this->max_word_length*($this->font_size+10)+75); |
325 $height = 90; |
325 $height = 90; |
326 |
326 |
327 $this->im = ImageCreateTrueColor($width, $height); |
327 $this->im = ImageCreateTrueColor($width, $height); |
328 $this->im2 = ImageCreateTrueColor($width, $height); |
328 $this->im2 = ImageCreateTrueColor($width, $height); |
329 |
329 |
330 //////////////////////////////////////////////////////// |
330 //////////////////////////////////////////////////////// |
331 // GENERATE IMAGE // |
331 // GENERATE IMAGE // |
332 //////////////////////////////////////////////////////// |
332 //////////////////////////////////////////////////////// |
333 |
333 |
334 $word = $this->get_code(); |
334 $word = $this->get_code(); |
335 |
335 |
336 // save hash of word for comparison |
336 // save hash of word for comparison |
337 // using hash so that if there's an insecurity elsewhere (eg on the form processor), |
337 // using hash so that if there's an insecurity elsewhere (eg on the form processor), |
338 // an attacker could only get the hash |
338 // an attacker could only get the hash |
339 // also, shared servers usually give all users access to the session files |
339 // also, shared servers usually give all users access to the session files |
340 // echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work |
340 // echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work |
341 // so even if your site is 100% secure, someone else's site on your server might not be |
341 // so even if your site is 100% secure, someone else's site on your server might not be |
342 // hence, even if attackers can read the session file, they can't get the freeCap word |
342 // hence, even if attackers can read the session file, they can't get the freeCap word |
343 // (though most hashes are easy to brute force for simple strings) |
343 // (though most hashes are easy to brute force for simple strings) |
344 |
344 |
345 ////////////////////////////////////////////////////// |
345 ////////////////////////////////////////////////////// |
346 ////// Fill BGs and Allocate Colours: |
346 ////// Fill BGs and Allocate Colours: |
347 ////////////////////////////////////////////////////// |
347 ////////////////////////////////////////////////////// |
348 |
348 |
349 // set tag colour |
349 // set tag colour |
350 // have to do this before any distortion |
350 // have to do this before any distortion |
351 // (otherwise colour allocation fails when bg type is 1) |
351 // (otherwise colour allocation fails when bg type is 1) |
352 $tag_col = ImageColorAllocate($this->im,10,10,10); |
352 $tag_col = ImageColorAllocate($this->im,10,10,10); |
353 $site_tag_col2 = ImageColorAllocate($this->im2,0,0,0); |
353 $site_tag_col2 = ImageColorAllocate($this->im2,0,0,0); |
354 |
354 |
355 // set debug colours (text colours are set later) |
355 // set debug colours (text colours are set later) |
356 $debug = ImageColorAllocate($this->im, 255, 0, 0); |
356 $debug = ImageColorAllocate($this->im, 255, 0, 0); |
357 $debug2 = ImageColorAllocate($this->im2, 255, 0, 0); |
357 $debug2 = ImageColorAllocate($this->im2, 255, 0, 0); |
358 |
358 |
359 // set background colour (can change to any colour not in possible $text_col range) |
359 // set background colour (can change to any colour not in possible $text_col range) |
360 // it doesn't matter as it'll be transparent or coloured over. |
360 // it doesn't matter as it'll be transparent or coloured over. |
361 // if you're using bg_type 3, you might want to try to ensure that the color chosen |
361 // if you're using bg_type 3, you might want to try to ensure that the color chosen |
362 // below doesn't appear too much in any of your background images. |
362 // below doesn't appear too much in any of your background images. |
363 $bg = ImageColorAllocate($this->im, 254, 254, 254); |
363 $bg = ImageColorAllocate($this->im, 254, 254, 254); |
364 $bg2 = ImageColorAllocate($this->im2, 254, 254, 254); |
364 $bg2 = ImageColorAllocate($this->im2, 254, 254, 254); |
365 |
365 |
366 // set transparencies |
366 // set transparencies |
367 ImageColorTransparent($this->im,$bg); |
367 ImageColorTransparent($this->im,$bg); |
368 // im2 transparent to allow characters to overlap slightly while morphing |
368 // im2 transparent to allow characters to overlap slightly while morphing |
369 ImageColorTransparent($this->im2,$bg2); |
369 ImageColorTransparent($this->im2,$bg2); |
370 |
370 |
371 // fill backgrounds |
371 // fill backgrounds |
372 ImageFill($this->im,0,0,$bg); |
372 ImageFill($this->im,0,0,$bg); |
373 ImageFill($this->im2,0,0,$bg2); |
373 ImageFill($this->im2,0,0,$bg2); |
374 |
374 |
375 if($this->bg_type!=0) |
375 if($this->bg_type!=0) |
376 { |
376 { |
377 // generate noisy background, to be merged with CAPTCHA later |
377 // generate noisy background, to be merged with CAPTCHA later |
378 // any suggestions on how best to do this much appreciated |
378 // any suggestions on how best to do this much appreciated |
379 // sample code would be even better! |
379 // sample code would be even better! |
380 // I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint) |
380 // I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint) |
381 // so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog |
381 // so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog |
382 // ideally, the character obfuscation would be strong enough not to need additional background noise |
382 // ideally, the character obfuscation would be strong enough not to need additional background noise |
383 // in any case, I hope at least one of the options given here provide some extra security! |
383 // in any case, I hope at least one of the options given here provide some extra security! |
384 |
384 |
385 $this->im3 = ImageCreateTrueColor($width,$height); |
385 $this->im3 = ImageCreateTrueColor($width,$height); |
386 $temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5); |
386 $temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5); |
387 $bg3 = ImageColorAllocate($this->im3,255,255,255); |
387 $bg3 = ImageColorAllocate($this->im3,255,255,255); |
388 ImageFill($this->im3,0,0,$bg3); |
388 ImageFill($this->im3,0,0,$bg3); |
389 $temp_bg_col = ImageColorAllocate($temp_bg,255,255,255); |
389 $temp_bg_col = ImageColorAllocate($temp_bg,255,255,255); |
390 ImageFill($temp_bg,0,0,$temp_bg_col); |
390 ImageFill($temp_bg,0,0,$temp_bg_col); |
391 |
391 |
392 // we draw all noise onto temp_bg |
392 // we draw all noise onto temp_bg |
393 // then if we're morphing, merge from temp_bg to im3 |
393 // then if we're morphing, merge from temp_bg to im3 |
394 // or if not, just copy a $widthx$height portion of $temp_bg to $this->im3 |
394 // or if not, just copy a $widthx$height portion of $temp_bg to $this->im3 |
395 // temp_bg is much larger so that when morphing, the edges retain the noise. |
395 // temp_bg is much larger so that when morphing, the edges retain the noise. |
396 |
396 |
397 if($this->bg_type==1) |
397 if($this->bg_type==1) |
398 { |
398 { |
399 // grid bg: |
399 // grid bg: |
400 |
400 |
401 // draw grid on x |
401 // draw grid on x |
402 for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25)) |
402 for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25)) |
403 { |
403 { |
404 ImageSetThickness($temp_bg,$this->rand_func(2,6)); |
404 ImageSetThickness($temp_bg,$this->rand_func(2,6)); |
405 $text_r = $this->rand_func(100,150); |
405 $text_r = $this->rand_func(100,150); |
406 $text_g = $this->rand_func(100,150); |
406 $text_g = $this->rand_func(100,150); |
407 $text_b = $this->rand_func(100,150); |
407 $text_b = $this->rand_func(100,150); |
408 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
408 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
409 |
409 |
410 ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3); |
410 ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3); |
411 } |
411 } |
412 // draw grid on y |
412 // draw grid on y |
413 for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25)) |
413 for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25)) |
414 { |
414 { |
415 ImageSetThickness($temp_bg,$this->rand_func(2,6)); |
415 ImageSetThickness($temp_bg,$this->rand_func(2,6)); |
416 $text_r = $this->rand_func(100,150); |
416 $text_r = $this->rand_func(100,150); |
417 $text_g = $this->rand_func(100,150); |
417 $text_g = $this->rand_func(100,150); |
418 $text_b = $this->rand_func(100,150); |
418 $text_b = $this->rand_func(100,150); |
419 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
419 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
420 |
420 |
421 ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3); |
421 ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3); |
422 } |
422 } |
423 } else if($this->bg_type==2) { |
423 } else if($this->bg_type==2) { |
424 // draw squiggles! |
424 // draw squiggles! |
425 |
425 |
426 $bg3 = ImageColorAllocate($this->im3,255,255,255); |
426 $bg3 = ImageColorAllocate($this->im3,255,255,255); |
427 ImageFill($this->im3,0,0,$bg3); |
427 ImageFill($this->im3,0,0,$bg3); |
428 ImageSetThickness($temp_bg,4); |
428 ImageSetThickness($temp_bg,4); |
429 |
429 |
430 for($i=0 ; $i<strlen($word)+1 ; $i++) |
430 for($i=0 ; $i<strlen($word)+1 ; $i++) |
431 { |
431 { |
432 $text_r = $this->rand_func(100,150); |
432 $text_r = $this->rand_func(100,150); |
433 $text_g = $this->rand_func(100,150); |
433 $text_g = $this->rand_func(100,150); |
434 $text_b = $this->rand_func(100,150); |
434 $text_b = $this->rand_func(100,150); |
435 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
435 $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b); |
436 |
436 |
437 $points = Array(); |
437 $points = Array(); |
438 // draw random squiggle for each character |
438 // draw random squiggle for each character |
439 // the longer the loop, the more complex the squiggle |
439 // the longer the loop, the more complex the squiggle |
440 // keep random so OCR can't say "if found shape has 10 points, ignore it" |
440 // keep random so OCR can't say "if found shape has 10 points, ignore it" |
441 // each squiggle will, however, be a closed shape, so OCR could try to find |
441 // each squiggle will, however, be a closed shape, so OCR could try to find |
442 // line terminations and start from there. (I don't think they're that advanced yet..) |
442 // line terminations and start from there. (I don't think they're that advanced yet..) |
443 for($j=1 ; $j<$this->rand_func(5,10) ; $j++) |
443 for($j=1 ; $j<$this->rand_func(5,10) ; $j++) |
444 { |
444 { |
445 $points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1))); |
445 $points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1))); |
446 $points[] = $this->rand_func(30,$height+30); |
446 $points[] = $this->rand_func(30,$height+30); |
447 } |
447 } |
448 |
448 |
449 ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3); |
449 ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3); |
450 } |
450 } |
451 |
451 |
452 } else if($this->bg_type==3) { |
452 } else if($this->bg_type==3) { |
453 // take random chunks of $this->bg_images and paste them onto the background |
453 // take random chunks of $this->bg_images and paste them onto the background |
454 |
454 |
455 for($i=0 ; $i<sizeof($this->bg_images) ; $i++) |
455 for($i=0 ; $i<sizeof($this->bg_images) ; $i++) |
456 { |
456 { |
457 // read each image and its size |
457 // read each image and its size |
458 $temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]); |
458 $temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]); |
459 $temp_width[$i] = imagesx($temp_im[$i]); |
459 $temp_width[$i] = imagesx($temp_im[$i]); |
460 $temp_height[$i] = imagesy($temp_im[$i]); |
460 $temp_height[$i] = imagesy($temp_im[$i]); |
461 } |
461 } |
462 |
462 |
463 $blocksize = $this->rand_func(20,60); |
463 $blocksize = $this->rand_func(20,60); |
464 for($i=0 ; $i<$width*2 ; $i+=$blocksize) |
464 for($i=0 ; $i<$width*2 ; $i+=$blocksize) |
465 { |
465 { |
466 // could randomise blocksize here... hardly matters |
466 // could randomise blocksize here... hardly matters |
467 for($j=0 ; $j<$height*2 ; $j+=$blocksize) |
467 for($j=0 ; $j<$height*2 ; $j+=$blocksize) |
468 { |
468 { |
469 $this->image_index = $this->rand_func(0,sizeof($temp_im)-1); |
469 $this->image_index = $this->rand_func(0,sizeof($temp_im)-1); |
470 $cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize); |
470 $cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize); |
471 $cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize); |
471 $cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize); |
472 ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize); |
472 ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize); |
473 } |
473 } |
474 } |
474 } |
475 for($i=0 ; $i<sizeof($temp_im) ; $i++) |
475 for($i=0 ; $i<sizeof($temp_im) ; $i++) |
476 { |
476 { |
477 // remove bgs from memory |
477 // remove bgs from memory |
478 ImageDestroy($temp_im[$i]); |
478 ImageDestroy($temp_im[$i]); |
479 } |
479 } |
480 |
480 |
481 // for debug: |
481 // for debug: |
482 //sendImage($temp_bg); |
482 //sendImage($temp_bg); |
483 } |
483 } |
484 |
484 |
485 // for debug: |
485 // for debug: |
486 //sendImage($this->im3); |
486 //sendImage($this->im3); |
487 |
487 |
488 if($this->morph_bg) |
488 if($this->morph_bg) |
489 { |
489 { |
490 // morph background |
490 // morph background |
491 // we do this separately to the main text morph because: |
491 // we do this separately to the main text morph because: |
492 // a) the main text morph is done char-by-char, this is done across whole image |
492 // a) the main text morph is done char-by-char, this is done across whole image |
493 // b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA |
493 // b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA |
494 // hence bg is morphed differently to text |
494 // hence bg is morphed differently to text |
495 // why do we morph it at all? it might make it harder for an attacker to remove the background |
495 // why do we morph it at all? it might make it harder for an attacker to remove the background |
496 // morph_chunk 1 looks better but takes longer |
496 // morph_chunk 1 looks better but takes longer |
497 |
497 |
498 // this is a different and less perfect morph than the one we do on the CAPTCHA |
498 // this is a different and less perfect morph than the one we do on the CAPTCHA |
499 // occasonally you get some dark background showing through around the edges |
499 // occasonally you get some dark background showing through around the edges |
500 // it doesn't need to be perfect as it's only the bg. |
500 // it doesn't need to be perfect as it's only the bg. |
501 $morph_chunk = $this->rand_func(1,5); |
501 $morph_chunk = $this->rand_func(1,5); |
502 $morph_y = 0; |
502 $morph_y = 0; |
503 for($x=0 ; $x<$width ; $x+=$morph_chunk) |
503 for($x=0 ; $x<$width ; $x+=$morph_chunk) |
504 { |
504 { |
505 $morph_chunk = $this->rand_func(1,5); |
505 $morph_chunk = $this->rand_func(1,5); |
506 $morph_y += $this->rand_func(-1,1); |
506 $morph_y += $this->rand_func(-1,1); |
507 ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2); |
507 ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2); |
508 } |
508 } |
509 |
509 |
510 ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height); |
510 ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height); |
511 |
511 |
512 $morph_x = 0; |
512 $morph_x = 0; |
513 for($y=0 ; $y<=$height; $y+=$morph_chunk) |
513 for($y=0 ; $y<=$height; $y+=$morph_chunk) |
514 { |
514 { |
515 $morph_chunk = $this->rand_func(1,5); |
515 $morph_chunk = $this->rand_func(1,5); |
516 $morph_x += $this->rand_func(-1,1); |
516 $morph_x += $this->rand_func(-1,1); |
517 ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk); |
517 ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk); |
518 |
518 |
519 } |
519 } |
520 } else { |
520 } else { |
521 // just copy temp_bg onto im3 |
521 // just copy temp_bg onto im3 |
522 ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height); |
522 ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height); |
523 } |
523 } |
524 |
524 |
525 ImageDestroy($temp_bg); |
525 ImageDestroy($temp_bg); |
526 |
526 |
527 if($this->blur_bg) |
527 if($this->blur_bg) |
528 { |
528 { |
529 $this->myImageBlur($this->im3); |
529 $this->myImageBlur($this->im3); |
530 } |
530 } |
531 } |
531 } |
532 // for debug: |
532 // for debug: |
533 //sendImage($this->im3); |
533 //sendImage($this->im3); |
534 |
534 |
535 ////////////////////////////////////////////////////// |
535 ////////////////////////////////////////////////////// |
536 ////// Write Word |
536 ////// Write Word |
537 ////////////////////////////////////////////////////// |
537 ////////////////////////////////////////////////////// |
538 |
538 |
539 // write word in random starting X position |
539 // write word in random starting X position |
540 $word_start_x = $this->rand_func(5,32); |
540 $word_start_x = $this->rand_func(5,32); |
541 // y positions jiggled about later |
541 // y positions jiggled about later |
542 $word_start_y = 50; |
542 $word_start_y = 50; |
543 |
543 |
544 // use last pixelwidth |
544 // use last pixelwidth |
545 $font_pixelwidth = $this->font_size + 10; |
545 $font_pixelwidth = $this->font_size + 10; |
546 |
546 |
547 if($this->col_type==0) |
547 if($this->col_type==0) |
548 { |
548 { |
549 $text_r = $this->rand_color(); |
549 $text_r = $this->rand_color(); |
550 $text_g = $this->rand_color(); |
550 $text_g = $this->rand_color(); |
551 $text_b = $this->rand_color(); |
551 $text_b = $this->rand_color(); |
552 $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b); |
552 $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b); |
553 } |
553 } |
554 |
554 |
555 // write each char in different font |
555 // write each char in different font |
556 for($i=0 ; $i<strlen($word) ; $i++) |
556 for($i=0 ; $i<strlen($word) ; $i++) |
557 { |
557 { |
558 if($this->col_type==1) |
558 if($this->col_type==1) |
559 { |
559 { |
560 $text_r = $this->rand_color(); |
560 $text_r = $this->rand_color(); |
561 $text_g = $this->rand_color(); |
561 $text_g = $this->rand_color(); |
562 $text_b = $this->rand_color(); |
562 $text_b = $this->rand_color(); |
563 $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b); |
563 $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b); |
564 } |
564 } |
565 |
565 |
566 $j = $this->rand_func(0,sizeof($this->font_locations)-1); |
566 $j = $this->rand_func(0,sizeof($this->font_locations)-1); |
567 // $font = ImageLoadFont($this->font_locations[$j]); |
567 // $font = ImageLoadFont($this->font_locations[$j]); |
568 // ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2); |
568 // ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2); |
569 ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i}); |
569 ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i}); |
570 } |
570 } |
571 |
571 |
572 // for debug: |
572 // for debug: |
573 // $this->sendImage($this->im2); |
573 // $this->sendImage($this->im2); |
574 |
574 |
575 ////////////////////////////////////////////////////// |
575 ////////////////////////////////////////////////////// |
576 ////// Morph Image: |
576 ////// Morph Image: |
577 ////////////////////////////////////////////////////// |
577 ////////////////////////////////////////////////////// |
578 |
578 |
579 // calculate how big the text is in pixels |
579 // calculate how big the text is in pixels |
580 // (so we only morph what we need to) |
580 // (so we only morph what we need to) |
581 $word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth); |
581 $word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth); |
582 |
582 |
583 // firstly move each character up or down a bit: |
583 // firstly move each character up or down a bit: |
584 $y_pos = 0; |
584 $y_pos = 0; |
585 for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth) |
585 for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth) |
586 { |
586 { |
587 // move on Y axis |
587 // move on Y axis |
588 // deviates at least 4 pixels between each letter |
588 // deviates at least 4 pixels between each letter |
589 $prev_y = $y_pos; |
589 $prev_y = $y_pos; |
590 do{ |
590 do{ |
591 $y_pos = $this->rand_func(-5,5); |
591 $y_pos = $this->rand_func(-5,5); |
592 } while($y_pos<$prev_y+2 && $y_pos>$prev_y-2); |
592 } while($y_pos<$prev_y+2 && $y_pos>$prev_y-2); |
593 ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height); |
593 ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height); |
594 |
594 |
595 // for debug: |
595 // for debug: |
596 // ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug); |
596 // ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug); |
597 } |
597 } |
598 |
598 |
599 // for debug: |
599 // for debug: |
600 // $this->sendImage($this->im); |
600 // $this->sendImage($this->im); |
601 |
601 |
602 ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2); |
602 ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2); |
603 |
603 |
604 // randomly morph each character individually on x-axis |
604 // randomly morph each character individually on x-axis |
605 // this is where the main distortion happens |
605 // this is where the main distortion happens |
606 // massively improved since v1.2 |
606 // massively improved since v1.2 |
607 $y_chunk = 1; |
607 $y_chunk = 1; |
608 $morph_factor = 1; |
608 $morph_factor = 1; |
609 $morph_x = 0; |
609 $morph_x = 0; |
610 for($j=0 ; $j<strlen($word) ; $j++) |
610 for($j=0 ; $j<strlen($word) ; $j++) |
611 { |
611 { |
612 $y_pos = 0; |
612 $y_pos = 0; |
613 for($i=0 ; $i<=$height; $i+=$y_chunk) |
613 for($i=0 ; $i<=$height; $i+=$y_chunk) |
614 { |
614 { |
615 $orig_x = $word_start_x+($j*$font_pixelwidth); |
615 $orig_x = $word_start_x+($j*$font_pixelwidth); |
616 // morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to |
616 // morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to |
617 // get it? instead of a zig zag, we get more of a sine wave. |
617 // get it? instead of a zig zag, we get more of a sine wave. |
618 // I wish we could deviate more but it looks crap if we do. |
618 // I wish we could deviate more but it looks crap if we do. |
619 $morph_x += $this->rand_func(-$morph_factor,$morph_factor); |
619 $morph_x += $this->rand_func(-$morph_factor,$morph_factor); |
620 // had to change this to ImageCopyMerge when starting using ImageCreateTrueColor |
620 // had to change this to ImageCopyMerge when starting using ImageCreateTrueColor |
621 // according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()" |
621 // according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()" |
622 // but this is NOT true when dealing with transparencies... |
622 // but this is NOT true when dealing with transparencies... |
623 ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100); |
623 ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100); |
624 |
624 |
625 // for debug: |
625 // for debug: |
626 //ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2); |
626 //ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2); |
627 //ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2); |
627 //ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2); |
628 } |
628 } |
629 } |
629 } |
630 |
630 |
631 // for debug: |
631 // for debug: |
632 //sendImage($this->im2); |
632 //sendImage($this->im2); |
633 |
633 |
634 ImageFilledRectangle($this->im,0,0,$width,$height,$bg); |
634 ImageFilledRectangle($this->im,0,0,$width,$height,$bg); |
635 // now do the same on the y-axis |
635 // now do the same on the y-axis |
636 // (much easier because we can just do it across the whole image, don't have to do it char-by-char) |
636 // (much easier because we can just do it across the whole image, don't have to do it char-by-char) |
637 $y_pos = 0; |
637 $y_pos = 0; |
638 $x_chunk = 1; |
638 $x_chunk = 1; |
639 for($i=0 ; $i<=$width ; $i+=$x_chunk) |
639 for($i=0 ; $i<=$width ; $i+=$x_chunk) |
640 { |
640 { |
641 // can result in image going too far off on Y-axis; |
641 // can result in image going too far off on Y-axis; |
642 // not much I can do about that, apart from make image bigger |
642 // not much I can do about that, apart from make image bigger |
643 // again, I wish I could do 1.5 pixels |
643 // again, I wish I could do 1.5 pixels |
644 $y_pos += $this->rand_func(-1,1); |
644 $y_pos += $this->rand_func(-1,1); |
645 ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height); |
645 ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height); |
646 |
646 |
647 // for debug: |
647 // for debug: |
648 //ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug); |
648 //ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug); |
649 //ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug); |
649 //ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug); |
650 } |
650 } |
651 |
651 |
652 // for debug: |
652 // for debug: |
653 //sendImage($this->im); |
653 //sendImage($this->im); |
654 |
654 |
655 // blur edges: |
655 // blur edges: |
656 // doesn't really add any security, but looks a lot nicer, and renders text a little easier to read |
656 // doesn't really add any security, but looks a lot nicer, and renders text a little easier to read |
657 // for humans (hopefully not for OCRs, but if you know better, feel free to disable this function) |
657 // for humans (hopefully not for OCRs, but if you know better, feel free to disable this function) |
658 // (and if you do, let me know why) |
658 // (and if you do, let me know why) |
659 $this->myImageBlur($this->im); |
659 $this->myImageBlur($this->im); |
660 |
660 |
661 // for debug: |
661 // for debug: |
662 //sendImage($this->im); |
662 //sendImage($this->im); |
663 |
663 |
664 if($this->output!="jpg" && $this->bg_type==0) |
664 if($this->output!="jpg" && $this->bg_type==0) |
665 { |
665 { |
666 // make background transparent |
666 // make background transparent |
667 ImageColorTransparent($this->im,$bg); |
667 ImageColorTransparent($this->im,$bg); |
668 } |
668 } |
669 |
669 |
670 |
670 |
671 |
671 |
672 |
672 |
673 |
673 |
674 ////////////////////////////////////////////////////// |
674 ////////////////////////////////////////////////////// |
675 ////// Try to avoid 'free p*rn' style CAPTCHA re-use |
675 ////// Try to avoid 'free p*rn' style CAPTCHA re-use |
676 ////////////////////////////////////////////////////// |
676 ////////////////////////////////////////////////////// |
677 // ('*'ed to stop my site coming up for certain keyword searches on google) |
677 // ('*'ed to stop my site coming up for certain keyword searches on google) |
678 |
678 |
679 // can obscure CAPTCHA word in some cases.. |
679 // can obscure CAPTCHA word in some cases.. |
680 |
680 |
681 // write site tags 'shining through' the morphed image |
681 // write site tags 'shining through' the morphed image |
682 ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2); |
682 ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2); |
683 if(is_array($this->site_tags)) |
683 if(is_array($this->site_tags)) |
684 { |
684 { |
685 for($i=0 ; $i<sizeof($this->site_tags) ; $i++) |
685 for($i=0 ; $i<sizeof($this->site_tags) ; $i++) |
686 { |
686 { |
687 // ensure tags are centered |
687 // ensure tags are centered |
688 $tag_width = strlen($this->site_tags[$i])*6; |
688 $tag_width = strlen($this->site_tags[$i])*6; |
689 // write tag is chosen position |
689 // write tag is chosen position |
690 if($this->tag_pos==0 || $this->tag_pos==2) |
690 if($this->tag_pos==0 || $this->tag_pos==2) |
691 { |
691 { |
692 // write at top |
692 // write at top |
693 ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2); |
693 ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2); |
694 } |
694 } |
695 if($this->tag_pos==1 || $this->tag_pos==2) |
695 if($this->tag_pos==1 || $this->tag_pos==2) |
696 { |
696 { |
697 // write at bottom |
697 // write at bottom |
698 ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2); |
698 ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2); |
699 } |
699 } |
700 } |
700 } |
701 } |
701 } |
702 ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80); |
702 ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80); |
703 ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height); |
703 ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height); |
704 // for debug: |
704 // for debug: |
705 //sendImage($this->im); |
705 //sendImage($this->im); |
706 |
706 |
707 |
707 |
708 |
708 |
709 |
709 |
710 ////////////////////////////////////////////////////// |
710 ////////////////////////////////////////////////////// |
711 ////// Merge with obfuscated background |
711 ////// Merge with obfuscated background |
712 ////////////////////////////////////////////////////// |
712 ////////////////////////////////////////////////////// |
713 |
713 |
714 if($this->bg_type!=0) |
714 if($this->bg_type!=0) |
715 { |
715 { |
716 // merge bg image with CAPTCHA image to create smooth background |
716 // merge bg image with CAPTCHA image to create smooth background |
717 |
717 |
718 // fade bg: |
718 // fade bg: |
719 if($this->bg_type!=3) |
719 if($this->bg_type!=3) |
720 { |
720 { |
721 $temp_im = ImageCreateTrueColor($width,$height); |
721 $temp_im = ImageCreateTrueColor($width,$height); |
722 $white = ImageColorAllocate($temp_im,255,255,255); |
722 $white = ImageColorAllocate($temp_im,255,255,255); |
723 ImageFill($temp_im,0,0,$white); |
723 ImageFill($temp_im,0,0,$white); |
724 ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct); |
724 ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct); |
725 // for debug: |
725 // for debug: |
726 //sendImage($this->im3); |
726 //sendImage($this->im3); |
727 ImageDestroy($temp_im); |
727 ImageDestroy($temp_im); |
728 $c_fade_pct = 50; |
728 $c_fade_pct = 50; |
729 } else { |
729 } else { |
730 $c_fade_pct = $bg_fade_pct; |
730 $c_fade_pct = $bg_fade_pct; |
731 } |
731 } |
732 |
732 |
733 // captcha over bg: |
733 // captcha over bg: |
734 // might want to not blur if using this method |
734 // might want to not blur if using this method |
735 // otherwise leaves white-ish border around each letter |
735 // otherwise leaves white-ish border around each letter |
736 if($this->merge_type==1) |
736 if($this->merge_type==1) |
737 { |
737 { |
738 ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100); |
738 ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100); |
739 ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height); |
739 ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height); |
740 } else { |
740 } else { |
741 // bg over captcha: |
741 // bg over captcha: |
742 ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct); |
742 ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct); |
743 } |
743 } |
744 } |
744 } |
745 // for debug: |
745 // for debug: |
746 //sendImage($this->im); |
746 //sendImage($this->im); |
747 |
747 |
748 |
748 |
749 ////////////////////////////////////////////////////// |
749 ////////////////////////////////////////////////////// |
750 ////// Write tags, remove variables and output! |
750 ////// Write tags, remove variables and output! |
751 ////////////////////////////////////////////////////// |
751 ////////////////////////////////////////////////////// |
752 |
752 |
753 // tag it |
753 // tag it |
754 // feel free to remove/change |
754 // feel free to remove/change |
755 // but if it's not essential I'd appreciate you leaving it |
755 // but if it's not essential I'd appreciate you leaving it |
756 // after all, I've put a lot of work into this and am giving it away for free |
756 // after all, I've put a lot of work into this and am giving it away for free |
757 // the least you could do is give me credit (or buy me stuff from amazon!) |
757 // the least you could do is give me credit (or buy me stuff from amazon!) |
758 // but I understand that in professional environments, your boss might not like this tag |
758 // but I understand that in professional environments, your boss might not like this tag |
759 // so that's cool. |
759 // so that's cool. |
760 $tag_str = ""; |
760 $tag_str = ""; |
761 // for debug: |
761 // for debug: |
762 //$tag_str = "[".$word."]"; |
762 //$tag_str = "[".$word."]"; |
763 |
763 |
764 // ensure tag is right-aligned |
764 // ensure tag is right-aligned |
765 $tag_width = strlen($tag_str)*6; |
765 $tag_width = strlen($tag_str)*6; |
766 // write tag |
766 // write tag |
767 ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col); |
767 ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col); |
768 |
768 |
769 // unset all sensetive vars |
769 // unset all sensetive vars |
770 // in case someone include()s this file on a shared server |
770 // in case someone include()s this file on a shared server |
771 // you might think this unneccessary, as it exit()s |
771 // you might think this unneccessary, as it exit()s |
772 // but by using register_shutdown_function |
772 // but by using register_shutdown_function |
773 // on a -very- insecure shared server, they -might- be able to get the word |
773 // on a -very- insecure shared server, they -might- be able to get the word |
774 unset($word); |
774 unset($word); |
775 // the below aren't really essential, but might aid an OCR attack if discovered. |
775 // the below aren't really essential, but might aid an OCR attack if discovered. |
776 // so we unset them |
776 // so we unset them |
777 |
777 |
778 // output final image :-) |
778 // output final image :-) |
779 $this->sendImage($this->im); |
779 $this->sendImage($this->im); |
780 // (sendImage also destroys all used images) |
780 // (sendImage also destroys all used images) |
781 } |
781 } |
782 |
782 |
783 function rand_func($s, $m) |
783 function rand_func($s, $m) |
784 { |
784 { |
785 global $_starttime; |
785 global $_starttime; |
786 $tn = microtime_float() - $_starttime; |
786 $tn = microtime_float() - $_starttime; |
787 if ( $tn > 5 ) |
787 if ( $tn > 5 ) |
788 { |
788 { |
789 echo '<pre>'; |
789 echo '<pre>'; |
790 enano_debug_print_backtrace(); |
790 enano_debug_print_backtrace(); |
791 echo '</pre>'; |
791 echo '</pre>'; |
792 exit; |
792 exit; |
793 } |
793 } |
794 $rf =& $this->rand_func; |
794 $rf =& $this->rand_func; |
795 return $rf($s, $m); |
795 return $rf($s, $m); |
796 } |
796 } |
797 |
797 |
798 function seed_func($s) |
798 function seed_func($s) |
799 { |
799 { |
800 $rf =& $this->seed_func; |
800 $rf =& $this->seed_func; |
801 return $rf($s); |
801 return $rf($s); |
802 } |
802 } |
803 |
803 |
804 function hash_func($s) |
804 function hash_func($s) |
805 { |
805 { |
806 $rf =& $this->hash_func; |
806 $rf =& $this->hash_func; |
807 return $rf($s); |
807 return $rf($s); |
808 } |
808 } |
809 |
809 |
810 } |
810 } |