Changeset 26554


Ignore:
Timestamp:
01/09/14 13:36:32 (6 years ago)
Author:
mistic100
Message:

update Securimage to 3.5.1, new font, new preset, allow to use backgrounds

Location:
extensions/CryptograPHP
Files:
4 added
1 deleted
12 edited
1 moved

Legend:

Unmodified
Added
Removed
  • extensions/CryptograPHP/admin.php

    r26072 r26554  
    3434    'height'          => (int)$_POST['height'], 
    3535    'perturbation'    => (float)$_POST['perturbation'], 
    36     'image_bg_color'  => $_POST['image_bg_color'], 
     36    'background'      => $_POST['background'], 
     37    'bg_color'        => $_POST['bg_color'], 
     38    'bg_image'        => $_POST['bg_image'], 
    3739    'code_length'     => (int)$_POST['code_length'], 
    3840    'text_color'      => $_POST['text_color'], 
     
    5052 
    5153$presets = array( 
    52   'bluenoise' =>  array('perturbation'=>0.25, 'image_bg_color'=>'ffffff', 'text_color'=>'0000ff', 'num_lines'=>2, 'line_color'=>'0000ff', 'noise_level'=>2,   'noise_color'=>'0000ff', 'ttf_file'=>'AlteHassGroteskB'), 
    53   'gray' =>       array('perturbation'=>1,    'image_bg_color'=>'ffffff', 'text_color'=>'8a8a8a', 'num_lines'=>2, 'line_color'=>'8a8a8a', 'noise_level'=>0.1, 'noise_color'=>'8a8a8a', 'ttf_file'=>'TopSecret'), 
    54   'xcolor' =>     array('perturbation'=>0.5,  'image_bg_color'=>'ffffff', 'text_color'=>'random', 'num_lines'=>1, 'line_color'=>'ffffff', 'noise_level'=>2,   'noise_color'=>'ffffff', 'ttf_file'=>'Dread'), 
    55   'pencil' =>     array('perturbation'=>0.8,  'image_bg_color'=>'9e9e9e', 'text_color'=>'363636', 'num_lines'=>0, 'line_color'=>'ffffff', 'noise_level'=>0,   'noise_color'=>'ffffff', 'ttf_file'=>'AllStar'), 
     54  'bluenoise' =>  array('perturbation'=>0.25, 'background'=>'color', 'bg_image'=>'', 'bg_color'=>'ffffff', 'text_color'=>'0000ff', 'num_lines'=>2, 'line_color'=>'0000ff', 'noise_level'=>2, 'noise_color'=>'0000ff', 'ttf_file'=>'AlteHassGroteskB'), 
     55  'gray' =>       array('perturbation'=>1, 'background'=>'color', 'bg_image'=>'', 'bg_color'=>'ffffff', 'text_color'=>'8a8a8a', 'num_lines'=>2, 'line_color'=>'8a8a8a', 'noise_level'=>0.1, 'noise_color'=>'8a8a8a', 'ttf_file'=>'TopSecret'), 
     56  'xcolor' =>     array('perturbation'=>0.5, 'background'=>'color', 'bg_image'=>'', 'bg_color'=>'ffffff', 'text_color'=>'random', 'num_lines'=>1, 'line_color'=>'ffffff', 'noise_level'=>2, 'noise_color'=>'ffffff', 'ttf_file'=>'Dread'), 
     57  'pencil' =>     array('perturbation'=>0.8, 'background'=>'color', 'bg_image'=>'', 'bg_color'=>'9e9e9e', 'text_color'=>'363636', 'num_lines'=>0, 'line_color'=>'ffffff', 'noise_level'=>0, 'noise_color'=>'ffffff', 'ttf_file'=>'AllStar'), 
     58  'ransom' =>     array('perturbation'=>0, 'background'=>'image', 'bg_image'=>'bg1.jpg', 'bg_color'=>'ffffff', 'text_color'=>'4a003a', 'num_lines'=>0, 'line_color'=>'ffffff', 'noise_level'=>0, 'noise_color'=>'ffffff', 'ttf_file'=>'ransom'), 
    5659  ); 
    57    
     60 
     61 
     62$template->assign(array( 
     63  'crypto' => $conf['cryptographp'], 
     64  'loaded' => $loaded, 
     65  'fonts' => list_fonts(CRYPTO_PATH.'securimage/fonts'), 
     66  'backgrounds' => list_backgrounds(CRYPTO_PATH.'securimage/backgrounds'), 
     67  'PRESETS' => $presets, 
     68  'CRYPTO_PATH' => CRYPTO_PATH, 
     69  )); 
     70 
     71$template->set_filename('plugin_admin_content', realpath(CRYPTO_PATH . 'template/admin.tpl')); 
     72$template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content'); 
     73 
     74 
     75 
    5876function list_fonts($dir) 
    5977{ 
     
    6583  { 
    6684    if ($file !== '.' && $file !== '..' && get_extension($file)=='ttf')  
    67       $fonts[] = get_filename_wo_extension($file); 
     85    { 
     86      $fonts[get_filename_wo_extension($file)] = $dir . '/' . $file; 
     87    } 
    6888  } 
    6989   
     
    7292} 
    7393 
    74 $template->assign(array( 
    75   'crypto' => $conf['cryptographp'], 
    76   'loaded' => $loaded, 
    77   'fonts' => list_fonts(CRYPTO_PATH.'securimage/fonts'), 
    78   'PRESETS' => $presets, 
    79   'CRYPTO_PATH' => CRYPTO_PATH, 
    80   )); 
    81  
    82 $template->set_filename('plugin_admin_content', dirname(__FILE__).'/template/admin.tpl'); 
    83 $template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content'); 
     94function list_backgrounds($dir) 
     95{ 
     96  $dir = rtrim($dir, '/'); 
     97  $dh = opendir($dir); 
     98  $backgrounds = array(); 
     99   
     100  while (($file = readdir($dh)) !== false ) 
     101  { 
     102    if ($file !== '.' && $file !== '..') 
     103    { 
     104      $ext = get_extension($file); 
     105      if ($ext=='jpg' || $ext=='png' || $ext=='jpeg' || $ext=='gif') 
     106      { 
     107        $backgrounds[$file] = $dir . '/' . $file; 
     108      } 
     109    } 
     110  } 
     111   
     112  closedir($dh); 
     113  return $backgrounds; 
     114} 
  • extensions/CryptograPHP/language/en_UK/plugin.lang.php

    r26041 r26554  
    55$lang['Solve equation'] = 'Solve equation'; 
    66$lang['Button color'] = 'Button color'; 
    7  
    87$lang['Comments action'] = 'Comments action'; 
    98$lang['Moderate'] = 'Moderate'; 
     
    2120$lang['Contact form'] = 'Contact form'; 
    2221$lang['Guestbook'] = 'Guestbook'; 
    23  
    2422$lang['Perturbation'] = 'Perturbation'; 
    2523$lang['range:'] = 'range:'; 
     
    3331$lang['Font'] = 'Font'; 
    3432$lang['Preview'] = 'Preview'; 
     33$lang['Background'] = 'Background'; 
     34$lang['Background image'] = 'Background image'; 
     35$lang['Color'] = 'Color'; 
     36$lang['Image'] = 'Image'; 
    3537$lang['Tip: type "random" on a color field to have a random color'] = 'Tip: type "random" on a color field to have a random color'; 
    3638$lang['We detected that EasyCaptcha plugin is available on your gallery. Both plugins can be used at the same time, but you should not under any circumstances activate both of them on the same page.'] = 'We detected that EasyCaptcha plugin is available in your gallery. Both plugins can be used at the same time, but you should not under any circumstances activate both of them on the same page.'; 
  • extensions/CryptograPHP/language/fr_FR/plugin.lang.php

    r26041 r26554  
    55$lang['Solve equation'] = 'Resolvez l\'équation'; 
    66$lang['Button color'] = 'Couleur du bouton'; 
    7  
    87$lang['Comments action'] = 'Action pour les commentaires'; 
    98$lang['Moderate'] = 'Modérer'; 
     
    2120$lang['Contact form'] = 'Formulaire de contact'; 
    2221$lang['Guestbook'] = 'Livre d\'or'; 
    23  
    2422$lang['Perturbation'] = 'Perturbation'; 
    2523$lang['range:'] = 'plage:'; 
     
    3331$lang['Font'] = 'Police'; 
    3432$lang['Preview'] = 'Prévisualisation'; 
     33$lang['Background'] = 'Fond'; 
     34$lang['Background image'] = 'Image de fond'; 
     35$lang['Color'] = 'Couleur'; 
     36$lang['Image'] = 'Image'; 
    3537$lang['Tip: type "random" on a color field to have a random color'] = 'Astuce: inscrivez "random" dans un champs de couleur pour avoir une couleur aléatoire'; 
    3638$lang['We detected that EasyCaptcha plugin is available on your gallery. Both plugins can be used at the same time, but you should not under any circumstances activate both of them on the same page.'] = 'Nous avons détecté que EasyCaptcha est activé sur votre galerie. Les deux plugins peuvent fonctionner en même temps, mais sous aucun prétexte ils ne doivent être activés sur les mêmes pages.'; 
  • extensions/CryptograPHP/maintain.inc.php

    r26072 r26554  
    3232        'width'           => 180,  
    3333        'height'          => 70, 
    34         'perturbation'    => 1,  
    35         'image_bg_color'  => 'ffffff',  
     34        'perturbation'    => 1, 
     35        'background'      => 'color', 
     36        'bg_color'        => 'ffffff',  
     37        'bg_image'        => '',  
    3638        'text_color'      => '8a8a8a',  
    3739        'num_lines'       => 2,  
     
    6466        $old_conf['button_color'] = 'dark'; 
    6567      } 
     68      if (!isset($old_conf['background'])) 
     69      { 
     70        $old_conf['background'] = 'color'; 
     71        $old_conf['bg_color'] = $old_conf['image_bg_color']; 
     72        $old_conf['bg_image'] = ''; 
     73        unset($old_conf['image_bg_color']); 
     74      } 
    6675       
    6776      $conf['cryptographp'] = serialize($old_conf); 
  • extensions/CryptograPHP/securimage/README.txt

    r12617 r26554  
    33    Securimage - A PHP class for creating captcha images and audio with many options. 
    44 
    5 VERSION: 3.0 
     5VERSION: 3.5.1 
    66 
    77AUTHOR: 
     
    2323    GD  2.0 
    2424    FreeType (Required, for TTF fonts) 
     25    PDO (if using Sqlite, MySQL, or PostgreSQL) 
    2526 
    2627SYNOPSIS: 
     
    5354 
    5455COPYRIGHT: 
    55     Copyright (c) 2011 Drew Phillips 
     56    Copyright (c) 2013 Drew Phillips 
    5657    All rights reserved. 
    5758 
     
    7879 
    7980    ----------------------------------------------------------------------------- 
    80     Flash code created for Securimage by Mario Romero (animario@hotmail.com) 
     81    The WavFile.php class used in Securimage by Drew Phillips and Paul Voegler is 
     82    used under the BSD License.  See WavFile.php for details. 
     83    Many thanks to Paul Voegler (http://www.voegler.eu/) for contributing to 
     84    Securimage. 
     85 
     86    ----------------------------------------------------------------------------- 
     87    Flash code created for Securimage by Age Bosma & Mario Romero (animario@hotmail.com) 
    8188    Many thanks for releasing this to the project! 
    8289 
     
    107114    yann@lecoroller.com 
    108115 
     116    ------------------------------------------------------------------------------- 
     117    Portions of securimage_play.swf use the PopForge flash library for playing audio 
     118 
     119    /** 
     120     * Copyright(C) 2007 Andre Michelle and Joa Ebert 
     121     * 
     122     * PopForge is an ActionScript3 code sandbox developed by Andre Michelle and Joa Ebert 
     123     * http://sandbox.popforge.de 
     124     * 
     125     * PopforgeAS3Audio is free software; you can redistribute it and/or modify 
     126     * it under the terms of the GNU General Public License as published by 
     127     * the Free Software Foundation; either version 3 of the License, or 
     128     * (at your option) any later version. 
     129     * 
     130     * PopforgeAS3Audio is distributed in the hope that it will be useful, 
     131     * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     132     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
     133     * GNU General Public License for more details. 
     134     * 
     135     * You should have received a copy of the GNU General Public License 
     136     * along with this program. If not, see <http://www.gnu.org/licenses/> 
     137     */ 
     138      
     139     ------------------------------------------------------------------------------- 
     140     Some graphics used are from the Humility Icon Pack by WorLord 
     141 
     142     License: GNU/GPL (http://findicons.com/pack/1723/humility) 
     143     http://findicons.com/icon/192558/gnome_volume_control 
     144     http://findicons.com/icon/192562/gtk_refresh 
     145 
     146     ------------------------------------------------------------------------------- 
     147     Background noise sound files are from SoundJay.com 
     148     http://www.soundjay.com/tos.html 
     149      
     150     All sound effects on this website are created by us and protected under 
     151     the copyright laws, international treaty provisions and other applicable 
     152     laws. By downloading sounds, music or any material from this site implies 
     153     that you have read and accepted these terms and conditions: 
     154 
     155     Sound Effects 
     156     You are allowed to use the sounds free of charge and royalty free in your 
     157     projects (such as films, videos, games, presentations, animations, stage 
     158     plays, radio plays, audio books, apps) be it for commercial or 
     159     non-commercial purposes. 
     160     
     161     But you are NOT allowed to 
     162     - post the sounds (as sound effects or ringtones) on any website for 
     163       others to download, copy or use 
     164     - use them as a raw material to create sound effects or ringtones that 
     165       you will sell, distribute or offer for downloading 
     166     - sell, re-sell, license or re-license the sounds (as individual sound 
     167       effects or as a sound effects library) to anyone else 
     168     - claim the sounds as yours 
     169     - link directly to individual sound files 
     170     - distribute the sounds in apps or computer programs that are clearly 
     171       sound related in nature (such as sound machine, sound effect 
     172       generator, ringtone maker, funny sounds app, sound therapy app, etc.) 
     173       or in apps or computer programs that use the sounds as the program's 
     174       sound resource library for other people's use (such as animation 
     175       creator, digital book creator, song maker software, etc.). If you are 
     176       developing such computer programs, contact us for licensing options. 
     177     
     178     If you use the sound effects, please consider giving us a credit and 
     179     linking back to us but it's not required. 
     180      
     181      
  • extensions/CryptograPHP/securimage/securimage.php

    r12617 r26554  
    11<?php 
     2 
     3// error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging 
     4 
    25/** 
    3  * Project:     Securimage: A PHP class for creating and managing form CAPTCHA images 
    4  * File:        securimage.php 
     6 * Project:     Securimage: A PHP class for creating and managing form CAPTCHA images<br /> 
     7 * File:        securimage.php<br /> 
    58 * 
    6  * Copyright (c) 2011, Drew Phillips 
     9 * Copyright (c) 2013, Drew Phillips 
    710 * All rights reserved. 
    8  *  
     11 * 
    912 * Redistribution and use in source and binary forms, with or without modification, 
    1013 * are permitted provided that the following conditions are met: 
    11  *  
     14 * 
    1215 *  - Redistributions of source code must retain the above copyright notice, 
    1316 *    this list of conditions and the following disclaimer. 
     
    1518 *    this list of conditions and the following disclaimer in the documentation 
    1619 *    and/or other materials provided with the distribution. 
    17  *  
     20 * 
    1821 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
    1922 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
     
    2932 * 
    3033 * Any modifications to the library should be indicated clearly in the source code 
    31  * to inform users that the changes are not a part of the original software. 
     34 * to inform users that the changes are not a part of the original software.<br /><br /> 
    3235 * 
    33  * If you found this script useful, please take a quick moment to rate it. 
     36 * If you found this script useful, please take a quick moment to rate it.<br /> 
    3437 * http://www.hotscripts.com/rate/49400.html  Thanks. 
    3538 * 
     
    3740 * @link http://www.phpcaptcha.org/latest.zip Download Latest Version 
    3841 * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation 
    39  * @copyright 2011 Drew Phillips 
     42 * @copyright 2013 Drew Phillips 
    4043 * @author Drew Phillips <drew@drew-phillips.com> 
    41  * @version 3.0 (October 2011) 
     44 * @version 3.5.1 (June 21, 2013) 
    4245 * @package Securimage 
    4346 * 
     
    4649/** 
    4750 ChangeLog 
    48   
     51 
     52 3.5.1 
     53 - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>) 
     54 
     55 3.5 
     56 - Release new version 
     57 - MB string support for charlist 
     58 - Modify audio file path to use language directories 
     59 - Changed default captcha appearance 
     60 
     61 3.2RC4 
     62 - Add MySQL, PostgreSQL, and SQLite3 support for database storage 
     63 - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions 
     64 - Add new captcha type that displays 2 dictionary words on one image 
     65 - Update examples 
     66 
     67 3.2RC3 
     68 - Fix canSendHeaders() check which was breaking if a PHP startup error was issued 
     69 
     70 3.2RC2 
     71 - Add error handler (https://github.com/dapphp/securimage/issues/15) 
     72 - Fix flash examples to use the correct value name for audio parameter 
     73 
     74 3.2RC1 
     75 - New audio captcha code.  Faster, fully dynamic audio, full WAV support 
     76   (Paul Voegler, Drew Phillips) <http://voegler.eu/pub/audio> 
     77 - New Flash audio streaming button.  User defined image and size supported 
     78 - Additional options for customizing captcha (noise_level, send_headers, 
     79   no_exit, no_session, display_value 
     80 - Add captcha ID support.  Uses sqlite and unique captcha IDs to track captchas, 
     81   no session used 
     82 - Add static methods for creating and validating captcha by ID 
     83 - Automatic clearing of old codes from SQLite database 
     84 
     85 3.0.3Beta 
     86 - Add improved mixing function to WavFile class (Paul Voegler) 
     87 - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips) 
     88 - Add option to use random file as background noise in captcha audio 
     89 - Add new securimage options for audio files 
     90 
     91 3.0.2Beta 
     92 - Fix issue with session variables when upgrading from 2.0 - 3.0 
     93 - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work 
     94 
     95 3.0.1 
     96 - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on 
     97 
    4998 3.0 
    5099 - Rewrite class using PHP5 OOP 
     
    78127 - Audio output is mp3 format by default 
    79128 - Change font to AlteHaasGrotesk by yann le coroller 
    80  - Some code cleanup  
     129 - Some code cleanup 
    81130 
    82131 1.0.4 (unreleased) 
     
    108157 * Securimage CAPTCHA Class. 
    109158 * 
    110  * @version    3.0 
     159 * @version    3.5 
    111160 * @package    Securimage 
    112161 * @subpackage classes 
     
    116165class Securimage 
    117166{ 
    118         // All of the public variables below are securimage options 
    119         // They can be passed as an array to the Securimage constructor, set below, 
    120         // or set from securimage_show.php and securimage_play.php 
    121          
     167    // All of the public variables below are securimage options 
     168    // They can be passed as an array to the Securimage constructor, set below, 
     169    // or set from securimage_show.php and securimage_play.php 
     170 
    122171    /** 
    123172     * Renders captcha as a JPEG image 
     
    135184     */ 
    136185    const SI_IMAGE_GIF  = 3; 
    137      
     186 
    138187    /** 
    139188     * Create a normal alphanumeric captcha 
     
    146195     */ 
    147196    const SI_CAPTCHA_MATHEMATIC = 1; 
    148      
     197    /** 
     198     * Create a word based captcha using 2 words 
     199     * @var int 
     200     */ 
     201    const SI_CAPTCHA_WORDS      = 2; 
     202 
     203    /** 
     204     * MySQL option identifier for database storage option 
     205     * 
     206     * @var string 
     207     */ 
     208    const SI_DRIVER_MYSQL   = 'mysql'; 
     209 
     210    /** 
     211     * PostgreSQL option identifier for database storage option 
     212     * 
     213     * @var string 
     214     */ 
     215    const SI_DRIVER_PGSQL   = 'pgsql'; 
     216 
     217    /** 
     218     * SQLite option identifier for database storage option 
     219     * 
     220     * @var string 
     221     */ 
     222    const SI_DRIVER_SQLITE3 = 'sqlite'; 
     223 
     224    /*%*********************************************************************%*/ 
     225    // Properties 
     226 
    149227    /** 
    150228     * The width of the captcha image 
     
    180258    /** 
    181259     * The color of the noise that is drawn 
    182      * @var Securimage_Color  
     260     * @var Securimage_Color 
    183261     */ 
    184262    public $noise_color    = '#707070'; 
    185      
     263 
    186264    /** 
    187265     * How transparent to make the text 0 = completely opaque, 100 = invisible 
    188266     * @var int 
    189267     */ 
    190     public $text_transparency_percentage = 50; 
     268    public $text_transparency_percentage = 20; 
    191269    /** 
    192270     * Whether or not to draw the text transparently, true = use transparency, false = no transparency 
    193271     * @var bool 
    194272     */ 
    195     public $use_transparent_text         = false; 
    196      
     273    public $use_transparent_text         = true; 
     274 
    197275    /** 
    198276     * The length of the captcha code 
     
    215293     */ 
    216294    public $expiry_time    = 900; 
    217      
     295 
    218296    /** 
    219297     * The session name securimage should use, only set this if your application uses a custom session name 
     
    222300     */ 
    223301    public $session_name   = null; 
    224      
     302 
    225303    /** 
    226304     * true to use the wordlist file, false to generate random captcha codes 
     
    233311     * @var double 
    234312     */ 
    235     public $perturbation = 0.75; 
     313    public $perturbation = 0.85; 
    236314    /** 
    237315     * How many lines to draw over the captcha code to increase security 
    238316     * @var int 
    239317     */ 
    240     public $num_lines    = 8; 
     318    public $num_lines    = 5; 
    241319    /** 
    242320     * The level of noise (random dots) to place on the image, 0-10 
    243321     * @var int 
    244322     */ 
    245     public $noise_level  = 0; 
    246      
     323    public $noise_level  = 2; 
     324 
    247325    /** 
    248326     * The signature text to draw on the bottom corner of the image 
     
    260338     */ 
    261339    public $signature_font; 
    262      
    263     /** 
     340 
     341    /** 
     342     * DO NOT USE!!! 
    264343     * Use an SQLite database to store data (for users that do not support cookies) 
    265344     * @var bool 
     345     * @see Securimage::$use_sqlite_db 
     346     * @deprecated 3.2RC4 
    266347     */ 
    267348    public $use_sqlite_db = false; 
    268      
     349 
     350    /** 
     351     * Use a database backend for code storage. 
     352     * Provides a fallback to users with cookies disabled. 
     353     * Required when using captcha IDs. 
     354     * 
     355     * @see Securimage::$database_driver 
     356     * @var bool 
     357     */ 
     358    public $use_database = false; 
     359 
     360    /** 
     361     * Database driver to use for database support. 
     362     * Allowable values: 'mysql', 'pgsql', 'sqlite'. 
     363     * Default: sqlite 
     364     * 
     365     * @var string 
     366     */ 
     367    public $database_driver = self::SI_DRIVER_SQLITE3; 
     368 
     369    /** 
     370     * Database host to connect to when using mysql or postgres 
     371     * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP 
     372     * Does not apply to SQLite 
     373     * 
     374     * @var string 
     375     */ 
     376    public $database_host   = 'localhost'; 
     377 
     378    /** 
     379     * Database username for connection (mysql, postgres only) 
     380     * Default is an empty string 
     381     * 
     382     * @var string 
     383     */ 
     384    public $database_user   = ''; 
     385 
     386    /** 
     387     * Database password for connection (mysql, postgres only) 
     388     * Default is empty string 
     389     * 
     390     * @var string 
     391     */ 
     392    public $database_pass   = ''; 
     393 
     394    /** 
     395     * Name of the database to select (mysql, postgres only) 
     396     * 
     397     * @see Securimage::$database_file for SQLite 
     398     * @var string 
     399     */ 
     400    public $database_name   = ''; 
     401 
     402    /** 
     403     * Database table where captcha codes are stored 
     404     * Note: Securimage will attempt to create this table for you if it does 
     405     * not exist.  If the table cannot be created, an E_USER_WARNING is emitted. 
     406     * 
     407     * @var string 
     408     */ 
     409    public $database_table  = 'captcha_codes'; 
     410 
     411    /** 
     412     * Fully qualified path to the database file when using SQLite3. 
     413     * This value is only used when $database_driver == sqlite3 and does 
     414     * not apply when no database is used, or when using MySQL or PostgreSQL. 
     415     * 
     416     * @var string 
     417     */ 
     418    public $database_file; 
     419 
    269420    /** 
    270421     * The type of captcha to create, either alphanumeric, or a math problem<br /> 
     
    272423     * @var int 
    273424     */ 
    274     public $captcha_type  = self::SI_CAPTCHA_STRING; 
    275      
     425    public $captcha_type  = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC; 
     426 
    276427    /** 
    277428     * The captcha namespace, use this if you have multiple forms on a single page, blank if you do not use multiple forms on one page 
     
    281432     * // in securimage_show.php (create one show script for each form) 
    282433     * $img->namespace = 'contact_form'; 
    283      *  
     434     * 
    284435     * // in form validator 
    285436     * $img->namespace = 'contact_form'; 
     
    290441     */ 
    291442    public $namespace; 
    292      
     443 
    293444    /** 
    294445     * The font file to use to draw the captcha code, leave blank for default font AHGBold.ttf 
     
    308459    /** 
    309460     * The path to the SQLite database file to use, if $use_sqlite_database = true, should be chmod 666 
     461     * @deprecated 3.2RC4 
    310462     * @var string 
    311463     */ 
     
    315467     * @var string 
    316468     * <code> 
    317      * $img->audio_path = '/home/yoursite/public_html/securimage/audio/'; 
     469     * $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/'; 
    318470     * </code> 
    319471     */ 
    320472    public $audio_path; 
    321  
    322      
    323      
     473    /** 
     474     * The path to the directory containing audio files that will be selected 
     475     * randomly and mixed with the captcha audio. 
     476     * 
     477     * @var string 
     478     */ 
     479    public $audio_noise_path; 
     480    /** 
     481     * Whether or not to mix background noise files into captcha audio (true = mix, false = no) 
     482     * Mixing random background audio with noise can help improve security of audio captcha. 
     483     * Default: securimage/audio/noise 
     484     * 
     485     * @since 3.0.3 
     486     * @see Securimage::$audio_noise_path 
     487     * @var bool 
     488     */ 
     489    public $audio_use_noise; 
     490    /** 
     491     * The method and threshold (or gain factor) used to normalize the mixing with background noise. 
     492     * See http://www.voegler.eu/pub/audio/ for more information. 
     493     * 
     494     * Valid: <ul> 
     495     *     <li> >= 1 - Normalize by multiplying by the threshold (boost - positive gain). <br /> 
     496     *            A value of 1 in effect means no normalization (and results in clipping). </li> 
     497     *     <li> <= -1 - Normalize by dividing by the the absolute value of threshold (attenuate - negative gain). <br /> 
     498     *            A factor of 2 (-2) is about 6dB reduction in volume.</li> 
     499     *     <li> [0, 1) - (open inverval - not including 1) - The threshold 
     500     *            above which amplitudes are comressed logarithmically. <br /> 
     501     *            e.g. 0.6 to leave amplitudes up to 60% "as is" and compress above. </li> 
     502     *     <li> (-1, 0) - (open inverval - not including -1 and 0) - The threshold 
     503     *            above which amplitudes are comressed linearly. <br /> 
     504     *            e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above. </li></ul> 
     505     * 
     506     * Default: 0.6 
     507     * 
     508     * @since 3.0.4 
     509     * @var float 
     510     */ 
     511    public $audio_mix_normalization = 0.6; 
     512    /** 
     513     * Whether or not to degrade audio by introducing random noise (improves security of audio captcha) 
     514     * Default: true 
     515     * 
     516     * @since 3.0.3 
     517     * @var bool 
     518     */ 
     519    public $degrade_audio; 
     520    /** 
     521     * Minimum delay to insert between captcha audio letters in milliseconds 
     522     * 
     523     * @since 3.0.3 
     524     * @var float 
     525     */ 
     526    public $audio_gap_min = 0; 
     527    /** 
     528     * Maximum delay to insert between captcha audio letters in milliseconds 
     529     * 
     530     * @since 3.0.3 
     531     * @var float 
     532     */ 
     533    public $audio_gap_max = 600; 
     534 
     535    /** 
     536     * Captcha ID if using static captcha 
     537     * @var string Unique captcha id 
     538     */ 
     539    protected static $_captchaId = null; 
     540 
    324541    protected $im; 
    325542    protected $tmpimg; 
    326543    protected $bgimg; 
    327544    protected $iscale = 5; 
    328      
    329     protected $securimage_path = null; 
    330      
     545 
     546    public $securimage_path = null; 
     547 
     548    /** 
     549     * The captcha challenge value (either the case-sensitive/insensitive word captcha, or the solution to the math captcha) 
     550     * 
     551     * @var string Captcha challenge value 
     552     */ 
    331553    protected $code; 
     554 
     555    /** 
     556     * The display value of the captcha to draw on the image (the word captcha, or the math equation to present to the user) 
     557     * 
     558     * @var string Captcha display value to draw on the image 
     559     */ 
    332560    protected $code_display; 
    333      
     561 
     562    /** 
     563     * A value that can be passed to the constructor that can be used to generate a captcha image with a given value 
     564     * This value does not get stored in the session or database and is only used when calling Securimage::show(). 
     565     * If a display_value was passed to the constructor and the captcha image is generated, the display_value will be used 
     566     * as the string to draw on the captcha image.  Used only if captcha codes are generated and managed by a 3rd party app/library 
     567     * 
     568     * @var string Captcha code value to display on the image 
     569     */ 
     570    public $display_value; 
     571 
     572    /** 
     573     * Captcha code supplied by user [set from Securimage::check()] 
     574     * 
     575     * @var string 
     576     */ 
    334577    protected $captcha_code; 
    335     protected $sqlite_handle; 
    336      
     578 
     579    /** 
     580     * Flag that can be specified telling securimage not to call exit after generating a captcha image or audio file 
     581     * 
     582     * @var bool If true, script will not terminate; if false script will terminate (default) 
     583     */ 
     584    protected $no_exit; 
     585 
     586    /** 
     587     * Flag indicating whether or not a PHP session should be started and used 
     588     * 
     589     * @var bool If true, no session will be started; if false, session will be started and used to store data (default) 
     590     */ 
     591    protected $no_session; 
     592 
     593    /** 
     594     * Flag indicating whether or not HTTP headers will be sent when outputting captcha image/audio 
     595     * 
     596     * @var bool If true (default) headers will be sent, if false, no headers are sent 
     597     */ 
     598    protected $send_headers; 
     599 
     600    /** 
     601     * PDO connection when a database is used 
     602     * 
     603     * @var resource 
     604     */ 
     605    protected $pdo_conn; 
     606 
     607    // gd color resources that are allocated for drawing the image 
    337608    protected $gdbgcolor; 
    338609    protected $gdtextcolor; 
    339610    protected $gdlinecolor; 
    340611    protected $gdsignaturecolor; 
    341      
     612 
    342613    /** 
    343614     * Create a new securimage object, pass options to set in the constructor.<br /> 
     
    352623     *     'font_file' => Securimage::getPath() . '/custom.ttf' 
    353624     * ); 
    354      *  
     625     * 
    355626     * $img = new Securimage($options); 
    356627     * </code> 
     
    359630    { 
    360631        $this->securimage_path = dirname(__FILE__); 
    361          
     632 
    362633        if (is_array($options) && sizeof($options) > 0) { 
    363634            foreach($options as $prop => $val) { 
    364                 $this->$prop = $val; 
     635                if ($prop == 'captchaId') { 
     636                    Securimage::$_captchaId = $val; 
     637                    $this->use_database     = true; 
     638                } else if ($prop == 'use_sqlite_db') { 
     639                    trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE); 
     640                } else { 
     641                    $this->$prop = $val; 
     642                } 
    365643            } 
    366644        } 
     
    372650        $this->signature_color = $this->initColor($this->signature_color, '#616161'); 
    373651 
    374         if ($this->ttf_file == null) { 
     652        if (is_null($this->ttf_file)) { 
    375653            $this->ttf_file = $this->securimage_path . '/AHGBold.ttf'; 
    376654        } 
    377          
     655 
    378656        $this->signature_font = $this->ttf_file; 
    379          
    380         if ($this->wordlist_file == null) { 
     657 
     658        if (is_null($this->wordlist_file)) { 
    381659            $this->wordlist_file = $this->securimage_path . '/words/words.txt'; 
    382660        } 
    383          
    384         if ($this->sqlite_database == null) { 
    385             $this->sqlite_database = $this->securimage_path . '/database/securimage.sqlite'; 
    386         } 
    387          
    388         if ($this->audio_path == null) { 
    389             $this->audio_path = $this->securimage_path . '/audio/'; 
    390         } 
    391          
    392         if ($this->code_length == null || $this->code_length < 1) { 
     661 
     662        if (is_null($this->database_file)) { 
     663            $this->database_file = $this->securimage_path . '/database/securimage.sq3'; 
     664        } 
     665 
     666        if (is_null($this->audio_path)) { 
     667            $this->audio_path = $this->securimage_path . '/audio/en/'; 
     668        } 
     669 
     670        if (is_null($this->audio_noise_path)) { 
     671            $this->audio_noise_path = $this->securimage_path . '/audio/noise/'; 
     672        } 
     673 
     674        if (is_null($this->audio_use_noise)) { 
     675            $this->audio_use_noise = true; 
     676        } 
     677 
     678        if (is_null($this->degrade_audio)) { 
     679            $this->degrade_audio = true; 
     680        } 
     681 
     682        if (is_null($this->code_length) || (int)$this->code_length < 1) { 
    393683            $this->code_length = 6; 
    394684        } 
    395          
    396         if ($this->perturbation == null || !is_numeric($this->perturbation)) { 
     685 
     686        if (is_null($this->perturbation) || !is_numeric($this->perturbation)) { 
    397687            $this->perturbation = 0.75; 
    398688        } 
    399          
    400         if ($this->namespace == null || !is_string($this->namespace)) { 
     689 
     690        if (is_null($this->namespace) || !is_string($this->namespace)) { 
    401691            $this->namespace = 'default'; 
    402692        } 
    403693 
    404         // Initialize session or attach to existing 
    405         if ( session_id() == '' ) { // no session has been started yet, which is needed for validation 
    406             if ($this->session_name != null && trim($this->session_name) != '') { 
    407                 session_name(trim($this->session_name)); // set session name if provided 
    408             } 
    409             session_start(); 
    410         } 
    411     } 
    412      
     694        if (is_null($this->no_exit)) { 
     695            $this->no_exit = false; 
     696        } 
     697 
     698        if (is_null($this->no_session)) { 
     699            $this->no_session = false; 
     700        } 
     701 
     702        if (is_null($this->send_headers)) { 
     703            $this->send_headers = true; 
     704        } 
     705 
     706        if ($this->no_session != true) { 
     707            // Initialize session or attach to existing 
     708            if ( session_id() == '' ) { // no session has been started yet, which is needed for validation 
     709                if (!is_null($this->session_name) && trim($this->session_name) != '') { 
     710                    session_name(trim($this->session_name)); // set session name if provided 
     711                } 
     712                session_start(); 
     713            } 
     714        } 
     715    } 
     716 
    413717    /** 
    414718     * Return the absolute path to the Securimage directory 
     
    419723        return dirname(__FILE__); 
    420724    } 
    421      
     725 
     726    /** 
     727     * Generate a new captcha ID or retrieve the current ID 
     728     * 
     729     * @param $new bool If true, generates a new challenge and returns and ID 
     730     * @param $options array Additional options to be passed to Securimage. 
     731     * Must include database options if not set directly in securimage.php 
     732     * 
     733     * @return null|string Returns null if no captcha id set and new was false, or string captcha ID 
     734     */ 
     735    public static function getCaptchaId($new = true, array $options = array()) 
     736    { 
     737        if (is_null($new) || (bool)$new == true) { 
     738            $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true)); 
     739            $opts = array('no_session'    => true, 
     740                          'use_database'  => true); 
     741            if (sizeof($options) > 0) $opts = array_merge($options, $opts); 
     742            $si = new self($opts); 
     743            Securimage::$_captchaId = $id; 
     744            $si->createCode(); 
     745 
     746            return $id; 
     747        } else { 
     748            return Securimage::$_captchaId; 
     749        } 
     750    } 
     751 
     752    /** 
     753     * Validate a captcha code input against a captcha ID 
     754     * 
     755     * @param string $id       The captcha ID to check 
     756     * @param string $value    The captcha value supplied by the user 
     757     * @param array  $options  Array of options to construct Securimage with. 
     758     * Options must include database options if they are not set in securimage.php 
     759     * 
     760     * @see Securimage::$database_driver 
     761     * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open 
     762     */ 
     763    public static function checkByCaptchaId($id, $value, array $options = array()) 
     764    { 
     765        $opts = array('captchaId'    => $id, 
     766                      'no_session'   => true, 
     767                      'use_database' => true); 
     768 
     769        if (sizeof($options) > 0) $opts = array_merge($options, $opts); 
     770 
     771        $si = new self($opts); 
     772 
     773        if ($si->openDatabase()) { 
     774            $code = $si->getCodeFromDatabase(); 
     775 
     776            if (is_array($code)) { 
     777                $si->code         = $code['code']; 
     778                $si->code_display = $code['code_disp']; 
     779            } 
     780 
     781            if ($si->check($value)) { 
     782                $si->clearCodeFromDatabase(); 
     783 
     784                return true; 
     785            } else { 
     786                return false; 
     787            } 
     788        } else { 
     789            return false; 
     790        } 
     791    } 
     792 
     793 
    422794    /** 
    423795     * Used to serve a captcha image to the browser 
    424796     * @param string $background_image The path to the background image to use 
    425      * <code>  
     797     * <code> 
    426798     * $img = new Securimage(); 
    427799     * $img->code_length = 6; 
    428800     * $img->num_lines   = 5; 
    429801     * $img->noise_level = 5; 
    430      *  
     802     * 
    431803     * $img->show(); // sends the image to browser 
    432804     * exit; 
     
    435807    public function show($background_image = '') 
    436808    { 
     809        set_error_handler(array(&$this, 'errorHandler')); 
     810 
    437811        if($background_image != '' && is_readable($background_image)) { 
    438812            $this->bgimg = $background_image; 
     
    441815        $this->doImage(); 
    442816    } 
    443      
     817 
    444818    /** 
    445819     * Check a submitted code against the stored value 
     
    461835        return $this->correct_code; 
    462836    } 
    463      
     837 
    464838    /** 
    465839     * Output a wav file of the captcha code to the browser 
    466      *  
     840     * 
    467841     * <code> 
    468842     * $img = new Securimage(); 
     
    473847    public function outputAudioFile() 
    474848    { 
    475         $ext = 'wav'; // force wav - mp3 is insecure 
    476          
    477         header("Content-Disposition: attachment; filename=\"securimage_audio.{$ext}\""); 
    478         header('Cache-Control: no-store, no-cache, must-revalidate'); 
    479         header('Expires: Sun, 1 Jan 2000 12:00:00 GMT'); 
    480         header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); 
    481         header('Content-type: audio/x-wav'); 
    482          
    483         $audio = $this->getAudibleCode($ext); 
    484  
    485         header('Content-Length: ' . strlen($audio)); 
    486  
    487         echo $audio; 
    488         exit; 
    489     } 
    490      
     849        set_error_handler(array(&$this, 'errorHandler')); 
     850 
     851        require_once dirname(__FILE__) . '/WavFile.php'; 
     852 
     853        try { 
     854            $audio = $this->getAudibleCode(); 
     855        } catch (Exception $ex) { 
     856            if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) { 
     857                fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n"); 
     858                fclose($fp); 
     859            } 
     860 
     861            $audio = $this->audioError(); 
     862        } 
     863 
     864        if ($this->canSendHeaders() || $this->send_headers == false) { 
     865            if ($this->send_headers) { 
     866                $uniq = md5(uniqid(microtime())); 
     867                header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.wav\""); 
     868                header('Cache-Control: no-store, no-cache, must-revalidate'); 
     869                header('Expires: Sun, 1 Jan 2000 12:00:00 GMT'); 
     870                header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); 
     871                header('Content-type: audio/x-wav'); 
     872 
     873                if (extension_loaded('zlib')) { 
     874                    ini_set('zlib.output_compression', true);  // compress output if supported by browser 
     875                } else { 
     876                    header('Content-Length: ' . strlen($audio)); 
     877                } 
     878            } 
     879 
     880            echo $audio; 
     881        } else { 
     882            echo '<hr /><strong>' 
     883                .'Failed to generate audio file, content has already been ' 
     884                .'output.<br />This is most likely due to misconfiguration or ' 
     885                .'a PHP error was sent to the browser.</strong>'; 
     886        } 
     887 
     888        restore_error_handler(); 
     889 
     890        if (!$this->no_exit) exit; 
     891    } 
     892 
     893    /** 
     894     * Return the code from the session or sqlite database if used.  If none exists yet, an empty string is returned 
     895     * 
     896     * @param $array bool   True to receive an array containing the code and properties 
     897     * @return array|string Array if $array = true, otherwise a string containing the code 
     898     */ 
     899    public function getCode($array = false, $returnExisting = false) 
     900    { 
     901        $code = ''; 
     902        $time = 0; 
     903        $disp = 'error'; 
     904 
     905        if ($returnExisting && strlen($this->code) > 0) { 
     906            if ($array) { 
     907                return array('code' => $this->code, 
     908                             'display' => $this->code_display, 
     909                             'code_display' => $this->code_display, 
     910                             'time' => 0); 
     911            } else { 
     912                return $this->code; 
     913            } 
     914        } 
     915 
     916        if ($this->no_session != true) { 
     917            if (isset($_SESSION['securimage_code_value'][$this->namespace]) && 
     918                    trim($_SESSION['securimage_code_value'][$this->namespace]) != '') { 
     919                if ($this->isCodeExpired( 
     920                        $_SESSION['securimage_code_ctime'][$this->namespace]) == false) { 
     921                    $code = $_SESSION['securimage_code_value'][$this->namespace]; 
     922                    $time = $_SESSION['securimage_code_ctime'][$this->namespace]; 
     923                    $disp = $_SESSION['securimage_code_disp'] [$this->namespace]; 
     924                } 
     925            } 
     926        } 
     927 
     928        if (empty($code) && $this->use_database) { 
     929            // no code in session - may mean user has cookies turned off 
     930            $this->openDatabase(); 
     931            $code = $this->getCodeFromDatabase(); 
     932        } else { /* no code stored in session or sqlite database, validation will fail */ } 
     933 
     934        if ($array == true) { 
     935            return array('code' => $code, 'ctime' => $time, 'display' => $disp); 
     936        } else { 
     937            return $code; 
     938        } 
     939    } 
     940 
    491941    /** 
    492942     * The main image drawing routing, responsible for constructing the entire image and serving it 
     
    499949            $imagecreate = 'imagecreate'; 
    500950        } 
    501          
     951 
    502952        $this->im     = $imagecreate($this->image_width, $this->image_height); 
    503953        $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale); 
    504          
     954 
    505955        $this->allocateColors(); 
    506956        imagepalettecopy($this->tmpimg, $this->im); 
     
    508958        $this->setBackground(); 
    509959 
    510         $this->createCode(); 
    511          
    512         $this->drawWord(); 
     960        $code = ''; 
     961 
     962        if ($this->getCaptchaId(false) !== null) { 
     963            // a captcha Id was supplied 
     964 
     965            // check to see if a display_value for the captcha image was set 
     966            if (is_string($this->display_value) && strlen($this->display_value) > 0) { 
     967                $this->code_display = $this->display_value; 
     968                $this->code         = ($this->case_sensitive) ? 
     969                                       $this->display_value   : 
     970                                       strtolower($this->display_value); 
     971                $code = $this->code; 
     972            } else if ($this->openDatabase()) { 
     973                // no display_value, check the database for existing captchaId 
     974                $code = $this->getCodeFromDatabase(); 
     975 
     976                // got back a result from the database with a valid code for captchaId 
     977                if (is_array($code)) { 
     978                    $this->code         = $code['code']; 
     979                    $this->code_display = $code['code_disp']; 
     980                    $code = $code['code']; 
     981                } 
     982            } 
     983        } 
     984 
     985        if ($code == '') { 
     986            // if the code was not set using display_value or was not found in 
     987            // the database, create a new code 
     988            $this->createCode(); 
     989        } 
    513990 
    514991        if ($this->noise_level > 0) { 
    515992            $this->drawNoise(); 
    516993        } 
    517          
     994 
     995        $this->drawWord(); 
     996 
    518997        if ($this->perturbation > 0 && is_readable($this->ttf_file)) { 
    519998            $this->distortedCopy(); 
     
    5301009        $this->output(); 
    5311010    } 
    532      
     1011 
    5331012    /** 
    5341013     * Allocate the colors to be used for the image 
     
    5411020                                              $this->image_bg_color->g, 
    5421021                                              $this->image_bg_color->b); 
    543          
     1022 
    5441023        $alpha = intval($this->text_transparency_percentage / 100 * 127); 
    545          
     1024 
    5461025        if ($this->use_transparent_text == true) { 
    5471026            $this->gdtextcolor = imagecolorallocatealpha($this->im, 
     
    5741053                                                          $this->noise_color->b); 
    5751054        } 
    576      
     1055 
    5771056        $this->gdsignaturecolor = imagecolorallocate($this->im, 
    5781057                                                     $this->signature_color->r, 
     
    5811060 
    5821061    } 
    583      
     1062 
    5841063    /** 
    5851064     * The the background color, or background image to be used 
     
    5941073                             $this->image_width * $this->iscale, $this->image_height * $this->iscale, 
    5951074                             $this->gdbgcolor); 
    596      
     1075 
    5971076        if ($this->bgimg == '') { 
    598             if ($this->background_directory != null &&  
     1077            if ($this->background_directory != null && 
    5991078                is_dir($this->background_directory) && 
    6001079                is_readable($this->background_directory)) 
     
    6061085            } 
    6071086        } 
    608          
     1087 
    6091088        if ($this->bgimg == '') { 
    6101089            return; 
     
    6121091 
    6131092        $dat = @getimagesize($this->bgimg); 
    614         if($dat == false) {  
     1093        if($dat == false) { 
    6151094            return; 
    6161095        } 
     
    6291108                         imagesx($newim), imagesy($newim)); 
    6301109    } 
    631      
     1110 
    6321111    /** 
    6331112     * Scan the directory for a background image to use 
     
    6451124 
    6461125            if (sizeof($images) > 0) { 
    647                 return rtrim($this->background_directory, '/') . '/' . $images[rand(0, sizeof($images)-1)]; 
     1126                return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)]; 
    6481127            } 
    6491128        } 
     
    6511130        return false; 
    6521131    } 
    653      
     1132 
    6541133    /** 
    6551134     * Generates the code or math problem and saves the value to the session 
    6561135     */ 
    657     protected function createCode() 
     1136    public function createCode() 
    6581137    { 
    6591138        $this->code = false; 
     
    6621141            case self::SI_CAPTCHA_MATHEMATIC: 
    6631142            { 
    664                 $signs = array('+', '-', 'x'); 
    665                 $left  = rand(1, 10); 
    666                 $right = rand(1, 5); 
    667                 $sign  = $signs[rand(0, 2)]; 
    668                  
    669                 switch($sign) { 
    670                     case 'x': $c = $left * $right; break; 
    671                     case '-': $c = $left - $right; break; 
    672                     default:  $c = $left + $right; break; 
    673                 } 
    674                  
     1143                do { 
     1144                    $signs = array('+', '-', 'x'); 
     1145                    $left  = mt_rand(1, 10); 
     1146                    $right = mt_rand(1, 5); 
     1147                    $sign  = $signs[mt_rand(0, 2)]; 
     1148 
     1149                    switch($sign) { 
     1150                        case 'x': $c = $left * $right; break; 
     1151                        case '-': $c = $left - $right; break; 
     1152                        default:  $c = $left + $right; break; 
     1153                    } 
     1154                } while ($c <= 0); // no negative #'s or 0 
     1155 
    6751156                $this->code         = $c; 
    6761157                $this->code_display = "$left $sign $right"; 
    6771158                break; 
    6781159            } 
    679              
     1160 
     1161            case self::SI_CAPTCHA_WORDS: 
     1162                $words = $this->readCodeFromFile(2); 
     1163                $this->code = implode(' ', $words); 
     1164                $this->code_display = $this->code; 
     1165                break; 
     1166 
    6801167            default: 
    6811168            { 
     
    6871174                    $this->code = $this->generateCode($this->code_length); 
    6881175                } 
    689                  
     1176 
    6901177                $this->code_display = $this->code; 
    6911178                $this->code         = ($this->case_sensitive) ? $this->code : strtolower($this->code); 
    6921179            } // default 
    6931180        } 
    694          
     1181 
    6951182        $this->saveData(); 
    6961183    } 
    697      
     1184 
    6981185    /** 
    6991186     * Draws the captcha code on the image 
     
    7031190        $width2  = $this->image_width * $this->iscale; 
    7041191        $height2 = $this->image_height * $this->iscale; 
    705           
     1192 
    7061193        if (!is_readable($this->ttf_file)) { 
    7071194            imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor); 
     
    7271214            } 
    7281215        } 
    729          
     1216 
    7301217        // DEBUG 
    7311218        //$this->im = $this->tmpimg; 
    7321219        //$this->output(); 
    733          
    734     } 
    735      
     1220 
     1221    } 
     1222 
    7361223    /** 
    7371224     * Copies the captcha image to the final image with distortion applied 
     
    7421229        // make array of poles AKA attractor points 
    7431230        for ($i = 0; $i < $numpoles; ++ $i) { 
    744             $px[$i]  = rand($this->image_width  * 0.2, $this->image_width  * 0.8); 
    745             $py[$i]  = rand($this->image_height * 0.2, $this->image_height * 0.8); 
    746             $rad[$i] = rand($this->image_height * 0.2, $this->image_height * 0.8); 
     1231            $px[$i]  = mt_rand($this->image_width  * 0.2, $this->image_width  * 0.8); 
     1232            $py[$i]  = mt_rand($this->image_height * 0.2, $this->image_height * 0.8); 
     1233            $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8); 
    7471234            $tmp     = ((- $this->frand()) * 0.15) - .15; 
    7481235            $amp[$i] = $this->perturbation * $tmp; 
    7491236        } 
    750          
     1237 
    7511238        $bgCol = imagecolorat($this->tmpimg, 0, 0); 
    7521239        $width2 = $this->iscale * $this->image_width; 
     
    7841271        } 
    7851272    } 
    786      
     1273 
    7871274    /** 
    7881275     * Draws distorted lines on the image 
     
    7931280            $x = $this->image_width * (1 + $line) / ($this->num_lines + 1); 
    7941281            $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines; 
    795             $y = rand($this->image_height * 0.1, $this->image_height * 0.9); 
    796              
     1282            $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9); 
     1283 
    7971284            $theta = ($this->frand() - 0.5) * M_PI * 0.7; 
    7981285            $w = $this->image_width; 
    799             $len = rand($w * 0.4, $w * 0.7); 
    800             $lwid = rand(0, 2); 
    801              
     1286            $len = mt_rand($w * 0.4, $w * 0.7); 
     1287            $lwid = mt_rand(0, 2); 
     1288 
    8021289            $k = $this->frand() * 0.6 + 0.2; 
    8031290            $k = $k * $k * 0.5; 
     
    8101297            $x0 = $x - 0.5 * $len * cos($theta); 
    8111298            $y0 = $y - 0.5 * $len * sin($theta); 
    812              
     1299 
    8131300            $ldx = round(- $dy * $lwid); 
    8141301            $ldy = round($dx * $lwid); 
    815              
     1302 
    8161303            for ($i = 0; $i < $n; ++ $i) { 
    8171304                $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi); 
     
    8211308        } 
    8221309    } 
    823      
     1310 
    8241311    /** 
    8251312     * Draws random noise on the image 
     
    8341321 
    8351322        $t0 = microtime(true); 
    836          
     1323 
    8371324        $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale 
    838          
     1325 
    8391326        $points = $this->image_width * $this->image_height * $this->iscale; 
    8401327        $height = $this->image_height * $this->iscale; 
    8411328        $width  = $this->image_width * $this->iscale; 
    8421329        for ($i = 0; $i < $noise_level; ++$i) { 
    843             $x = rand(10, $width); 
    844             $y = rand(10, $height); 
    845             $size = rand(7, 10); 
     1330            $x = mt_rand(10, $width); 
     1331            $y = mt_rand(10, $height); 
     1332            $size = mt_rand(7, 10); 
    8461333            if ($x - $size <= 0 && $y - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy 
    8471334            imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE); 
    8481335        } 
    849          
     1336 
    8501337        $t1 = microtime(true); 
    851          
     1338 
    8521339        $t = $t1 - $t0; 
    853          
     1340 
    8541341        /* 
    8551342        // DEBUG 
     
    8601347        */ 
    8611348    } 
    862      
    863         /** 
    864         * Print signature text on image 
    865         */ 
     1349 
     1350    /** 
     1351    * Print signature text on image 
     1352    */ 
    8661353    protected function addSignature() 
    8671354    { 
    868         if ($this->use_gd_font) { 
    869             imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $this->gdsignaturecolor); 
     1355        $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature); 
     1356        $textlen = $bbox[2] - $bbox[0]; 
     1357        $x = $this->image_width - $textlen - 5; 
     1358        $y = $this->image_height - 3; 
     1359 
     1360        imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature); 
     1361    } 
     1362 
     1363    /** 
     1364     * Sends the appropriate image and cache headers and outputs image to the browser 
     1365     */ 
     1366    protected function output() 
     1367    { 
     1368        if ($this->canSendHeaders() || $this->send_headers == false) { 
     1369            if ($this->send_headers) { 
     1370                // only send the content-type headers if no headers have been output 
     1371                // this will ease debugging on misconfigured servers where warnings 
     1372                // may have been output which break the image and prevent easily viewing 
     1373                // source to see the error. 
     1374                header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 
     1375                header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); 
     1376                header("Cache-Control: no-store, no-cache, must-revalidate"); 
     1377                header("Cache-Control: post-check=0, pre-check=0", false); 
     1378                header("Pragma: no-cache"); 
     1379            } 
     1380 
     1381            switch ($this->image_type) { 
     1382                case self::SI_IMAGE_JPEG: 
     1383                    if ($this->send_headers) header("Content-Type: image/jpeg"); 
     1384                    imagejpeg($this->im, null, 90); 
     1385                    break; 
     1386                case self::SI_IMAGE_GIF: 
     1387                    if ($this->send_headers) header("Content-Type: image/gif"); 
     1388                    imagegif($this->im); 
     1389                    break; 
     1390                default: 
     1391                    if ($this->send_headers) header("Content-Type: image/png"); 
     1392                    imagepng($this->im); 
     1393                    break; 
     1394            } 
    8701395        } else { 
    871               
    872             $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature); 
    873             $textlen = $bbox[2] - $bbox[0]; 
    874             $x = $this->image_width - $textlen - 5; 
    875             $y = $this->image_height - 3; 
    876               
    877             imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature); 
    878         } 
    879     } 
    880      
    881     /** 
    882      * Sends the appropriate image and cache headers and outputs image to the browser 
    883      */ 
    884     protected function output() 
    885     { 
    886         header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 
    887         header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); 
    888         header("Cache-Control: no-store, no-cache, must-revalidate"); 
    889         header("Cache-Control: post-check=0, pre-check=0", false); 
    890         header("Pragma: no-cache"); 
    891          
    892         switch ($this->image_type) { 
    893             case self::SI_IMAGE_JPEG: 
    894                 header("Content-Type: image/jpeg"); 
    895                 imagejpeg($this->im, null, 90); 
    896                 break; 
    897             case self::SI_IMAGE_GIF: 
    898                 header("Content-Type: image/gif"); 
    899                 imagegif($this->im); 
    900                 break; 
    901             default: 
    902                 header("Content-Type: image/png"); 
    903                 imagepng($this->im); 
    904                 break; 
    905         } 
    906          
     1396            echo '<hr /><strong>' 
     1397                .'Failed to generate captcha image, content has already been ' 
     1398                .'output.<br />This is most likely due to misconfiguration or ' 
     1399                .'a PHP error was sent to the browser.</strong>'; 
     1400        } 
     1401 
    9071402        imagedestroy($this->im); 
    908         exit(); 
    909     } 
    910      
     1403        restore_error_handler(); 
     1404 
     1405        if (!$this->no_exit) exit; 
     1406    } 
     1407 
    9111408    /** 
    9121409     * Gets the code and returns the binary audio file for the stored captcha code 
    913      * @param string $format WAV only 
    914      */ 
    915     protected function getAudibleCode($format = 'wav') 
    916     { 
    917         // override any format other than wav for now 
    918         // this is due to security issues with MP3 files 
    919         $format  = 'wav'; 
    920          
     1410     * 
     1411     * @return The audio representation of the captcha in Wav format 
     1412     */ 
     1413    protected function getAudibleCode() 
     1414    { 
    9211415        $letters = array(); 
    922         $code    = $this->getCode(); 
    923  
    924         if ($code == '') { 
    925             $this->createCode(); 
    926             $code = $this->getCode(); 
    927         } 
    928  
    929         for($i = 0; $i < strlen($code); ++$i) { 
    930             $letters[] = $code{$i}; 
    931         } 
    932          
    933         if ($format == 'mp3') { 
    934             return $this->generateMP3($letters); 
     1416        $code    = $this->getCode(true, true); 
     1417 
     1418        if ($code['code'] == '') { 
     1419            if (strlen($this->display_value) > 0) { 
     1420                $code = array('code' => $this->display_value, 'display' => $this->display_value); 
     1421            } else { 
     1422                $this->createCode(); 
     1423                $code = $this->getCode(true); 
     1424            } 
     1425        } 
     1426 
     1427        if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) { 
     1428            $math = true; 
     1429 
     1430            $left  = $eq[1]; 
     1431            $sign  = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]); 
     1432            $right = $eq[3]; 
     1433 
     1434            $letters = array($left, $sign, $right); 
    9351435        } else { 
     1436            $math = false; 
     1437 
     1438            $length = strlen($code['display']); 
     1439 
     1440            for($i = 0; $i < $length; ++$i) { 
     1441                $letter    = $code['display']{$i}; 
     1442                $letters[] = $letter; 
     1443            } 
     1444        } 
     1445 
     1446        try { 
    9361447            return $this->generateWAV($letters); 
     1448        } catch(Exception $ex) { 
     1449            throw $ex; 
    9371450        } 
    9381451    } 
     
    9411454     * Gets a captcha code from a wordlist 
    9421455     */ 
    943     protected function readCodeFromFile() 
    944     { 
    945         $fp = @fopen($this->wordlist_file, 'rb'); 
     1456    protected function readCodeFromFile($numWords = 1) 
     1457    { 
     1458        $fp = fopen($this->wordlist_file, 'rb'); 
    9461459        if (!$fp) return false; 
    9471460 
     
    9491462        if ($fsize < 128) return false; // too small of a list to be effective 
    9501463 
    951         fseek($fp, rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64 
    952         $data = fread($fp, 64); // read a chunk from our random position 
     1464        if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1; 
     1465 
     1466        $words = array(); 
     1467        $i = 0; 
     1468        do { 
     1469            fseek($fp, mt_rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64 
     1470            $data = fread($fp, 64); // read a chunk from our random position 
     1471            $data = preg_replace("/\r?\n/", "\n", $data); 
     1472 
     1473            $start = @strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position 
     1474            $end   = @strpos($data, "\n", $start);          // find end of word 
     1475 
     1476            if ($start === false) { 
     1477                // picked start position at end of file 
     1478                continue; 
     1479            } else if ($end === false) { 
     1480                $end = strlen($data); 
     1481            } 
     1482 
     1483            $word = strtolower(substr($data, $start, $end - $start)); // return a line of the file 
     1484            $words[] = $word; 
     1485        } while (++$i < $numWords); 
     1486 
    9531487        fclose($fp); 
    954         $data = preg_replace("/\r?\n/", "\n", $data); 
    955  
    956         $start = @strpos($data, "\n", rand(0, 56)) + 1; // random start position 
    957         $end   = @strpos($data, "\n", $start);          // find end of word 
    958          
    959         if ($start === false) { 
    960             return false; 
    961         } else if ($end === false) { 
    962             $end = strlen($data); 
    963         } 
    964  
    965         return strtolower(substr($data, $start, $end - $start)); // return a line of the file 
    966     } 
    967      
     1488 
     1489        if ($numWords < 2) { 
     1490            return $words[0]; 
     1491        } else { 
     1492            return $words; 
     1493        } 
     1494    } 
     1495 
    9681496    /** 
    9691497     * Generates a random captcha code from the set character set 
     
    9731501        $code = ''; 
    9741502 
    975         for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) { 
    976             $code .= $this->charset{rand(0, $cslen - 1)}; 
    977         } 
    978          
    979         //return 'testing';  // debug, set the code to given string 
    980          
     1503        if (function_exists('mb_strlen')) { 
     1504            for($i = 1, $cslen = mb_strlen($this->charset); $i <= $this->code_length; ++$i) { 
     1505                $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8'); 
     1506            } 
     1507        } else { 
     1508            for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) { 
     1509                $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1); 
     1510            } 
     1511        } 
     1512 
    9811513        return $code; 
    9821514    } 
    983      
     1515 
    9841516    /** 
    9851517     * Checks the entered code against the value stored in the session or sqlite database, handles case sensitivity 
     
    9881520    protected function validate() 
    9891521    { 
    990         $code = $this->getCode(); 
    991         // returns stored code, or an empty string if no stored code was found 
    992         // checks the session and sqlite database if enabled 
    993          
     1522        if (!is_string($this->code) || strlen($this->code) == 0) { 
     1523            $code = $this->getCode(); 
     1524            // returns stored code, or an empty string if no stored code was found 
     1525            // checks the session and database if enabled 
     1526        } else { 
     1527            $code = $this->code; 
     1528        } 
     1529 
    9941530        if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) { 
    9951531            // case sensitive was set from securimage_show.php but not in class 
     
    9971533            $this->case_sensitive = true; 
    9981534        } 
    999          
     1535 
    10001536        $code_entered = trim( (($this->case_sensitive) ? $this->code_entered 
    10011537                                                       : strtolower($this->code_entered)) 
    10021538                        ); 
    10031539        $this->correct_code = false; 
    1004          
     1540 
    10051541        if ($code != '') { 
     1542            if (strpos($code, ' ') !== false) { 
     1543                // for multi word captchas, remove more than once space from input 
     1544                $code_entered = preg_replace('/\s+/', ' ', $code_entered); 
     1545                $code_entered = strtolower($code_entered); 
     1546            } 
     1547 
    10061548            if ($code == $code_entered) { 
    10071549                $this->correct_code = true; 
    1008                 $_SESSION['securimage_code_value'][$this->namespace] = ''; 
    1009                 $_SESSION['securimage_code_ctime'][$this->namespace] = ''; 
     1550                if ($this->no_session != true) { 
     1551                    $_SESSION['securimage_code_value'][$this->namespace] = ''; 
     1552                    $_SESSION['securimage_code_ctime'][$this->namespace] = ''; 
     1553                } 
    10101554                $this->clearCodeFromDatabase(); 
    10111555            } 
    10121556        } 
    10131557    } 
    1014      
    1015     /** 
    1016      * Return the code from the session or sqlite database if used.  If none exists yet, an empty string is returned 
    1017      */ 
    1018     protected function getCode() 
     1558 
     1559    /** 
     1560     * Save data to session namespace and database if used 
     1561     */ 
     1562    protected function saveData() 
     1563    { 
     1564        if ($this->no_session != true) { 
     1565            if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) { 
     1566                // fix for migration from v2 - v3 
     1567                unset($_SESSION['securimage_code_value']); 
     1568                unset($_SESSION['securimage_code_ctime']); 
     1569            } 
     1570 
     1571            $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display; 
     1572            $_SESSION['securimage_code_value'][$this->namespace] = $this->code; 
     1573            $_SESSION['securimage_code_ctime'][$this->namespace] = time(); 
     1574        } 
     1575 
     1576        if ($this->use_database) { 
     1577            $this->saveCodeToDatabase(); 
     1578        } 
     1579    } 
     1580 
     1581    /** 
     1582     * Saves the code to the sqlite database 
     1583     */ 
     1584    protected function saveCodeToDatabase() 
     1585    { 
     1586        $success = false; 
     1587        $this->openDatabase(); 
     1588 
     1589        if ($this->use_database && $this->pdo_conn) { 
     1590            $id = $this->getCaptchaId(false); 
     1591            $ip = $_SERVER['REMOTE_ADDR']; 
     1592 
     1593            if (empty($id)) { 
     1594                $id = $ip; 
     1595            } 
     1596 
     1597            $time      = time(); 
     1598            $code      = $this->code; 
     1599            $code_disp = $this->code_display; 
     1600 
     1601            // This is somewhat expensive in PDO Sqlite3 (when there is something to delete) 
     1602            $this->clearCodeFromDatabase(); 
     1603 
     1604            $query = "INSERT INTO {$this->database_table} (" 
     1605                    ."id, code, code_display, namespace, created) " 
     1606                    ."VALUES(?, ?, ?, ?, ?)"; 
     1607 
     1608            $stmt    = $this->pdo_conn->prepare($query); 
     1609            $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time)); 
     1610 
     1611            if (!$success) { 
     1612                $err = $stmt->errorInfo(); 
     1613                trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING); 
     1614            } 
     1615        } 
     1616 
     1617        return $success !== false; 
     1618    } 
     1619 
     1620    /** 
     1621     * Open sqlite database 
     1622     */ 
     1623    protected function openDatabase() 
     1624    { 
     1625        $this->pdo_conn = false; 
     1626 
     1627        if ($this->use_database) { 
     1628            $pdo_extension = 'PDO_' . strtoupper($this->database_driver); 
     1629 
     1630            if (!extension_loaded($pdo_extension)) { 
     1631                trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING); 
     1632                return false; 
     1633            } 
     1634        } 
     1635 
     1636        if ($this->database_driver == self::SI_DRIVER_SQLITE3) { 
     1637            if (!file_exists($this->database_file)) { 
     1638                $fp = fopen($this->database_file, 'w+'); 
     1639                if (!$fp) { 
     1640                    $err = error_get_last(); 
     1641                    trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING); 
     1642                    return false; 
     1643                } 
     1644                fclose($fp); 
     1645                chmod($this->database_file, 0666); 
     1646            } else if (!is_writeable($this->database_file)) { 
     1647                trigger_error("Securimage does not have read/write access to database file '{$this->database_file}. Make sure permissions are 0666 and writeable by user '" . get_current_user() . "'", E_USER_WARNING); 
     1648                return false; 
     1649            } 
     1650        } 
     1651 
     1652        $dsn = $this->getDsn(); 
     1653 
     1654        try { 
     1655            $options        = array(); 
     1656            $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options); 
     1657        } catch (PDOException $pdoex) { 
     1658            trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING); 
     1659            return false; 
     1660        } 
     1661 
     1662        try { 
     1663            if (!$this->checkTablesExist()) { 
     1664                // create tables... 
     1665                $this->createDatabaseTables(); 
     1666            } 
     1667        } catch (Exception $ex) { 
     1668            trigger_error($ex->getMessage(), E_USER_WARNING); 
     1669            $this->pdo_conn = null; 
     1670            return false; 
     1671        } 
     1672 
     1673        if (mt_rand(0, 100) / 100.0 == 1.0) { 
     1674            $this->purgeOldCodesFromDatabase(); 
     1675        } 
     1676 
     1677        return $this->pdo_conn; 
     1678    } 
     1679 
     1680    protected function getDsn() 
     1681    { 
     1682        $dsn = sprintf('%s:', $this->database_driver); 
     1683 
     1684        switch($this->database_driver) { 
     1685            case self::SI_DRIVER_SQLITE3: 
     1686                $dsn .= $this->database_file; 
     1687                break; 
     1688 
     1689            case self::SI_DRIVER_MYSQL: 
     1690            case self::SI_DRIVER_PGSQL: 
     1691                $dsn .= sprintf('host=%s;dbname=%s', 
     1692                                $this->database_host, 
     1693                                $this->database_name); 
     1694                break; 
     1695 
     1696        } 
     1697 
     1698        return $dsn; 
     1699    } 
     1700 
     1701    protected function checkTablesExist() 
     1702    { 
     1703        $table = $this->pdo_conn->quote($this->database_table); 
     1704 
     1705        switch($this->database_driver) { 
     1706            case self::SI_DRIVER_SQLITE3: 
     1707                // query row count for sqlite, PRAGMA queries seem to return no 
     1708                // rowCount using PDO even if there are rows returned 
     1709                $query = "SELECT COUNT(id) FROM $table"; 
     1710                break; 
     1711 
     1712            case self::SI_DRIVER_MYSQL: 
     1713                $query = "SHOW TABLES LIKE $table"; 
     1714                break; 
     1715 
     1716            case self::SI_DRIVER_PGSQL: 
     1717                $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;"; 
     1718                break; 
     1719        } 
     1720 
     1721        $result = $this->pdo_conn->query($query); 
     1722 
     1723        if (!$result) { 
     1724            $err = $this->pdo_conn->errorInfo(); 
     1725 
     1726            if ($this->database_driver == self::SI_DRIVER_SQLITE3 && 
     1727                $err[1] === 1 && strpos($err[2], 'no such table') !== false) 
     1728            { 
     1729                return false; 
     1730            } 
     1731 
     1732            throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}"); 
     1733        } else if ($this->database_driver == self::SI_DRIVER_SQLITE3) { 
     1734            // successful here regardless of row count for sqlite 
     1735            return true; 
     1736        } else if ($result->rowCount() == 0) { 
     1737            return false; 
     1738        } else { 
     1739            return true; 
     1740        } 
     1741    } 
     1742 
     1743    protected function createDatabaseTables() 
     1744    { 
     1745        $queries = array(); 
     1746 
     1747        switch($this->database_driver) { 
     1748            case self::SI_DRIVER_SQLITE3: 
     1749                $queries[] = "CREATE TABLE \"{$this->database_table}\" ( 
     1750                                id VARCHAR(40), 
     1751                                namespace VARCHAR(32) NOT NULL, 
     1752                                code VARCHAR(32) NOT NULL, 
     1753                                code_display VARCHAR(32) NOT NULL, 
     1754                                created INTEGER NOT NULL, 
     1755                                PRIMARY KEY(id, namespace) 
     1756                              )"; 
     1757 
     1758                $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)"; 
     1759                break; 
     1760 
     1761            case self::SI_DRIVER_MYSQL: 
     1762                $queries[] = "CREATE TABLE `{$this->database_table}` ( 
     1763                                `id` VARCHAR(40) NOT NULL, 
     1764                                `namespace` VARCHAR(32) NOT NULL, 
     1765                                `code` VARCHAR(32) NOT NULL, 
     1766                                `code_display` VARCHAR(32) NOT NULL, 
     1767                                `created` INT NOT NULL, 
     1768                                PRIMARY KEY(id, namespace), 
     1769                                INDEX(created) 
     1770                              )"; 
     1771                break; 
     1772 
     1773            case self::SI_DRIVER_PGSQL: 
     1774                $queries[] = "CREATE TABLE {$this->database_table} ( 
     1775                                id character varying(40) NOT NULL, 
     1776                                namespace character varying(32) NOT NULL, 
     1777                                code character varying(32) NOT NULL, 
     1778                                code_display character varying(32) NOT NULL, 
     1779                                created integer NOT NULL, 
     1780                                CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace) 
     1781                              )"; 
     1782 
     1783                $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);"; 
     1784                break; 
     1785        } 
     1786 
     1787        $this->pdo_conn->beginTransaction(); 
     1788 
     1789        foreach($queries as $query) { 
     1790            $result = $this->pdo_conn->query($query); 
     1791 
     1792            if (!$result) { 
     1793                $err = $this->pdo_conn->errorInfo(); 
     1794                trigger_error("Failed to create table.  {$err[1]}: {$err[2]}", E_USER_WARNING); 
     1795                $this->pdo_conn->rollBack(); 
     1796                $this->pdo_conn = false; 
     1797                return false; 
     1798            } 
     1799        } 
     1800 
     1801        $this->pdo_conn->commit(); 
     1802 
     1803        return true; 
     1804    } 
     1805 
     1806    /** 
     1807     * Get a code from the sqlite database for ip address/captchaId. 
     1808     * 
     1809     * @return string|array Empty string if no code was found or has expired, 
     1810     * otherwise returns the stored captcha code.  If a captchaId is set, this 
     1811     * returns an array with indices "code" and "code_disp" 
     1812     */ 
     1813    protected function getCodeFromDatabase() 
    10191814    { 
    10201815        $code = ''; 
    1021          
    1022         if (isset($_SESSION['securimage_code_value'][$this->namespace]) && 
    1023          trim($_SESSION['securimage_code_value'][$this->namespace]) != '') { 
    1024             if ($this->isCodeExpired( 
    1025             $_SESSION['securimage_code_ctime'][$this->namespace]) == false) { 
    1026                 $code = $_SESSION['securimage_code_value'][$this->namespace]; 
    1027             } 
    1028         } else if ($this->use_sqlite_db == true && function_exists('sqlite_open')) { 
    1029             // no code in session - may mean user has cookies turned off 
    1030             $this->openDatabase(); 
    1031             $code = $this->getCodeFromDatabase(); 
    1032         } else { /* no code stored in session or sqlite database, validation will fail */ } 
    1033          
     1816 
     1817        if ($this->use_database == true && $this->pdo_conn) { 
     1818            if (Securimage::$_captchaId !== null) { 
     1819                $query  = "SELECT * FROM {$this->database_table} WHERE id = ?"; 
     1820                $stmt   = $this->pdo_conn->prepare($query); 
     1821                $result = $stmt->execute(array(Securimage::$_captchaId)); 
     1822            } else { 
     1823                $ip = $_SERVER['REMOTE_ADDR']; 
     1824                $ns = $this->namespace; 
     1825 
     1826                // ip is stored in id column when no captchaId 
     1827                $query  = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?"; 
     1828                $stmt   = $this->pdo_conn->prepare($query); 
     1829                $result = $stmt->execute(array($ip, $ns)); 
     1830            } 
     1831 
     1832            if (!$result) { 
     1833                $err = $this->pdo_conn->errorInfo(); 
     1834                trigger_error("Failed to select code from database.  {$err[0]}: {$err[1]}", E_USER_WARNING); 
     1835            } else { 
     1836                if ( ($row = $stmt->fetch()) !== false ) { 
     1837                    if (false == $this->isCodeExpired($row['created'])) { 
     1838                        if (Securimage::$_captchaId !== null) { 
     1839                            // return an array when using captchaId 
     1840                            $code = array('code'      => $row['code'], 
     1841                                          'code_disp' => $row['code_display']); 
     1842                        } else { 
     1843                            $code = $row['code']; 
     1844                        } 
     1845                    } 
     1846                } 
     1847            } 
     1848        } 
     1849 
    10341850        return $code; 
    10351851    } 
    1036      
    1037     /** 
    1038      * Save data to session namespace and database if used 
    1039      */ 
    1040     protected function saveData() 
    1041     { 
    1042         $_SESSION['securimage_code_value'][$this->namespace] = $this->code; 
    1043         $_SESSION['securimage_code_ctime'][$this->namespace] = time(); 
    1044          
    1045         $this->saveCodeToDatabase(); 
    1046     } 
    1047      
    1048     /** 
    1049      * Saves the code to the sqlite database 
    1050      */ 
    1051     protected function saveCodeToDatabase() 
    1052     { 
    1053         $success = false; 
    1054          
    1055         $this->openDatabase(); 
    1056          
    1057         if ($this->use_sqlite_db && $this->sqlite_handle !== false) { 
    1058             $ip      = $_SERVER['REMOTE_ADDR']; 
    1059             $time    = time(); 
    1060             $code    = $_SESSION['securimage_code_value'][$this->namespace]; // if cookies are disabled the session still exists at this point 
    1061             $success = sqlite_query($this->sqlite_handle, 
    1062                                     "INSERT OR REPLACE INTO codes(ip, code, namespace, created) 
    1063                                     VALUES('$ip', '$code', '{$this->namespace}', $time)"); 
    1064         } 
    1065          
    1066         return $success !== false; 
    1067     } 
    1068      
    1069     /** 
    1070      * Open sqlite database 
    1071      */ 
    1072     protected function openDatabase() 
    1073     { 
    1074         $this->sqlite_handle = false; 
    1075          
    1076         if ($this->use_sqlite_db && function_exists('sqlite_open')) { 
    1077             $this->sqlite_handle = sqlite_open($this->sqlite_database, 0666, $error); 
    1078              
    1079             if ($this->sqlite_handle !== false) { 
    1080                 $res = sqlite_query($this->sqlite_handle, "PRAGMA table_info(codes)"); 
    1081                 if (sqlite_num_rows($res) == 0) { 
    1082                     sqlite_query($this->sqlite_handle, "CREATE TABLE codes (ip VARCHAR(32) PRIMARY KEY, code VARCHAR(32) NOT NULL, namespace VARCHAR(32) NOT NULL, created INTEGER)"); 
    1083                 } 
    1084             } 
    1085              
    1086             return $this->sqlite_handle != false; 
    1087         } 
    1088          
    1089         return $this->sqlite_handle; 
    1090     } 
    1091      
    1092     /** 
    1093      * Get a code from the sqlite database for ip address 
    1094      */ 
    1095     protected function getCodeFromDatabase() 
    1096     { 
    1097         $code = ''; 
    1098  
    1099         if ($this->use_sqlite_db && $this->sqlite_handle !== false) { 
     1852 
     1853    /** 
     1854     * Remove an entered code from the database 
     1855     */ 
     1856    protected function clearCodeFromDatabase() 
     1857    { 
     1858        if ($this->pdo_conn) { 
    11001859            $ip = $_SERVER['REMOTE_ADDR']; 
    1101             $ns = sqlite_escape_string($this->namespace); 
    1102  
    1103             $res = sqlite_query($this->sqlite_handle, "SELECT * FROM codes WHERE ip = '$ip' AND namespace = '$ns'"); 
    1104             if ($res && sqlite_num_rows($res) > 0) { 
    1105                 $res = sqlite_fetch_array($res); 
    1106  
    1107                 if ($this->isCodeExpired($res['created']) == false) { 
    1108                     $code = $res['code']; 
    1109                 } 
    1110             } 
    1111         } 
    1112         return $code; 
    1113     } 
    1114      
    1115     /** 
    1116      * Remove an entered code from the database 
    1117      */ 
    1118     protected function clearCodeFromDatabase() 
    1119     { 
    1120         if (is_resource($this->sqlite_handle)) { 
    1121             $ip = $_SERVER['REMOTE_ADDR']; 
    1122             $ns = sqlite_escape_string($this->namespace); 
    1123              
    1124             sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE ip = '$ip' AND namespace = '$ns'"); 
    1125         } 
    1126     } 
    1127      
     1860            $ns = $this->pdo_conn->quote($this->namespace); 
     1861            $id = Securimage::$_captchaId; 
     1862 
     1863            if (empty($id)) { 
     1864                $id = $ip; // if no captchaId set, IP address is captchaId. 
     1865            } 
     1866 
     1867            $id = $this->pdo_conn->quote($id); 
     1868 
     1869            $query = sprintf("DELETE FROM %s WHERE id = %s AND namespace = %s", 
     1870                             $this->database_table, $id, $ns); 
     1871 
     1872            $result = $this->pdo_conn->query($query); 
     1873            if (!$result) { 
     1874                trigger_error("Failed to delete code from database.", E_USER_WARNING); 
     1875            } 
     1876        } 
     1877    } 
     1878 
    11281879    /** 
    11291880     * Deletes old codes from sqlite database 
     
    11311882    protected function purgeOldCodesFromDatabase() 
    11321883    { 
    1133         if ($this->use_sqlite_db && $this->sqlite_handle !== false) { 
     1884        if ($this->use_database && $this->pdo_conn) { 
    11341885            $now   = time(); 
    11351886            $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time; 
    1136              
    1137             sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE $now - created > $limit"); 
    1138         } 
    1139     } 
    1140      
     1887 
     1888            $query = sprintf("DELETE FROM %s WHERE %s - created > %s", 
     1889                             $this->database_table, 
     1890                             $this->pdo_conn->quote($now, PDO::PARAM_INT), 
     1891                             $this->pdo_conn->quote($limit, PDO::PARAM_INT)); 
     1892 
     1893            $result = $this->pdo_conn->query($query); 
     1894        } 
     1895    } 
     1896 
    11411897    /** 
    11421898     * Checks to see if the captcha code has expired and cannot be used 
     
    11461902    { 
    11471903        $expired = true; 
    1148          
     1904 
    11491905        if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) { 
    11501906            $expired = false; 
     
    11521908            $expired = false; 
    11531909        } 
    1154          
     1910 
    11551911        return $expired; 
    11561912    } 
    1157      
    1158     /** 
    1159      *  
    1160      * Generate an MP3 audio file of the captcha image 
    1161      *  
    1162      * @deprecated 3.0 
    1163      */ 
    1164     protected function generateMP3() 
    1165     { 
    1166         return false; 
    1167     } 
    1168      
     1913 
    11691914    /** 
    11701915     * Generate a wav file given the $letters in the code 
     
    11751920    protected function generateWAV($letters) 
    11761921    { 
    1177         $data_len       = 0; 
    1178         $files          = array(); 
    1179         $out_data       = ''; 
    1180         $out_channels   = 0; 
    1181         $out_samplert   = 0; 
    1182         $out_bpersample = 0; 
    1183         $numSamples     = 0; 
    1184         $removeChunks   = array('LIST', 'DISP', 'NOTE'); 
    1185  
    1186         for ($i = 0; $i < sizeof($letters); ++$i) { 
    1187             $letter   = $letters[$i]; 
    1188             $filename = $this->audio_path . strtoupper($letter) . '.wav'; 
    1189             $file     = array(); 
    1190             $data     = @file_get_contents($filename); 
    1191              
    1192             if ($data === false) { 
    1193                 // echo "Failed to read $filename"; 
    1194                 return $this->audioError(); 
    1195             } 
    1196  
    1197             $header = substr($data, 0, 36); 
    1198             $info   = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/' 
    1199                             .'VSubChunk1Size/vAudioFormat/vNumChannels/' 
    1200                             .'VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', 
    1201                              $header); 
    1202              
    1203             $dataPos        = strpos($data, 'data'); 
    1204             $out_channels   = $info['NumChannels']; 
    1205             $out_samplert   = $info['SampleRate']; 
    1206             $out_bpersample = $info['BitsPerSample']; 
    1207              
    1208             if ($dataPos === false) { 
    1209                 // wav file with no data? 
    1210                 // echo "Failed to find DATA segment in $filename"; 
    1211                 return $this->audioError(); 
    1212             } 
    1213              
    1214             if ($info['AudioFormat'] != 1) { 
    1215                 // only work with PCM audio 
    1216                 // echo "$filename was not PCM audio, only PCM is supported"; 
    1217                 return $this->audioError(); 
    1218             } 
    1219              
    1220             if ($info['SubChunk1Size'] != 16 && $info['SubChunk1Size'] != 18) { 
    1221                 // probably unsupported extension 
    1222                 // echo "Bad SubChunk1Size in $filename - Size was {$info['SubChunk1Size']}"; 
    1223                 return $this->audioError(); 
    1224             } 
    1225              
    1226             if ($info['SubChunk1Size'] > 16) { 
    1227                 $header .= substr($data, 36, $info['SubChunk1Size'] - 16); 
    1228             } 
    1229              
    1230             if ($i == 0) { 
    1231                 // create the final file's header, size will be adjusted later 
    1232                 $out_data = $header . 'data'; 
    1233             } 
    1234              
    1235             $removed = 0; 
    1236              
    1237             foreach($removeChunks as $chunk) { 
    1238                 $chunkPos = strpos($data, $chunk); 
    1239                 if ($chunkPos !== false) { 
    1240                     $listSize = unpack('VSize', substr($data, $chunkPos + 4, 4)); 
    1241                      
    1242                     $data = substr($data, 0, $chunkPos) . 
    1243                             substr($data, $chunkPos + 8 + $listSize['Size']); 
    1244                              
    1245                     $removed += $listSize['Size'] + 8; 
     1922        $wavCaptcha = new WavFile(); 
     1923        $first      = true;     // reading first wav file 
     1924 
     1925        foreach ($letters as $letter) { 
     1926            $letter = strtoupper($letter); 
     1927 
     1928            try { 
     1929                $l = new WavFile($this->audio_path . '/' . $letter . '.wav'); 
     1930 
     1931                if ($first) { 
     1932                    // set sample rate, bits/sample, and # of channels for file based on first letter 
     1933                    $wavCaptcha->setSampleRate($l->getSampleRate()) 
     1934                               ->setBitsPerSample($l->getBitsPerSample()) 
     1935                               ->setNumChannels($l->getNumChannels()); 
     1936                    $first = false; 
    12461937                } 
    1247             } 
    1248              
    1249             $dataSize    = unpack('VSubchunk2Size', substr($data, $dataPos + 4, 4)); 
    1250             $dataSize['Subchunk2Size'] -= $removed; 
    1251             $out_data   .= substr($data, $dataPos + 8, $dataSize['Subchunk2Size'] * ($out_bpersample / 8)); 
    1252             $numSamples += $dataSize['Subchunk2Size']; 
    1253         } 
    1254  
    1255         $filesize  = strlen($out_data); 
    1256         $chunkSize = $filesize - 8; 
    1257         $dataCSize = $numSamples; 
    1258          
    1259         $out_data = substr_replace($out_data, pack('V', $chunkSize), 4, 4); 
    1260         $out_data = substr_replace($out_data, pack('V', $numSamples), 40 + ($info['SubChunk1Size'] - 16), 4); 
    1261  
    1262         $this->scrambleAudioData($out_data, 'wav'); 
    1263          
    1264         return $out_data; 
    1265     } 
    1266      
    1267     /** 
    1268      * Randomizes the audio data to add noise and prevent binary recognition 
    1269      * @param string $data  The binary audio file data 
    1270      * @param string $format The format of the sound file (wav only) 
    1271      */ 
    1272     protected function scrambleAudioData(&$data, $format) 
    1273     { 
    1274         $start = strpos($data, 'data') + 4; // look for "data" indicator 
    1275         if ($start === false) $start = 44;  // if not found assume 44 byte header 
    1276           
    1277         $start  += rand(1, 4); // randomize starting offset 
    1278         $datalen = strlen($data) - $start; 
    1279         $step    = 1; 
    1280          
    1281         for ($i = $start; $i < $datalen; $i += $step) { 
    1282             $ch = ord($data{$i}); 
    1283             if ($ch == 0 || $ch == 255) continue; 
    1284              
    1285             if ($ch < 16 || $ch > 239) { 
    1286                 $ch += rand(-6, 6); 
    1287             } else { 
    1288                 $ch += rand(-12, 12); 
    1289             } 
    1290              
    1291             if ($ch < 0) $ch = 0; else if ($ch > 255) $ch = 255; 
    1292  
    1293             $data{$i} = chr($ch); 
    1294              
    1295             $step = rand(1,4); 
    1296         } 
    1297  
    1298         return $data; 
    1299     } 
    1300      
     1938 
     1939                // append letter to the captcha audio 
     1940                $wavCaptcha->appendWav($l); 
     1941 
     1942                // random length of silence between $audio_gap_min and $audio_gap_max 
     1943                if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) { 
     1944                    $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 ); 
     1945                } 
     1946            } catch (Exception $ex) { 
     1947                // failed to open file, or the wav file is broken or not supported 
     1948                // 2 wav files were not compatible, different # channels, bits/sample, or sample rate 
     1949                throw $ex; 
     1950            } 
     1951        } 
     1952 
     1953        /********* Set up audio filters *****************************/ 
     1954        $filters = array(); 
     1955 
     1956        if ($this->audio_use_noise == true) { 
     1957            // use background audio - find random file 
     1958            $noiseFile = $this->getRandomNoiseFile(); 
     1959 
     1960            if ($noiseFile !== false && is_readable($noiseFile)) { 
     1961                try { 
     1962                    $wavNoise = new WavFile($noiseFile, false); 
     1963                } catch(Exception $ex) { 
     1964                    throw $ex; 
     1965                } 
     1966 
     1967                // start at a random offset from the beginning of the wavfile 
     1968                // in order to add more randomness 
     1969                $randOffset = 0; 
     1970                if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) { 
     1971                    $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks()); 
     1972                    $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign()); 
     1973                } else { 
     1974                    $wavNoise->readWavData(); 
     1975                    $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1); 
     1976                } 
     1977 
     1978 
     1979                $mixOpts = array('wav'  => $wavNoise, 
     1980                                 'loop' => true, 
     1981                                 'blockOffset' => $randOffset); 
     1982 
     1983                $filters[WavFile::FILTER_MIX]       = $mixOpts; 
     1984                $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization; 
     1985            } 
     1986        } 
     1987 
     1988        if ($this->degrade_audio == true) { 
     1989            // add random noise. 
     1990            // any noise level below 95% is intensely distorted and not pleasant to the ear 
     1991            $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0; 
     1992        } 
     1993 
     1994        if (!empty($filters)) { 
     1995            $wavCaptcha->filter($filters);  // apply filters to captcha audio 
     1996        } 
     1997 
     1998        return $wavCaptcha->__toString(); 
     1999    } 
     2000 
     2001    public function getRandomNoiseFile() 
     2002    { 
     2003        $return = false; 
     2004 
     2005        if ( ($dh = opendir($this->audio_noise_path)) !== false ) { 
     2006            $list = array(); 
     2007 
     2008            while ( ($file = readdir($dh)) !== false ) { 
     2009                if ($file == '.' || $file == '..') continue; 
     2010                if (strtolower(substr($file, -4)) != '.wav') continue; 
     2011 
     2012                $list[] = $file; 
     2013            } 
     2014 
     2015            closedir($dh); 
     2016 
     2017            if (sizeof($list) > 0) { 
     2018                $file   = $list[array_rand($list, 1)]; 
     2019                $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file; 
     2020            } 
     2021        } 
     2022 
     2023        return $return; 
     2024    } 
     2025 
    13012026    /** 
    13022027     * Return a wav file saying there was an error generating file 
    1303      *  
     2028     * 
    13042029     * @return string The binary audio contents 
    13052030     */ 
    13062031    protected function audioError() 
    13072032    { 
    1308         return @file_get_contents(dirname(__FILE__) . '/audio/error.wav'); 
    1309     } 
    1310      
     2033        return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav'); 
     2034    } 
     2035 
     2036    /** 
     2037     * Checks to see if headers can be sent and if any error has been output to the browser 
     2038     * 
     2039     * @return bool true if headers haven't been sent and no output/errors will break audio/images, false if unsafe 
     2040     */ 
     2041    protected function canSendHeaders() 
     2042    { 
     2043        if (headers_sent()) { 
     2044            // output has been flushed and headers have already been sent 
     2045            return false; 
     2046        } else if (strlen((string)ob_get_contents()) > 0) { 
     2047            // headers haven't been sent, but there is data in the buffer that will break image and audio data 
     2048            return false; 
     2049        } 
     2050 
     2051        return true; 
     2052    } 
     2053 
     2054    /** 
     2055     * Return a random float between 0 and 0.9999 
     2056     * 
     2057     * @return float Random float between 0 and 0.9999 
     2058     */ 
    13112059    function frand() 
    13122060    { 
    1313         return 0.0001 * rand(0,9999); 
    1314     } 
    1315      
     2061        return 0.0001 * mt_rand(0,9999); 
     2062    } 
     2063 
    13162064    /** 
    13172065     * Convert an html color code to a Securimage_Color 
     
    13342082            return new Securimage_Color($default); 
    13352083        } 
     2084    } 
     2085 
     2086    /** 
     2087     * Error handler used when outputting captcha image or audio. 
     2088     * This error handler helps determine if any errors raised would 
     2089     * prevent captcha image or audio from displaying.  If they have 
     2090     * no effect on the output buffer or headers, true is returned so 
     2091     * the script can continue processing. 
     2092     * See https://github.com/dapphp/securimage/issues/15 
     2093     * 
     2094     * @param int $errno 
     2095     * @param string $errstr 
     2096     * @param string $errfile 
     2097     * @param int $errline 
     2098     * @param array $errcontext 
     2099     * @return boolean true if handled, false if PHP should handle 
     2100     */ 
     2101    public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) 
     2102    { 
     2103        // get the current error reporting level 
     2104        $level = error_reporting(); 
     2105 
     2106        // if error was supressed or $errno not set in current error level 
     2107        if ($level == 0 || ($level & $errno) == 0) { 
     2108            return true; 
     2109        } 
     2110 
     2111        return false; 
    13362112    } 
    13372113} 
     
    13602136     * $color = new Securimage_Color('#0080FF') or <br /> 
    13612137     * $color = new Securimage_Color(0, 128, 255) 
    1362      *  
     2138     * 
    13632139     * @param string $color 
    13642140     * @throws Exception 
     
    13672143    { 
    13682144        $args = func_get_args(); 
    1369          
     2145 
    13702146        if (sizeof($args) == 0) { 
    13712147            $this->r = 255; 
     
    13772153                $color = substr($color, 1); 
    13782154            } 
    1379              
     2155 
    13802156            if (strlen($color) != 3 && strlen($color) != 6) { 
    13812157                throw new InvalidArgumentException( 
     
    13832159                ); 
    13842160            } 
    1385              
     2161 
    13862162            $this->constructHTML($color); 
    13872163        } else if (sizeof($args) == 3) { 
     
    13932169        } 
    13942170    } 
    1395      
     2171 
    13962172    /** 
    13972173     * Construct from an rgb triplet 
     
    14082184        if ($blue < 0)    $blue  = 0; 
    14092185        if ($blue > 255)  $blue  = 255; 
    1410          
     2186 
    14112187        $this->r = $red; 
    14122188        $this->g = $green; 
    14132189        $this->b = $blue; 
    14142190    } 
    1415      
     2191 
    14162192    /** 
    14172193     * Construct from an html hex color code 
     
    14272203            $red   = substr($color, 0, 2); 
    14282204            $green = substr($color, 2, 2); 
    1429             $blue  = substr($color, 4, 2);  
    1430         } 
    1431          
     2205            $blue  = substr($color, 4, 2); 
     2206        } 
     2207 
    14322208        $this->r = hexdec($red); 
    14332209        $this->g = hexdec($green); 
     
    14352211    } 
    14362212} 
    1437 ?> 
  • extensions/CryptograPHP/securimage/securimage_preview.php

    r26041 r26554  
    1515  'height'          => (int)$_GET['height'], 
    1616  'perturbation'    => (float)$_GET['perturbation'], 
    17   'image_bg_color'  => $_GET['image_bg_color'], 
     17  'background'      => $_GET['background'], 
     18  'bg_color'        => $_GET['bg_color'], 
     19  'bg_image'        => $_GET['bg_image'], 
    1820  'code_length'     => (int)$_GET['code_length'], 
    1921  'text_color'      => $_GET['text_color'], 
     
    3840} 
    3941 
    40 foreach (array('image_bg_color','text_color','line_color','noise_color') as $color) 
     42foreach (array('bg_color','text_color','line_color','noise_color') as $color) 
    4143{ 
    4244  if ($temp_conf[$color] == 'random') $temp_conf[$color] = randomColor(); 
     
    5355$img->image_height    = $temp_conf['height']; 
    5456$img->perturbation    = $temp_conf['perturbation']; 
    55 $img->image_bg_color  = new Securimage_Color('#'.$temp_conf['image_bg_color']); 
    5657$img->text_color      = new Securimage_Color('#'.$temp_conf['text_color']);  
    5758$img->num_lines       = $temp_conf['num_lines']; 
     
    6162$img->code_length     = $temp_conf['code_length']; 
    6263 
    63 $img->show(); 
    64  
    65 ?> 
     64if ($temp_conf['background'] == 'image') 
     65{ 
     66  if ($temp_conf['bg_image'] == 'random') 
     67  { 
     68    $img->background_directory = realpath(CRYPTO_PATH . 'securimage/backgrounds/'); 
     69    $img->show(); 
     70  } 
     71  else 
     72  { 
     73    $img->show(realpath(CRYPTO_PATH . 'securimage/backgrounds/' . $temp_conf['bg_image'])); 
     74  } 
     75} 
     76else 
     77{ 
     78  $img->image_bg_color  = new Securimage_Color('#'.$temp_conf['bg_color']); 
     79  $img->show(); 
     80} 
  • extensions/CryptograPHP/securimage/securimage_show.php

    r19428 r26554  
    6060} 
    6161 
    62 foreach (array('image_bg_color','text_color','line_color','noise_color') as $color) 
     62foreach (array('bg_color','text_color','line_color','noise_color') as $color) 
    6363{ 
    6464  if ($conf['cryptographp'][$color] == 'random') $conf['cryptographp'][$color] = randomColor(); 
     
    7575$img->image_height    = $conf['cryptographp']['height']; 
    7676$img->perturbation    = $conf['cryptographp']['perturbation']; 
    77 $img->image_bg_color  = new Securimage_Color('#'.$conf['cryptographp']['image_bg_color']); 
    7877$img->text_color      = new Securimage_Color('#'.$conf['cryptographp']['text_color']);  
    7978$img->num_lines       = $conf['cryptographp']['num_lines']; 
     
    8382$img->code_length     = $conf['cryptographp']['code_length']; 
    8483 
    85 $img->show(); 
    86  
    87 ?> 
     84if ($conf['cryptographp']['background'] == 'image') 
     85{ 
     86  if ($conf['cryptographp']['bg_image'] == 'random') 
     87  { 
     88    $img->background_directory = realpath(CRYPTO_PATH . 'securimage/backgrounds/'); 
     89    $img->show(); 
     90  } 
     91  else 
     92  { 
     93    $img->show(realpath(CRYPTO_PATH . 'securimage/backgrounds/' . $conf['cryptographp']['bg_image'])); 
     94  } 
     95} 
     96else 
     97{ 
     98  $img->image_bg_color  = new Securimage_Color('#'.$conf['cryptographp']['bg_color']); 
     99  $img->show(); 
     100} 
  • extensions/CryptograPHP/template/admin.tpl

    r26041 r26554  
    99 
    1010{footer_script} 
     11var time = 0; 
     12 
    1113// colorpicker 
    1214$('.colorpicker-input') 
     
    1719    }, 
    1820    onChange: function(hsb, hex, rgb, el) {  
    19       $(el).val(hex);  
     21      $(el).val(hex).trigger('change'); 
    2022      changeColor(el, hex); 
    21       changePreview(); 
    22       setThemeCutom(); 
    2323    }, 
    2424    onBeforeShow: function () {  
     
    3333    changeColor(this, $(this).val()); 
    3434  }); 
     35   
     36   
     37$('.button').click(function() { 
     38  $(this).siblings('.button').removeClass('selected'); 
     39  $(this).addClass('selected'); 
     40  $('input[name='+ $(this).data('input') +']').val($(this).data('val')).trigger('change'); 
     41}); 
    3542 
    3643// change button 
    37 $('.button').click(function() { 
    38   $('.button').removeClass('selected'); 
    39   $(this).addClass('selected'); 
    40   $('input[name=button_color]').val($(this).attr('title')); 
    41   $('#reload').attr('src', '{$CRYPTO_PATH}template/refresh_'+ $(this).attr('title') +'.png'); 
     44$('input[name=button_color]').change(function() { 
     45  $('#reload').attr('src', '{$CRYPTO_PATH}template/refresh_'+ $(this).val() +'.png'); 
    4246}); 
    4347 
    4448// apply a preset 
    45 $('.preset').click(function() { 
    46   $('.preset').removeClass('selected'); 
    47   $(this).addClass('selected'); 
    48    
    49   var id = $(this).attr("title"); 
     49$('input[name=theme]').change(function() { 
     50  var id = $(this).val(); 
    5051   
    5152  for (key in presets[id]) { 
    52     $('input[name="'+ key +'"]').val([presets[id][key]]); 
     53    if ($('input[name="'+ key +'"]').attr('type') == 'radio') { 
     54      $('input[name="'+ key +'"][value="'+ presets[id][key] +'"]').prop('checked', true).trigger('change', false); 
     55    } 
     56    else { 
     57      $('input[name="'+ key +'"]').val(presets[id][key]).trigger('change', false); 
     58    } 
    5359  } 
    5460   
     
    5662    changeColor(this, $(this).val()); 
    5763  }); 
    58   $('input[name="theme"]').val($(this).attr('title')); 
     64 
    5965  changePreview(); 
     66}); 
     67 
     68// toggle background type 
     69$('input[name=background]').change(function() { 
     70  $('li[id^=background]').hide().filter('#background-'+$(this).val()).show(); 
    6071}); 
    6172 
     
    6677 
    6778// change theme to 'custom' if a parameter is changed 
    68 $('input.istheme').change(function() { 
    69   setThemeCutom(); 
     79$('input.istheme').change(function(e, p) { 
     80  if (p!==false) setThemeCustom(); 
    7081}); 
    7182 
    7283// update the preview 
    73 $('input.istheme, input.preview').change(function() { 
    74   changePreview(); 
     84$('input.preview').change(function(e, p) { 
     85  if (p!==false) changePreview(); 
    7586}); 
    7687$('#reload').click(function() { 
     
    8091// links for random color 
    8192$('a.random').click(function() { 
    82   $(this).prev('label').children('input').val('random'); 
     93  $(this).prev('label').children('input').val('random').trigger('change'); 
    8394  changeColor($(this).prev('label').children('input'), 'random'); 
    84   changePreview(); 
    85   setThemeCutom(); 
    8695}); 
    8796 
     
    93102}); 
    94103 
    95 function setThemeCutom() { 
    96   $('.preset').removeClass('selected'); 
     104function setThemeCustom() { 
     105  $('.button[data-input=theme]').removeClass('selected'); 
    97106  $('input[name=theme]').val('custom'); 
    98107} 
    99108 
    100109function changePreview() { 
     110  var now = (new Date()).getTime(); 
     111 
     112  if (now-time < 1000) { 
     113    return; 
     114  } 
     115  time = now; 
     116   
    101117  options = new Array(); 
    102118  str = ''; 
    103119   
    104   $('input[type="text"], input[type="radio"]:checked').each(function() { 
     120  $('input.preview:not([type=radio]), input[type=radio].preview:checked').each(function() { 
    105121    options[$(this).attr('name')] = $(this).val(); 
    106122  }); 
     
    113129 
    114130function changeColor(target, color) { 
    115   if (color == 'random') color = '808080'; 
     131  if (color == 'random') { 
     132    color = '808080'; 
     133  } 
    116134  if (parseInt(color, 16) > 16777215/2) { 
    117135    $(target).css('color', '#222'); 
     
    136154 
    137155{html_style} 
    138 {foreach from=$fonts item=font} 
     156{foreach from=$fonts item=path key=font} 
    139157@font-face {   
    140158  font-family: '{$font}';   
    141   src: url({$CRYPTO_PATH}securimage/fonts/{$font}.ttf) format("truetype");   
     159  src: url({$path}) format("truetype");   
    142160} 
    143161{/foreach} 
     
    193211    <li> 
    194212      <b>{'Button color'|translate}</b> 
    195       <a class="button {if $crypto.button_color == 'dark'}selected{/if}" title="dark"><img src="{$CRYPTO_PATH}template/refresh_dark.png" alt="dark"></a> 
    196       <a class="button {if $crypto.button_color == 'light'}selected{/if}" title="light"><img src="{$CRYPTO_PATH}template/refresh_light.png" alt="light"></a> 
     213      <a class="button {if $crypto.button_color == 'dark'}selected{/if}" data-val="dark" data-input="button_color"><img src="{$CRYPTO_PATH}template/refresh_dark.png" alt="dark"></a> 
     214      <a class="button {if $crypto.button_color == 'light'}selected{/if}" data-val="light" data-input="button_color"><img src="{$CRYPTO_PATH}template/refresh_light.png" alt="light"></a> 
    197215      <input type="hidden" name="button_color" value="{$crypto.button_color}"> 
    198216    </li> 
    199217    <li> 
    200218      <b>{'Captcha theme'|translate}</b> 
    201       {foreach from=$PRESETS key=preset item=params} 
    202       <a class="preset {if $crypto.theme == $preset}selected{/if}" title="{$preset}"><img src="{$CRYPTO_PATH}template/presets/{$preset}.png" alt="{$preset}"></a> 
    203       {/foreach} 
     219    {foreach from=$PRESETS key=preset item=params} 
     220      <a class="button {if $crypto.theme == $preset}selected{/if}" data-val="{$preset}" data-input="theme"><img src="{$CRYPTO_PATH}template/presets/{$preset}.png" alt="{$preset}"></a> 
     221    {/foreach} 
    204222      <input type="hidden" name="theme" value="{$crypto.theme}"> 
    205223      <a class="customize">{'Customize'|translate}</a> 
     
    213231      <li> 
    214232        <b>{'Perturbation'|translate}</b> 
    215         <label><input type="text" name="perturbation" value="{$crypto.perturbation}" class="istheme" size="6" maxlength="4"> {'range:'|translate} 0 - 1</label> 
    216       </li> 
    217       <li> 
     233        <label><input type="text" name="perturbation" value="{$crypto.perturbation}" class="istheme preview" size="6" maxlength="4"> {'range:'|translate} 0 - 1</label> 
     234      </li> 
     235      <li> 
     236        <b>{'Background'|translate}</b> 
     237        <label><input type="radio" name="background" class="istheme preview" value="color" {if $crypto.background == 'color'}checked="checked"{/if}> {'Color'|translate}</label> 
     238        <label><input type="radio" name="background" class="istheme preview" value="image" {if $crypto.background == 'image'}checked="checked"{/if}> {'Image'|translate}</label> 
     239      </li> 
     240      <li id="background-color" {if $crypto.background != 'color'}style="display:none;"{/if}> 
    218241        <b>{'Background color'|translate}</b> 
    219         <label><input type="text" name="image_bg_color" value="{$crypto.image_bg_color}" class="colorpicker-input istheme" size="6" maxlength="6"></label>  
     242        <label><input type="text" name="bg_color" value="{$crypto.bg_color}" class="colorpicker-input istheme preview" size="6" maxlength="6"></label>  
    220243        <a class="random" title="{'random'|translate}"><img src="{$CRYPTO_PATH}/template/arrow_switch.png"></a> 
    221244      </li> 
     245      <li id="background-image" {if $crypto.background != 'image'}style="display:none;"{/if}> 
     246        <b>{'Background image'|translate}</b> 
     247      {foreach from=$backgrounds item=path key=background} 
     248        <a class="button {if $crypto.bg_image == $background}selected{/if}" data-val="{$background}" data-input="bg_image"><img src="{$path}" alt="{$background}" style="width:120px;height:40px;"></a> 
     249      {/foreach} 
     250        <!-- <a class="button {if $crypto.bg_image == 'random'}selected{/if}" title="{'random'|translate}" data-val="random" data-input="bg_image"><img src="{$CRYPTO_PATH}/template/arrow_switch.png"></a> --> 
     251        <input type="hidden" name="bg_image" value="{$crypto.bg_image}" class="istheme preview"> 
     252      </li> 
    222253      <li> 
    223254        <b>{'Text color'|translate}</b> 
    224         <label><input type="text" name="text_color" value="{$crypto.text_color}" class="colorpicker-input istheme" size="6" maxlength="6"></label>  
     255        <label><input type="text" name="text_color" value="{$crypto.text_color}" class="colorpicker-input istheme preview" size="6" maxlength="6"></label>  
    225256        <a class="random" title="{'random'|translate}"><img src="{$CRYPTO_PATH}/template/arrow_switch.png"></a> 
    226257      </li> 
    227258      <li> 
    228259        <b>{'Lines density'|translate}</b> 
    229         <label><input type="text" name="num_lines" value="{$crypto.num_lines}" class="istheme" size="6" maxlength="4"> {'range:'|translate} 0 - 10</label> 
     260        <label><input type="text" name="num_lines" value="{$crypto.num_lines}" class="istheme preview" size="6" maxlength="4"> {'range:'|translate} 0 - 10</label> 
    230261      </li> 
    231262      <li> 
    232263        <b>{'Lines color'|translate}</b> 
    233         <label><input type="text" name="line_color" value="{$crypto.line_color}" class="colorpicker-input istheme" size="6" maxlength="6"></label>  
     264        <label><input type="text" name="line_color" value="{$crypto.line_color}" class="colorpicker-input istheme preview" size="6" maxlength="6"></label>  
    234265        <a class="random" title="{'random'|translate}"><img src="{$CRYPTO_PATH}/template/arrow_switch.png"></a> 
    235266      </li> 
    236267      <li> 
    237268        <b>{'Noise level'|translate}</b> 
    238         <label><input type="text" name="noise_level" value="{$crypto.noise_level}" class="istheme" size="6" maxlength="4"> {'range:'|translate} 0 - 10</label> 
     269        <label><input type="text" name="noise_level" value="{$crypto.noise_level}" class="istheme preview" size="6" maxlength="4"> {'range:'|translate} 0 - 10</label> 
    239270      </li> 
    240271      <li> 
    241272        <b>{'Noise color'|translate}</b> 
    242         <label><input type="text" name="noise_color" value="{$crypto.noise_color}" class="colorpicker-input istheme" size="6" maxlength="6"></label>  
     273        <label><input type="text" name="noise_color" value="{$crypto.noise_color}" class="colorpicker-input istheme preview" size="6" maxlength="6"></label>  
    243274        <a class="random" title="{'random'|translate}"><img src="{$CRYPTO_PATH}/template/arrow_switch.png"></a> 
    244275      </li> 
    245276      <li> 
    246277        <b>{'Font'|translate}</b> 
    247         {foreach from=$fonts item=font} 
    248         <label style="font-family:{$font};" title="{$font}"><input type="radio" name="ttf_file" value="{$font}" {if $crypto.ttf_file == $font}checked="checked"{/if} class="istheme"> {$font}</label> 
    249         {/foreach} 
     278      {foreach from=$fonts item=path key=font} 
     279        <label style="font-family:{$font};" title="{$font}"><input type="radio" name="ttf_file" value="{$font}" {if $crypto.ttf_file == $font}checked="checked"{/if} class="istheme preview"> {$font}</label> 
     280      {/foreach} 
    250281      </li> 
    251282    </ul> 
  • extensions/CryptograPHP/template/style.css

    r23209 r26554  
    1 .preset img, .button img { 
     1.button img { 
    22  margin:1px; 
    33  padding:3px; 
    44  border:1px solid #999; 
    55} 
    6 .preset.selected img, .button.selected img { 
     6.button.selected img { 
    77  border-color:#f70; 
    88} 
Note: See TracChangeset for help on using the changeset viewer.