~ubuntu-branches/ubuntu/raring/webcalendar/raring

« back to all changes in this revision

Viewing changes to includes/classes/captcha/captcha.php

  • Committer: Bazaar Package Importer
  • Author(s): Rafael Laboissiere
  • Date: 2009-06-09 06:26:24 UTC
  • mfrom: (18.2.3 karmic)
  • Revision ID: james.westby@ubuntu.com-20090609062624-9n9xea2ftpipmg38
Tags: 1.2.0+dfsg-4
* debian/patches/06_send-reminder-paths.diff: Adjust patch to help
  translate.php to find the translation files under /etc/webcalendar.
  Thanks to Dale and Cheryl Schroeder for the help on debugging this
  (really, closes: #531312).
* debian/patches/16_no-blink-public-access-title.diff: New patch for
  avoiding the blinking title when changing the Public Access title in
  English-US.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/*
 
3
   Does emit a CAPTCHA graphic and form fields, which allows to tell real
 
4
   people from bots.
 
5
   Though a textual description is generated as well, this sort of access
 
6
   restriction will knock out visually impaired users, and frustrate all
 
7
   others anyhow. Therefore this should only be used as last resort for
 
8
   defending against spambots. Because of the readable text and the used
 
9
   colorspaces this is a weak implementation, not completely OCR-secure.
 
10
 
 
11
   captcha::form () will return a html string to be inserted into textarea/
 
12
   [save] <forms> and alike. User input is veryfied with captcha::check ().
 
13
   You should leave the sample COLLEGE.ttf next to this script, else you
 
14
   have to define the _FONT_DIR constant correctly. Use only 1 font type.
 
15
 
 
16
   Creates temporary files, which however get purged automatically after
 
17
   four hours.
 
18
 
 
19
   Public Domain, available via http://freshmeat.net/p/captchaphp
 
20
 
 
21
  Modified to work with WebCalendar by Ray Jones
 
22
    Added translation function calls
 
23
    Formatting to display properly within our layout
 
24
    Disabled textual_riddle function
 
25
*/
 
26
 
 
27
 
 
28
#-- config
 
29
define( 'EWIKI_FONT_DIR', dirname(__FILE__));  // which fonts to use
 
30
define( 'CAPTCHA_INVERSE', 0);                 // white or black(=1)
 
31
define( 'CAPTCHA_TIMEOUT', 5000);              // in seconds (=max 4 hours)
 
32
define( 'CAPTCHA_MAXSIZE', 4500);              // preferred image size
 
33
define( 'CAPTCHA_COOKIE', 'captcha_solved');   // to unlock captcha protection
 
34
define( 'CAPTCHA_DATA_URLS', 0);               // RFC2397-URLs exclude MSIE users
 
35
define( 'CAPTCHA_TEMP_DIR', 'images/captcha');
 
36
 
 
37
/* static - (you could instantiate it, but...) */
 
38
class captcha {
 
39
 
 
40
 
 
41
   /* gets parameter from $_REQUEST[] array (POST vars) and so can
 
42
      verify input, @returns boolean
 
43
   */
 
44
   function check () {
 
45
      $to = (int)(time ()/1000000);
 
46
      if ( ! empty ( $_COOKIE[CAPTCHA_COOKIE] ) && $_COOKIE[CAPTCHA_COOKIE] == $to) {
 
47
         return(true);
 
48
      }
 
49
      if (($hash = $_REQUEST['captcha_hash'])
 
50
      and ($pw = trim($_REQUEST['captcha_input']))) {
 
51
         $r = (captcha::hash($pw)==$hash) || (captcha::hash($pw,-1)==$hash);
 
52
         if ($r) {
 
53
            setcookie(CAPTCHA_COOKIE, $to, time ()+1000000);
 
54
         }
 
55
         return($r);
 
56
      }
 
57
   }
 
58
 
 
59
 
 
60
   /* yields <input> fields html string (no complete form), with captcha
 
61
      image already embedded as data:-URI
 
62
   */
 
63
   function form () {
 
64
 
 
65
      #-- stop if user already verified
 
66
      if ( ! empty ( $_COOKIE[CAPTCHA_COOKIE] ) &&
 
67
        $_COOKIE[CAPTCHA_COOKIE] == (int)(time ()/1000000)) {
 
68
         return "";
 
69
      }
 
70
 
 
71
      $title = translate ( 'Enter Characters Seen in Graphic' );
 
72
      $more = translate ( 'Enter the correct letters and numbers from the image into the text box...' );
 
73
      #-- prepare image text
 
74
      $pw = captcha::mkpass ();
 
75
      $hash = captcha::hash($pw);
 
76
      //$alt = htmlentities(captcha::textual_riddle($pw));
 
77
      $alt = $title;
 
78
 
 
79
      #-- image
 
80
      $img = captcha::image($pw, 200, 60, CAPTCHA_INVERSE, CAPTCHA_MAXSIZE);
 
81
      if (CAPTCHA_DATA_URLS && !strpos ('MSIE', $_SERVER['HTTP_USER_AGENT'])) {
 
82
         $img_fn = 'data:image/jpeg;base64,' . base64_encode($img);
 
83
      }
 
84
      else {
 
85
         $img_fn = CAPTCHA_TEMP_DIR . '/' . captcha::store_image($img) . '.jpg';
 
86
      }
 
87
//echo $img_fn;
 
88
      #-- emit html form
 
89
      $html = '
 
90
        <div class="captcha">
 
91
          <fieldset style="width:420px">
 
92
            <legend>' . translate ('Challenge/Response') .'</legend>
 
93
            <table border="0" summary="captcha input" width="400px"><tr>
 
94
              <td colspan="2"><small>'.$more.'</small></td></tr><tr>
 
95
              <td><img name="captcha_image" id="captcha_image" src="' .$img_fn.
 
96
        '" height="60" width="200" alt="' .$alt. '" /></td>
 
97
              <td>'.$title. '<br /><input name="captcha_hash" type="hidden" value="'.$hash. '" />
 
98
                <input name="captcha_input" type="text" size="7" maxlength="16" style="height:46px;font-size:34px; font-weight:bold;" />
 
99
              </td></tr></table>
 
100
          </fieldset>
 
101
        </div>
 
102
      ';
 
103
      return($html);
 
104
   }
 
105
 
 
106
 
 
107
   /* generates alternative (non-graphic), human-understandable
 
108
      representation of the passphrase
 
109
   */
 
110
   function textual_riddle($phrase) {
 
111
      $symbols0 = '"\'-/_:';
 
112
      $symbols1 = array ("\n,", "\n;", ";", "\n&", "\n-", ",", ",", "\nand then", "\nfollowed by", "\nand", "\nand not a\n\"".chr(65+rand(0,26))."\",\nbut");
 
113
      $s = "Guess the letters and numbers\n(passphrase riddle)\n--\n";
 
114
      for ($p=0; $p<strlen($phrase); $p++) {
 
115
         $c = $phrase[$p];
 
116
         $add = "";
 
117
         #-- asis
 
118
         if (!rand(0,3)) {
 
119
            $i = $symbols0[rand(0,strlen($symbols0)-1)];
 
120
            $add = "$i$c$i";
 
121
         }
 
122
         #-- letter
 
123
         elseif ($c >= 'A') {
 
124
            $type = ($c >= 'a' ? "small " : "");
 
125
            do {
 
126
               $n = rand(-3,3);
 
127
               $c2 = chr((ord($c) & 0x5F) + $n);
 
128
            }
 
129
            while (($c2 < 'A') || ($c2 > 'Z'));
 
130
            if ($n < 0) {
 
131
               $n = -$n;
 
132
               $add .= "$type'$c2' +$n letters";
 
133
            }
 
134
            else {
 
135
               $add .= "$n chars before $type$c2";
 
136
            }
 
137
         }
 
138
         #-- number
 
139
         else {
 
140
            $add = "???";
 
141
            $n = (int) $c;
 
142
            do {
 
143
               do { $x = rand(1, 10); } while (!$x);
 
144
               $op = rand(0,11);
 
145
               if ($op <= 2) {
 
146
                  $add = "($add * $x)"; $n *= $x;
 
147
               }
 
148
               elseif ($op == 3) {
 
149
                  $x = 2 * rand(1,2);
 
150
                  $add = "($add / $x)"; $n /= $x;
 
151
               }
 
152
               elseif ( ! empty ( $sel ) && $sel % 2) {
 
153
                  $add = "($add + $x)"; $n += $x;
 
154
               }
 
155
               else {
 
156
                  $add = "($add - $x)"; $n -= $x;
 
157
               }
 
158
            }
 
159
            while (rand(0,1));
 
160
            $add .= " = $n";
 
161
         }
 
162
         $s .= "$add";
 
163
         $s .= $symbols1[rand(0,count($symbols1)-1)] . "\n";
 
164
      }
 
165
      return($s);
 
166
   }
 
167
 
 
168
 
 
169
   /* returns jpeg file stream with unscannable letters encoded
 
170
      in front of colorful disturbing background
 
171
   */
 
172
   function image($phrase, $width=200, $height=60, $inverse=0, $maxsize=0xFFFFF) {
 
173
 
 
174
      #-- initialize in-memory image with gd library
 
175
      srand(microtime ()*21017);
 
176
      $img = imagecreatetruecolor($width, $height);
 
177
      $R = $inverse ? 0xFF : 0x00;
 
178
      imagefilledrectangle($img, 0,0, $width,$height, captcha::random_color($img, 222^$R, 255^$R));
 
179
      $c1 = rand(150^$R, 185^$R);
 
180
      $c2 = rand(195^$R, 230^$R);
 
181
 
 
182
      #-- configuration
 
183
      $fonts = array (
 
184
        // "COLLEGE.ttf",
 
185
      );
 
186
      $fonts += glob(EWIKI_FONT_DIR."/*.ttf");
 
187
 
 
188
      #-- encolour bg
 
189
      $wd = 20;
 
190
      $x = 0;
 
191
      $y = $height;
 
192
      while ($x < $width) {
 
193
         imagefilledrectangle($img, $x, 0, $x+=$wd, $height, captcha::random_color($img, 222^$R, 255^$R));
 
194
         $wd += max(10, rand(0, 20) - 10);
 
195
      }
 
196
 
 
197
      #-- make interesting background I, lines
 
198
      $wd = 4;
 
199
      $w1 = 0;
 
200
      $w2 = 0;
 
201
      for ($x=0; $x<$width; $x+=(int)$wd) {
 
202
         if ($x < $width) {   // verical
 
203
            imageline($img, $x+$w1, 0, $x+$w2, $height-1, captcha::random_color($img,$c1,$c2));
 
204
         }
 
205
         if ($x < $height) {  // horizontally ("y")
 
206
            imageline($img, 0, $x-$w2, $width-1, $x-$w1, captcha::random_color($img,$c1,$c2));
 
207
         }
 
208
         $wd += rand(0,8) - 4;
 
209
         if ($wd < 1) { $wd = 2; }
 
210
         $w1 += rand(0,8) - 4;
 
211
         $w2 += rand(0,8) - 4;
 
212
         if (($x > $height) && ($y > $height)) {
 
213
            break;
 
214
         }
 
215
      }
 
216
 
 
217
      #-- more disturbing II, random letters
 
218
      $limit = rand(30,90);
 
219
      for ($n=0; $n<$limit; $n++) {
 
220
         $letter = "";
 
221
         do {
 
222
            $letter .= chr(rand(31,125)); // random symbol
 
223
         } while (rand(0,1));
 
224
         $size = rand(5, $height/2);
 
225
         $half = (int) ($size / 2);
 
226
         $x = rand(-$half, $width+$half);
 
227
         $y = rand(+$half, $height);
 
228
         $rotation = rand(60, 300);
 
229
         $c1  = captcha::random_color($img, 130^$R, 240^$R);
 
230
         $font = $fonts[rand(0, count($fonts)-1)];
 
231
         imagettftext($img, $size, $rotation, $x, $y, $c1, $font, $letter);
 
232
      }
 
233
 
 
234
      #-- add the real text to it
 
235
      $len = strlen($phrase);
 
236
      $w1 = 10;
 
237
      $w2 = $width / ($len+1);
 
238
      for ($p=0; $p<$len; $p++) {
 
239
         $letter = $phrase[$p];
 
240
         $size = rand(18, $height/2.2);
 
241
         $half = (int) $size / 2;
 
242
         $rotation = rand(-33, 33);
 
243
         $y = rand($size+3, $height-3);
 
244
         $x = $w1 + $w2*$p;
 
245
         $w1 += rand(-$width/90, $width/40);  // @BUG: last char could be +30 pixel outside of image
 
246
         $font = $fonts[rand(0, count($fonts)-1)];
 
247
         $r=rand(30,99); $g=rand(30,99); $b=rand(30,99); // two colors for shadow
 
248
         $c1  = imagecolorallocate($img, $r*1^$R, $g*1^$R, $b*1^$R);
 
249
         $c2  = imagecolorallocate($img, $r*2^$R, $g*2^$R, $b*2^$R);
 
250
         imagettftext($img, $size, $rotation, $x+1, $y, $c2, $font, $letter);
 
251
         imagettftext($img, $size, $rotation, $x, $y-1, $c1, $font, $letter);
 
252
      }
 
253
 
 
254
      #-- let JFIF stream be generated
 
255
      $quality = 67;
 
256
      $s = array ();
 
257
      do {
 
258
         ob_start (); ob_implicit_flush(0);
 
259
         imagejpeg($img, "", (int)$quality);
 
260
         $jpeg = ob_get_contents (); ob_end_clean ();
 
261
         $size = strlen($jpeg);
 
262
         $s_debug[] = ((int)($quality*10)/10) . "%=$size";
 
263
         $quality = $quality * ($maxsize/$size) * 0.93 - 1.7;  // -($quality/7.222)*
 
264
      }
 
265
      while (($size > $maxsize) && ($quality >= 16));
 
266
      imagedestroy($img);
 
267
      return($jpeg);
 
268
   }
 
269
 
 
270
 
 
271
   /* helper code */
 
272
   function random_color($img, $a,$b) {
 
273
      return imagecolorallocate($img, rand($a,$b), rand($a,$b), rand($a,$b));
 
274
   }
 
275
 
 
276
 
 
277
   /* creates temporary file, returns basename */
 
278
   function store_image($data) {
 
279
      $dir = CAPTCHA_TEMP_DIR;
 
280
      $id = md5($data);
 
281
 
 
282
      #-- create temp dir
 
283
      if (!file_exists($dir)) {
 
284
         mkdir($dir) && chmod($dir, 0777);
 
285
      }
 
286
 
 
287
      #-- remove stale files
 
288
      if ($dh = opendir($dir)) {
 
289
         $t_kill = time () - CAPTCHA_TIMEOUT;
 
290
         while($fn = readdir($dh)) if ($fn[0] != ".") {
 
291
            if (filemtime("$dir/$fn") < $t_kill) {
 
292
               @unlink("$dir/$fn");
 
293
            }
 
294
         }
 
295
      }
 
296
 
 
297
      #-- store file
 
298
      fwrite($f = fopen("$dir/$id.jpg", 'wb'), $data) && fclose($f);
 
299
      return($id);
 
300
   }
 
301
 
 
302
   /* sends it */
 
303
   function get_image($id) {
 
304
      $dir = CAPTCHA_TEMP_DIR;
 
305
      $fn = "$dir/$id.jpg";
 
306
      #-- find it
 
307
      if (preg_match('/^\w+$/', $id)) {
 
308
         header('Content-Type: image/jpeg');
 
309
         readfile($fn);
 
310
         exit;
 
311
      }
 
312
   }
 
313
 
 
314
 
 
315
   /* unreversable hash from passphrase, with time () slice encoded */
 
316
   function hash($text, $dtime=0) {
 
317
      $text = strtolower($text);
 
318
      $pfix = (int) (time () / CAPTCHA_TIMEOUT) + $dtime;
 
319
      return md5("captcha::$pfix:$text::".__FILE__.":$_SERVER[SERVER_NAME]:80");
 
320
   }
 
321
 
 
322
 
 
323
   /* makes string of random letters for embedding into image and for
 
324
      encoding as hash, later verification
 
325
   */
 
326
   function mkpass () {
 
327
      $s = "";
 
328
      for ($n=0; $n<10; $n++) {
 
329
         $s .= chr(rand(0, 255));
 
330
      }
 
331
      $s = base64_encode($s);   // base64-set, but filter out unwanted chars
 
332
      $s = preg_replace("/[+\/=IG0ODQR]/i", "", $s);  // (depends on YOUR font)
 
333
      $s = substr ($s, 0, rand(5,7));
 
334
      return($s);
 
335
   }
 
336
}
 
337
 
 
338
#-- IE workaround
 
339
if (isset($_REQUEST['_tcf'])) {
 
340
   captcha::get_image($_REQUEST['_tcf']);
 
341
}
 
342
 
 
343
 
 
344
?>