Subversion Repositories cheapmusic

Rev

Rev 103 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
   /***************************************************************/
3
   /* PhpCaptcha - A visual and audio CAPTCHA generation library
4
 
5
      Software License Agreement (BSD License)
6
 
7
      Copyright (C) 2005-2006, Edward Eliot.
8
      All rights reserved.
9
 
10
      Redistribution and use in source and binary forms, with or without
11
      modification, are permitted provided that the following conditions are met:
12
 
13
         * Redistributions of source code must retain the above copyright
14
           notice, this list of conditions and the following disclaimer.
15
         * Redistributions in binary form must reproduce the above copyright
16
           notice, this list of conditions and the following disclaimer in the
17
           documentation and/or other materials provided with the distribution.
18
         * Neither the name of Edward Eliot nor the names of its contributors
19
           may be used to endorse or promote products derived from this software
20
           without specific prior written permission of Edward Eliot.
21
 
22
      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND ANY
23
      EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24
      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
26
      DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27
      (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29
      ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
      (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
      SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 
33
      Last Updated:  18th April 2006                               */
34
   /***************************************************************/
35
 
36
   /************************ Documentation ************************/
37
   /*
38
 
39
   Documentation is available at http://www.ejeliot.com/pages/2
40
 
41
   */
42
   /************************ Default Options **********************/
43
 
44
   // start a PHP session - this class uses sessions to store the generated
45
   // code. Comment out if you are calling already from your application
46
   session_start();
47
 
48
   // class defaults - change to effect globally
49
 
50
   define('CAPTCHA_SESSION_ID', 'php_captcha');
51
   define('CAPTCHA_WIDTH', 200); // max 500
52
   define('CAPTCHA_HEIGHT', 50); // max 200
53
   define('CAPTCHA_NUM_CHARS', 5);
54
   define('CAPTCHA_NUM_LINES', 70);
55
   define('CAPTCHA_CHAR_SHADOW', false);
56
   define('CAPTCHA_OWNER_TEXT', '');
57
   define('CAPTCHA_CHAR_SET', ''); // defaults to A-Z
58
   define('CAPTCHA_CASE_INSENSITIVE', true);
59
   define('CAPTCHA_BACKGROUND_IMAGES', '');
60
   define('CAPTCHA_MIN_FONT_SIZE', 16);
61
   define('CAPTCHA_MAX_FONT_SIZE', 25);
62
   define('CAPTCHA_USE_COLOUR', false);
63
   define('CAPTCHA_FILE_TYPE', 'jpeg');
64
   define('CAPTCHA_FLITE_PATH', '/usr/bin/flite');
65
   define('CAPTCHA_AUDIO_PATH', '/tmp/'); // must be writeable by PHP process
66
 
67
   /************************ End Default Options **********************/
68
 
69
   // don't edit below this line (unless you want to change the class!)
70
 
71
   class PhpCaptcha {
72
      var $oImage;
73
      var $aFonts;
74
      var $iWidth;
75
      var $iHeight;
76
      var $iNumChars;
77
      var $iNumLines;
78
      var $iSpacing;
79
      var $bCharShadow;
80
      var $sOwnerText;
81
      var $aCharSet;
82
      var $bCaseInsensitive;
83
      var $vBackgroundImages;
84
      var $iMinFontSize;
85
      var $iMaxFontSize;
86
      var $bUseColour;
87
      var $sFileType;
88
      var $sCode = '';
89
 
90
      function __construct(
91
         $aFonts, // array of TrueType fonts to use - specify full path
92
         $iWidth = CAPTCHA_WIDTH, // width of image
93
         $iHeight = CAPTCHA_HEIGHT // height of image
94
      ) {
95
         // get parameters
96
         $this->aFonts = $aFonts;
97
         $this->SetNumChars(CAPTCHA_NUM_CHARS);
98
         $this->SetNumLines(CAPTCHA_NUM_LINES);
99
         $this->DisplayShadow(CAPTCHA_CHAR_SHADOW);
100
         $this->SetOwnerText(CAPTCHA_OWNER_TEXT);
101
         $this->SetCharSet(CAPTCHA_CHAR_SET);
102
         $this->CaseInsensitive(CAPTCHA_CASE_INSENSITIVE);
103
         $this->SetBackgroundImages(CAPTCHA_BACKGROUND_IMAGES);
104
         $this->SetMinFontSize(CAPTCHA_MIN_FONT_SIZE);
105
         $this->SetMaxFontSize(CAPTCHA_MAX_FONT_SIZE);
106
         $this->UseColour(CAPTCHA_USE_COLOUR);
107
         $this->SetFileType(CAPTCHA_FILE_TYPE);
108
         $this->SetWidth($iWidth);
109
         $this->SetHeight($iHeight);
110
      }
111
 
112
      function CalculateSpacing() {
113
         $this->iSpacing = (int)($this->iWidth / $this->iNumChars);
114
      }
115
 
116
      function SetWidth($iWidth) {
117
         $this->iWidth = $iWidth;
118
         if ($this->iWidth > 500) $this->iWidth = 500; // to prevent perfomance impact
119
         $this->CalculateSpacing();
120
      }
121
 
122
      function SetHeight($iHeight) {
123
         $this->iHeight = $iHeight;
124
         if ($this->iHeight > 200) $this->iHeight = 200; // to prevent performance impact
125
      }
126
 
127
      function SetNumChars($iNumChars) {
128
         $this->iNumChars = $iNumChars;
129
         $this->CalculateSpacing();
130
      }
131
 
132
      function SetNumLines($iNumLines) {
133
         $this->iNumLines = $iNumLines;
134
      }
135
 
136
      function DisplayShadow($bCharShadow) {
137
         $this->bCharShadow = $bCharShadow;
138
      }
139
 
140
      function SetOwnerText($sOwnerText) {
141
         $this->sOwnerText = $sOwnerText;
142
      }
143
 
144
      function SetCharSet($vCharSet) {
145
         // check for input type
146
         if (is_array($vCharSet)) {
147
            $this->aCharSet = $vCharSet;
148
         } else {
149
            if ($vCharSet != '') {
150
               // split items on commas
151
               $aCharSet = explode(',', $vCharSet);
152
 
153
               // initialise array
154
               $this->aCharSet = array();
155
 
156
               // loop through items
157
               foreach ($aCharSet as $sCurrentItem) {
158
                  // a range should have 3 characters, otherwise is normal character
159
                  if (strlen($sCurrentItem) == 3) {
160
                     // split on range character
161
                     $aRange = explode('-', $sCurrentItem);
162
 
163
                     // check for valid range
164
                     if (count($aRange) == 2 && $aRange[0] < $aRange[1]) {
165
                        // create array of characters from range
166
                        $aRange = range($aRange[0], $aRange[1]);
167
 
168
                        // add to charset array
169
                        $this->aCharSet = array_merge($this->aCharSet, $aRange);
170
                     }
171
                  } else {
172
                     $this->aCharSet[] = $sCurrentItem;
173
                  }
174
               }
175
            }
176
         }
177
      }
178
 
179
      function CaseInsensitive($bCaseInsensitive) {
180
         $this->bCaseInsensitive = $bCaseInsensitive;
181
      }
182
 
183
      function SetBackgroundImages($vBackgroundImages) {
184
         $this->vBackgroundImages = $vBackgroundImages;
185
      }
186
 
187
      function SetMinFontSize($iMinFontSize) {
188
         $this->iMinFontSize = $iMinFontSize;
189
      }
190
 
191
      function SetMaxFontSize($iMaxFontSize) {
192
         $this->iMaxFontSize = $iMaxFontSize;
193
      }
194
 
195
      function UseColour($bUseColour) {
196
         $this->bUseColour = $bUseColour;
197
      }
198
 
199
      function SetFileType($sFileType) {
200
         // check for valid file type
201
         if (in_array($sFileType, array('gif', 'png', 'jpeg'))) {
202
            $this->sFileType = $sFileType;
203
         } else {
204
            $this->sFileType = 'jpeg';
205
         }
206
      }
207
 
208
      function DrawLines() {
209
         for ($i = 0; $i < $this->iNumLines; $i++) {
210
            // allocate colour
211
            if ($this->bUseColour) {
212
               $iLineColour = imagecolorallocate($this->oImage, rand(100, 250), rand(100, 250), rand(100, 250));
213
            } else {
214
               $iRandColour = rand(100, 250);
215
               $iLineColour = imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
216
            }
217
 
218
            // draw line
219
            imageline($this->oImage, rand(0, $this->iWidth), rand(0, $this->iHeight), rand(0, $this->iWidth), rand(0, $this->iHeight), $iLineColour);
220
         }
221
      }
222
 
223
      function DrawOwnerText() {
224
         // allocate owner text colour
225
         $iBlack = imagecolorallocate($this->oImage, 0, 0, 0);
226
         // get height of selected font
227
         $iOwnerTextHeight = imagefontheight(2);
228
         // calculate overall height
229
         $iLineHeight = $this->iHeight - $iOwnerTextHeight - 4;
230
 
231
         // draw line above text to separate from CAPTCHA
232
         imageline($this->oImage, 0, $iLineHeight, $this->iWidth, $iLineHeight, $iBlack);
233
 
234
         // write owner text
235
         imagestring($this->oImage, 2, 3, $this->iHeight - $iOwnerTextHeight - 3, $this->sOwnerText, $iBlack);
236
 
237
         // reduce available height for drawing CAPTCHA
238
         $this->iHeight = $this->iHeight - $iOwnerTextHeight - 5;
239
      }
240
 
241
      function GenerateCode() {
242
         // reset code
243
         $this->sCode = '';
244
 
245
         // loop through and generate the code letter by letter
246
         for ($i = 0; $i < $this->iNumChars; $i++) {
155 - 247
            if (is_countable($this->aCharSet) && count($this->aCharSet) > 0) {
103 - 248
               // select random character and add to code string
249
               $this->sCode .= $this->aCharSet[array_rand($this->aCharSet)];
250
            } else {
251
               // select random character and add to code string
252
               $this->sCode .= chr(rand(65, 90));
253
            }
254
         }
255
 
256
         // save code in session variable
257
         if ($this->bCaseInsensitive) {
258
            $_SESSION[CAPTCHA_SESSION_ID] = strtoupper($this->sCode);
259
         } else {
260
            $_SESSION[CAPTCHA_SESSION_ID] = $this->sCode;
261
         }
262
      }
263
 
264
      function DrawCharacters() {
265
         // loop through and write out selected number of characters
266
         for ($i = 0; $i < strlen($this->sCode); $i++) {
267
            // select random font
268
            $sCurrentFont = $this->aFonts[array_rand($this->aFonts)];
269
 
270
            // select random colour
271
            if ($this->bUseColour) {
272
               $iTextColour = imagecolorallocate($this->oImage, rand(0, 100), rand(0, 100), rand(0, 100));
273
 
274
               if ($this->bCharShadow) {
275
                  // shadow colour
276
                  $iShadowColour = imagecolorallocate($this->oImage, rand(0, 100), rand(0, 100), rand(0, 100));
277
               }
278
            } else {
279
               $iRandColour = rand(0, 100);
280
               $iTextColour = imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
281
 
282
               if ($this->bCharShadow) {
283
                  // shadow colour
284
                  $iRandColour = rand(0, 100);
285
                  $iShadowColour = imagecolorallocate($this->oImage, $iRandColour, $iRandColour, $iRandColour);
286
               }
287
            }
288
 
289
            // select random font size
290
            $iFontSize = rand($this->iMinFontSize, $this->iMaxFontSize);
291
 
292
            // select random angle
293
            $iAngle = rand(-30, 30);
294
 
295
            // get dimensions of character in selected font and text size
296
            $aCharDetails = imageftbbox($iFontSize, $iAngle, $sCurrentFont, $this->sCode[$i], array());
297
 
298
            // calculate character starting coordinates
299
            $iX = $this->iSpacing / 4 + $i * $this->iSpacing;
300
            $iCharHeight = $aCharDetails[2] - $aCharDetails[5];
301
            $iY = $this->iHeight / 2 + $iCharHeight / 4;
302
 
303
            // write text to image
304
            imagefttext($this->oImage, $iFontSize, $iAngle, $iX, $iY, $iTextColour, $sCurrentFont, $this->sCode[$i], array());
305
 
306
            if ($this->bCharShadow) {
307
               $iOffsetAngle = rand(-30, 30);
308
 
309
               $iRandOffsetX = rand(-5, 5);
310
               $iRandOffsetY = rand(-5, 5);
311
 
312
               imagefttext($this->oImage, $iFontSize, $iOffsetAngle, $iX + $iRandOffsetX, $iY + $iRandOffsetY, $iShadowColour, $sCurrentFont, $this->sCode[$i], array());
313
            }
314
         }
315
      }
316
 
317
      function WriteFile($sFilename) {
318
         if ($sFilename == '') {
319
            // tell browser that data is jpeg
320
            header("Content-type: image/$this->sFileType");
321
         }
322
 
323
         switch ($this->sFileType) {
324
            case 'gif':
325
               $sFilename != '' ? imagegif($this->oImage, $sFilename) : imagegif($this->oImage);
326
               break;
327
            case 'png':
328
               $sFilename != '' ? imagepng($this->oImage, $sFilename) : imagepng($this->oImage);
329
               break;
330
            default:
331
               $sFilename != '' ? imagejpeg($this->oImage, $sFilename) : imagejpeg($this->oImage);
332
         }
333
      }
334
 
335
      function Create($sFilename = '') {
336
         // check for required gd functions
337
         if (!function_exists('imagecreate') || !function_exists("image$this->sFileType") || ($this->vBackgroundImages != '' && !function_exists('imagecreatetruecolor'))) {
338
            return false;
339
         }
340
 
341
         // get background image if specified and copy to CAPTCHA
342
         if (is_array($this->vBackgroundImages) || $this->vBackgroundImages != '') {
343
            // create new image
344
            $this->oImage = imagecreatetruecolor($this->iWidth, $this->iHeight);
345
 
346
            // create background image
347
            if (is_array($this->vBackgroundImages)) {
348
               $iRandImage = array_rand($this->vBackgroundImages);
349
               $oBackgroundImage = imagecreatefromjpeg($this->vBackgroundImages[$iRandImage]);
350
            } else {
351
               $oBackgroundImage = imagecreatefromjpeg($this->vBackgroundImages);
352
            }
353
 
354
            // copy background image
355
            imagecopy($this->oImage, $oBackgroundImage, 0, 0, 0, 0, $this->iWidth, $this->iHeight);
356
 
357
            // free memory used to create background image
358
            imagedestroy($oBackgroundImage);
359
         } else {
360
            // create new image
361
            $this->oImage = imagecreate($this->iWidth, $this->iHeight);
362
         }
363
 
364
         // allocate white background colour
365
         imagecolorallocate($this->oImage, 255, 255, 255);
366
 
367
         // check for owner text
368
         if ($this->sOwnerText != '') {
369
            $this->DrawOwnerText();
370
         }
371
 
372
         // check for background image before drawing lines
373
         if (!is_array($this->vBackgroundImages) && $this->vBackgroundImages == '') {
374
            $this->DrawLines();
375
         }
376
 
377
         $this->GenerateCode();
378
         $this->DrawCharacters();
379
 
380
         // write out image to file or browser
381
         $this->WriteFile($sFilename);
382
 
383
         // free memory used in creating image
384
         imagedestroy($this->oImage);
385
 
386
         return true;
387
      }
388
 
389
      // call this method statically
390
      function Validate($sUserCode, $bCaseInsensitive = true) {
391
         if ($bCaseInsensitive) {
392
            $sUserCode = strtoupper($sUserCode);
393
         }
394
 
395
         if (!empty($_SESSION[CAPTCHA_SESSION_ID]) && $sUserCode == $_SESSION[CAPTCHA_SESSION_ID]) {
396
            // clear to prevent re-use
397
            unset($_SESSION[CAPTCHA_SESSION_ID]);
398
 
399
            return true;
400
         }
401
 
402
         return false;
403
      }
404
   }
405
 
406
   // this class will only work correctly if a visual CAPTCHA has been created first using PhpCaptcha
407
   class AudioPhpCaptcha {
408
      var $sFlitePath;
409
      var $sAudioPath;
410
      var $sCode;
411
 
412
      function __construct(
413
         $sFlitePath = CAPTCHA_FLITE_PATH, // path to flite binary
414
         $sAudioPath = CAPTCHA_AUDIO_PATH // the location to temporarily store the generated audio CAPTCHA
415
      ) {
416
         $this->SetFlitePath($sFlitePath);
417
         $this->SetAudioPath($sAudioPath);
418
 
419
         // retrieve code if already set by previous instance of visual PhpCaptcha
420
         if (isset($_SESSION[CAPTCHA_SESSION_ID])) {
421
            $this->sCode = $_SESSION[CAPTCHA_SESSION_ID];
422
         }
423
      }
424
 
425
      function SetFlitePath($sFlitePath) {
426
         $this->sFlitePath = $sFlitePath;
427
      }
428
 
429
      function SetAudioPath($sAudioPath) {
430
         $this->sAudioPath = $sAudioPath;
431
      }
432
 
433
      function Mask($sText) {
434
         $iLength = strlen($sText);
435
 
436
         // loop through characters in code and format
437
         $sFormattedText = '';
438
         for ($i = 0; $i < $iLength; $i++) {
439
            // comma separate all but first and last characters
440
            if ($i > 0 && $i < $iLength - 1) {
441
               $sFormattedText .= ', ';
442
            } elseif ($i == $iLength - 1) { // precede last character with "and"
443
               $sFormattedText .= ' and ';
444
            }
445
            $sFormattedText .= $sText[$i];
446
         }
447
 
448
         $aPhrases = array(
449
            "The %1\$s characters are as follows: %2\$s",
450
            "%2\$s, are the %1\$s letters",
451
            "Here are the %1\$s characters: %2\$s",
452
            "%1\$s characters are: %2\$s",
453
            "%1\$s letters: %2\$s"
454
         );
455
 
456
         $iPhrase = array_rand($aPhrases);
457
 
458
         return sprintf($aPhrases[$iPhrase], $iLength, $sFormattedText);
459
      }
460
 
461
      function Create() {
462
         $sText = $this->Mask($this->sCode);
463
         $sFile = md5($this->sCode.time());
464
 
465
         // create file with flite
466
         shell_exec("$this->sFlitePath -t \"$sText\" -o $this->sAudioPath$sFile.wav");
467
 
468
         // set headers
469
         header('Content-type: audio/x-wav');
470
         header("Content-Disposition: attachment;filename=$sFile.wav");
471
 
472
         // output to browser
473
         echo file_get_contents("$this->sAudioPath$sFile.wav");
474
 
475
         // delete temporary file
476
         @unlink("$this->sAudioPath$sFile.wav");
477
      }
478
   }
479
 
480
   // example sub class
481
   class PhpCaptchaColour extends PhpCaptcha {
482
       function __construct($aFonts, $iWidth = CAPTCHA_WIDTH, $iHeight = CAPTCHA_HEIGHT) {
483
         // call parent constructor
484
         parent::PhpCaptcha($aFonts, $iWidth, $iHeight);
485
 
486
         // set options
487
         $this->UseColour(true);
488
      }
489
   }
490
?>