OSDN Git Service

Update main.php
[idb/iDB.git.git] / inc / captcha.php
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 PhpCaptcha(
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++) {
247             if (count($this->aCharSet) > 0) {
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 AudioPhpCaptcha(
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 PhpCaptchaColour($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 ?>