PageRenderTime 36ms CodeModel.GetById 3ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

/securimage.php

http://github.com/dapphp/securimage
PHP | 3770 lines | 2111 code | 407 blank | 1252 comment | 377 complexity | a8b63fa6d44bd6018ffc116ec7b751c5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3// error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging
   4
   5/**
   6 * Project:  Securimage: A PHP class dealing with CAPTCHA images, audio, and validation
   7 * File:     securimage.php
   8 *
   9 * Copyright (c) 2018, Drew Phillips
  10 * All rights reserved.
  11 *
  12 * Redistribution and use in source and binary forms, with or without modification,
  13 * are permitted provided that the following conditions are met:
  14 *
  15 *  - Redistributions of source code must retain the above copyright notice,
  16 *    this list of conditions and the following disclaimer.
  17 *  - Redistributions in binary form must reproduce the above copyright notice,
  18 *    this list of conditions and the following disclaimer in the documentation
  19 *    and/or other materials provided with the distribution.
  20 *
  21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31 * POSSIBILITY OF SUCH DAMAGE.
  32 *
  33 * Any modifications to the library should be indicated clearly in the source code
  34 * to inform users that the changes are not a part of the original software.
  35 *
  36 * @link https://www.phpcaptcha.org Securimage Homepage
  37 * @link https://www.phpcaptcha.org/latest.zip Download Latest Version
  38 * @link https://github.com/dapphp/securimage GitHub page
  39 * @link https://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
  40 * @copyright 2018 Drew Phillips
  41 * @author Drew Phillips <drew@drew-phillips.com>
  42 * @version 3.6.7 (March 2018)
  43 * @package Securimage
  44 *
  45 */
  46
  47/**
  48
  49 ChangeLog
  50 3.6.7
  51 - Merge changes from 4.0.1-nextgen
  52 - Increase captcha difficulty
  53 - Add setting "use_text_angles". Enable to select a random angle and step value and draw each character at an angle in a step like fashion
  54 - Add setting "use_random_spaces". Enable to insert 1-3 spaces between a random group of letters some of the time
  55 - Add setting "use_random_baseline". Enable to draw letters at a random height instead of centered.  Each character's baseline is a step up or down from the previous (not totally random)
  56 - Add setting "use_random_boxes". Enable to draw a bounding box around one or more characters at random
  57 - Improve performance of captcha generation when using distortion (perturbation) and noise (noise_level)
  58 - Enable image anti-aliasing
  59 - Make all text functions multibyte safe when using UTF-8 or other encodings for charsets and wordlists (using mbstring)
  60
  61 3.6.6
  62 - Not critical: Fix potential HTML injection in example form via HTTP_USER_AGENT (CVE-2017-14077)
  63
  64 3.6.5
  65 - Fix regex in replaceElements in securimage.js
  66 - Update examples
  67 - Exclude certain examples from Git autogenerated archives
  68
  69 3.6.4
  70 - Fix XSS vulnerability in example_form.ajax.php (Discovered by RedTeam. advisory rt-sa-2016-002)
  71 - Update example_form.ajax.php to use Securimage::getCaptchaHtml()
  72
  73 3.6.3
  74 - Add support for multibyte wordlist files
  75 - Fix code generation issues with UTF-8 charsets
  76 - Add parameter to getCaptchaHtml() method to control display components of captcha HTML
  77 - Fix database audio storage issue with multiple namespaces
  78
  79 3.6.2
  80 - Support HTTP range requests with audio playback (iOS requirement)
  81 - Add optional config.inc.php for storing global configuration settings
  82
  83 3.6.1
  84 - Fix copyElement bug in securimage.js for IE Flash fallback
  85
  86 3.6
  87 - Implement CAPTCHA audio using HTML5 <audio> with optional Flash fallback
  88 - Support MP3 audio using LAME MP3 Encoder (Internet Explorer 9+ does not support WAV format in <audio> tags)
  89 - Add getCaptchaHtml() options to support full framework integration (ruifil)
  90
  91 3.5.4
  92 - Fix email validation code in example form files
  93 - Fix backslashes in getCaptchaHtml for img attribute on Windows systems
  94
  95 3.5.3
  96 - Add options for audio button to getCaptchaHtml(), fix urlencoding of flash parameters that was breaking button
  97
  98 3.5.2
  99
 100 - Add Securimage::getCaptchaHtml() for getting automatically generated captcha html code
 101 - Option for using SoX to add effects to captcha audio to make identification by neural networks more difficult
 102 - Add setNamespace() method
 103 - Add getTimeToSolve() method
 104 - Add session_status() check so session still starts if one had previously been opened and closed
 105 - Add .htaccess file to audio directory to deny access; update audio files
 106 - Option to skip checking of database tables during connection
 107 - Add composer.json to package, submit to packagist
 108 - Add font_ratio variable to determine size of font (github.com/wilkor)
 109 - Add hint if sqlite3 database is not writeable.  Improve database error handling, add example database options to securimage_play.php
 110 - Fixed issue regarding database storage and math captcha breaking audio output (github.com/SoftwareAndOutsourcing)
 111
 112 3.5.1
 113 - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>)
 114
 115 3.5
 116 - Release new version
 117 - MB string support for charlist
 118 - Modify audio file path to use language directories
 119 - Changed default captcha appearance
 120
 121 3.2RC4
 122 - Add MySQL, PostgreSQL, and SQLite3 support for database storage
 123 - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions
 124 - Add new captcha type that displays 2 dictionary words on one image
 125 - Update examples
 126
 127 3.2RC3
 128 - Fix canSendHeaders() check which was breaking if a PHP startup error was issued
 129
 130 3.2RC2
 131 - Add error handler (https://github.com/dapphp/securimage/issues/15)
 132 - Fix flash examples to use the correct value name for audio parameter
 133
 134 3.2RC1
 135 - New audio captcha code.  Faster, fully dynamic audio, full WAV support
 136   (Paul Voegler, Drew Phillips) <http://voegler.eu/pub/audio>
 137 - New Flash audio streaming button.  User defined image and size supported
 138 - Additional options for customizing captcha (noise_level, send_headers,
 139   no_exit, no_session, display_value
 140 - Add captcha ID support.  Uses sqlite and unique captcha IDs to track captchas,
 141   no session used
 142 - Add static methods for creating and validating captcha by ID
 143 - Automatic clearing of old codes from SQLite database
 144
 145 3.0.3Beta
 146 - Add improved mixing function to WavFile class (Paul Voegler)
 147 - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips)
 148 - Add option to use random file as background noise in captcha audio
 149 - Add new securimage options for audio files
 150
 151 3.0.2Beta
 152 - Fix issue with session variables when upgrading from 2.0 - 3.0
 153 - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work
 154
 155 3.0.1
 156 - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on
 157
 158 3.0
 159 - Rewrite class using PHP5 OOP
 160 - Remove support for GD fonts, require FreeType
 161 - Remove support for multi-color codes
 162 - Add option to make codes case-sensitive
 163 - Add namespaces to support multiple captchas on a single page or page specific captchas
 164 - Add option to show simple math problems instead of codes
 165 - Remove support for mp3 files due to vulnerability in decoding mp3 audio files
 166 - Create new flash file to stream wav files instead of mp3
 167 - Changed to BSD license
 168
 169 2.0.2
 170 - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
 171
 172 2.0.1
 173 - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
 174 - Add fallback to gd fonts if ttf support is not enabled or font file not found (Mike Challis http://www.642weather.com/weather/scripts.php)
 175 - Check for previous definition of image type constants (Mike Challis)
 176 - Fix mime type settings for audio output
 177 - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function
 178 - Ability to let codes expire after a given length of time
 179 - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis)
 180
 181 2.0.0
 182 - Add mathematical distortion to characters (using code from HKCaptcha)
 183 - Improved session support
 184 - Added Securimage_Color class for easier color definitions
 185 - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
 186 - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
 187 - Audio output is mp3 format by default
 188 - Change font to AlteHaasGrotesk by yann le coroller
 189 - Some code cleanup
 190
 191 1.0.4 (unreleased)
 192 - Ability to output audible codes in mp3 format to stream from flash
 193
 194 1.0.3.1
 195 - Error reading from wordlist in some cases caused words to be cut off 1 letter short
 196
 197 1.0.3
 198 - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
 199
 200 1.0.2
 201 - Audible CAPTCHA Code wav files
 202 - Create codes from a word list instead of random strings
 203
 204 1.0
 205 - Added the ability to use a selected character set, rather than a-z0-9 only.
 206 - Added the multi-color text option to use different colors for each letter.
 207 - Switched to automatic session handling instead of using files for code storage
 208 - Added GD Font support if ttf support is not available.  Can use internal GD fonts or load new ones.
 209 - Added the ability to set line thickness
 210 - Added option for drawing arced lines over letters
 211 - Added ability to choose image type for output
 212
 213 */
 214
 215
 216/**
 217 * Securimage CAPTCHA Class.
 218 *
 219 * A class for creating and validating secure CAPTCHA images and audio.
 220 *
 221 * The class contains many options regarding appearance, security, storage of
 222 * captcha data and image/audio generation options.
 223 *
 224 * @package    Securimage
 225 * @subpackage classes
 226 * @author     Drew Phillips <drew@drew-phillips.com>
 227 *
 228 */
 229class Securimage
 230{
 231    // All of the public variables below are securimage options
 232    // They can be passed as an array to the Securimage constructor, set below,
 233    // or set from securimage_show.php and securimage_play.php
 234
 235    /**
 236     * Constant for rendering captcha as a JPEG image
 237     * @var int
 238     */
 239    const SI_IMAGE_JPEG = 1;
 240
 241    /**
 242     * Constant for rendering captcha as a PNG image (default)
 243     * @var int
 244     */
 245
 246    const SI_IMAGE_PNG  = 2;
 247    /**
 248     * Constant for rendering captcha as a GIF image
 249     * @var int
 250     */
 251    const SI_IMAGE_GIF  = 3;
 252
 253    /**
 254     * Constant for generating a normal alphanumeric captcha based on the
 255     * character set
 256     *
 257     * @see Securimage::$charset charset property
 258     * @var int
 259     */
 260    const SI_CAPTCHA_STRING     = 0;
 261
 262    /**
 263     * Constant for generating a captcha consisting of a simple math problem
 264     *
 265     * @var int
 266     */
 267    const SI_CAPTCHA_MATHEMATIC = 1;
 268
 269    /**
 270     * Constant for generating a word based captcha using 2 words from a list
 271     *
 272     * @var int
 273     */
 274    const SI_CAPTCHA_WORDS      = 2;
 275
 276    /**
 277     * MySQL option identifier for database storage option
 278     *
 279     * @var string
 280     */
 281    const SI_DRIVER_MYSQL   = 'mysql';
 282
 283    /**
 284     * PostgreSQL option identifier for database storage option
 285     *
 286     * @var string
 287     */
 288    const SI_DRIVER_PGSQL   = 'pgsql';
 289
 290    /**
 291     * SQLite option identifier for database storage option
 292     *
 293     * @var string
 294     */
 295    const SI_DRIVER_SQLITE3 = 'sqlite';
 296
 297    /**
 298     * getCaptchaHtml() display constant for HTML Captcha Image
 299     *
 300     * @var integer
 301     */
 302    const HTML_IMG   = 1;
 303
 304    /**
 305     * getCaptchaHtml() display constant for HTML5 Audio code
 306     *
 307     * @var integer
 308     */
 309    const HTML_AUDIO = 2;
 310
 311    /**
 312     * getCaptchaHtml() display constant for Captcha Input text box
 313     *
 314     * @var integer
 315     */
 316    const HTML_INPUT = 4;
 317
 318    /**
 319     * getCaptchaHtml() display constant for Captcha Text HTML label
 320     *
 321     * @var integer
 322     */
 323    const HTML_INPUT_LABEL = 8;
 324
 325    /**
 326     * getCaptchaHtml() display constant for HTML Refresh button
 327     *
 328     * @var integer
 329     */
 330    const HTML_ICON_REFRESH = 16;
 331
 332    /**
 333     * getCaptchaHtml() display constant for all HTML elements (default)
 334     *
 335     * @var integer
 336     */
 337    const HTML_ALL = 0xffffffff;
 338
 339    /*%*********************************************************************%*/
 340    // Properties
 341
 342    /**
 343     * The width of the captcha image
 344     * @var int
 345     */
 346    public $image_width = 215;
 347
 348    /**
 349     * The height of the captcha image
 350     * @var int
 351     */
 352    public $image_height = 80;
 353
 354    /**
 355     * Font size is calculated by image height and this ratio.  Leave blank for
 356     * default ratio of 0.4.
 357     *
 358     * Valid range: 0.1 - 0.99.
 359     *
 360     * Depending on image_width, values > 0.6 are probably too large and
 361     * values < 0.3 are too small.
 362     *
 363     * @var float
 364     */
 365    public $font_ratio;
 366
 367    /**
 368     * The type of the image, default = png
 369     *
 370     * @see Securimage::SI_IMAGE_PNG SI_IMAGE_PNG
 371     * @see Securimage::SI_IMAGE_JPEG SI_IMAGE_JPEG
 372     * @see Securimage::SI_IMAGE_GIF SI_IMAGE_GIF
 373     * @var int
 374     */
 375    public $image_type   = self::SI_IMAGE_PNG;
 376
 377    /**
 378     * The background color of the captcha
 379     * @var Securimage_Color|string
 380     */
 381    public $image_bg_color = '#ffffff';
 382
 383    /**
 384     * The color of the captcha text
 385     * @var Securimage_Color|string
 386     */
 387    public $text_color     = '#707070';
 388
 389    /**
 390     * The color of the lines over the captcha
 391     * @var Securimage_Color|string
 392     */
 393    public $line_color     = '#707070';
 394
 395    /**
 396     * The color of the noise that is drawn
 397     * @var Securimage_Color|string
 398     */
 399    public $noise_color    = '#707070';
 400
 401    /**
 402     * How transparent to make the text.
 403     *
 404     * 0 = completely opaque, 100 = invisible
 405     *
 406     * @var int
 407     */
 408    public $text_transparency_percentage = 20;
 409
 410    /**
 411     * Whether or not to draw the text transparently.
 412     *
 413     * true = use transparency, false = no transparency
 414     *
 415     * @var bool
 416     */
 417    public $use_transparent_text         = true;
 418
 419    /**
 420     * The length of the captcha code
 421     * @var int
 422     */
 423    public $code_length    = 6;
 424
 425    /**
 426     * Display random spaces in the captcha text on the image
 427     *
 428     * @var bool true to insert random spacing between groups of letters
 429     */
 430    public $use_random_spaces  = false;
 431
 432    /**
 433     * Draw each character at an angle with random starting angle and increase/decrease per character
 434     * @var bool true to use random angles, false to draw each character normally
 435     */
 436    public $use_text_angles = false;
 437
 438    /**
 439     * Instead of centering text vertically in the image, the baseline of each character is
 440     * randomized in such a way that the next character is drawn slightly higher or lower than
 441     * the previous in a step-like fashion.
 442     *
 443     * @var bool true to use random baselines, false to center text in image
 444     */
 445    public $use_random_baseline = false;
 446
 447    /**
 448     * Draw a bounding box around some characters at random.  20% of the time, random boxes
 449     * may be drawn around 0 or more characters on the image.
 450     *
 451     * @var bool  true to randomly draw boxes around letters, false not to
 452     */
 453    public $use_random_boxes = false;
 454
 455    /**
 456     * Whether the captcha should be case sensitive or not.
 457     *
 458     * Not recommended, use only for maximum protection.
 459     *
 460     * @var bool
 461     */
 462    public $case_sensitive = false;
 463
 464    /**
 465     * The character set to use for generating the captcha code
 466     * @var string
 467     */
 468    public $charset        = 'abcdefghijkmnopqrstuvwxzyABCDEFGHJKLMNPQRSTUVWXZY0123456789';
 469
 470    /**
 471     * How long in seconds a captcha remains valid, after this time it will be
 472     * considered incorrect.
 473     *
 474     * @var int
 475     */
 476    public $expiry_time    = 900;
 477
 478    /**
 479     * The session name securimage should use.
 480     *
 481     * Only use if your application uses a custom session name (e.g. Joomla).
 482     * It is recommended to set this value here so it is used by all securimage
 483     * scripts (i.e. securimage_show.php)
 484     *
 485     * @var string
 486     */
 487    public $session_name   = null;
 488
 489    /**
 490     * true to use the wordlist file, false to generate random captcha codes
 491     * @var bool
 492     */
 493    public $use_wordlist   = false;
 494
 495    /**
 496     * The level of distortion.
 497     *
 498     * 0.75 = normal, 1.0 = very high distortion
 499     *
 500     * @var double
 501     */
 502    public $perturbation = 0.85;
 503
 504    /**
 505     * How many lines to draw over the captcha code to increase security
 506     * @var int
 507     */
 508    public $num_lines    = 5;
 509
 510    /**
 511     * The level of noise (random dots) to place on the image, 0-10
 512     * @var int
 513     */
 514    public $noise_level  = 2;
 515
 516    /**
 517     * The signature text to draw on the bottom corner of the image
 518     * @var string
 519     */
 520    public $image_signature = '';
 521
 522    /**
 523     * The color of the signature text
 524     * @var Securimage_Color|string
 525     */
 526    public $signature_color = '#707070';
 527
 528    /**
 529     * The path to the ttf font file to use for the signature text.
 530     * Defaults to $ttf_file (AHGBold.ttf)
 531     *
 532     * @see Securimage::$ttf_file
 533     * @var string
 534     */
 535    public $signature_font;
 536
 537    /**
 538     * No longer used.
 539     *
 540     * Use an SQLite database to store data (for users that do not support cookies)
 541     *
 542     * @var bool
 543     * @see Securimage::$database_driver database_driver property
 544     * @deprecated 3.2RC4
 545     */
 546    public $use_sqlite_db = false;
 547
 548    /**
 549     * Use a database backend for code storage.
 550     * Provides a fallback to users with cookies disabled.
 551     * Required when using captcha IDs.
 552     *
 553     * @see Securimage::$database_driver
 554     * @var bool
 555     */
 556    public $use_database = false;
 557
 558    /**
 559     * Whether or not to skip checking if Securimage tables exist when using a
 560     * database.
 561     *
 562     * Turn this to true once database functionality is working to improve
 563     * performance.
 564     *
 565     * @var bool true to not check if captcha_codes tables are set up, false
 566     * to check (and create if necessary)
 567     */
 568    public $skip_table_check = false;
 569
 570    /**
 571     * Database driver to use for database support.
 572     * Allowable values: *mysql*, *pgsql*, *sqlite*.
 573     * Default: sqlite
 574     *
 575     * @var string
 576     */
 577    public $database_driver = self::SI_DRIVER_SQLITE3;
 578
 579    /**
 580     * Database host to connect to when using mysql or postgres
 581     *
 582     * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP
 583     *
 584     * Does not apply to SQLite
 585     *
 586     * @var string
 587     */
 588    public $database_host   = 'localhost';
 589
 590    /**
 591     * Database username for connection (mysql, postgres only)
 592     * Default is an empty string
 593     *
 594     * @var string
 595     */
 596    public $database_user   = '';
 597
 598    /**
 599     * Database password for connection (mysql, postgres only)
 600     * Default is empty string
 601     *
 602     * @var string
 603     */
 604    public $database_pass   = '';
 605
 606    /**
 607     * Name of the database to select (mysql, postgres only)
 608     *
 609     * @see Securimage::$database_file for SQLite
 610     * @var string
 611     */
 612    public $database_name   = '';
 613
 614    /**
 615     * Database table where captcha codes are stored
 616     *
 617     * Note: Securimage will attempt to create this table for you if it does
 618     * not exist.  If the table cannot be created, an E_USER_WARNING is emitted
 619     *
 620     * @var string
 621     */
 622    public $database_table  = 'captcha_codes';
 623
 624    /**
 625     * Fully qualified path to the database file when using SQLite3.
 626     *
 627     * This value is only used when $database_driver == sqlite and does
 628     * not apply when no database is used, or when using MySQL or PostgreSQL.
 629     *
 630     * On *nix, file must have permissions of 0666.
 631     *
 632     * **Make sure the directory containing this file is NOT web accessible**
 633     *
 634     * @var string
 635     */
 636    public $database_file;
 637
 638    /**
 639     * The type of captcha to create.
 640     *
 641     * Either alphanumeric based on *charset*, a simple math problem, or an
 642     * image consisting of 2 words from the word list.
 643     *
 644     * @see Securimage::SI_CAPTCHA_STRING SI_CAPTCHA_STRING
 645     * @see Securimage::SI_CAPTCHA_MATHEMATIC SI_CAPTCHA_MATHEMATIC
 646     * @see Securimage::SI_CAPTCHA_WORDS SI_CAPTCHA_WORDS
 647     * @see Securimage::$charset charset property
 648     * @see Securimage::$wordlist_file wordlist_file property
 649     * @var int
 650     */
 651    public $captcha_type  = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC, or self::SI_CAPTCHA_WORDS;
 652
 653    /**
 654     * The captcha namespace used for having multiple captchas on a page or
 655     * to separate captchas from differen forms on your site.
 656     * Example:
 657     *
 658     *     <?php
 659     *     // use <img src="securimage_show.php?namespace=contact_form">
 660     *     // or manually in securimage_show.php
 661     *     $img->setNamespace('contact_form');
 662     *
 663     *     // in form validator
 664     *     $img->setNamespace('contact_form');
 665     *     if ($img->check($code) == true) {
 666     *         echo "Valid!";
 667     *     }
 668     *
 669     * @var string
 670     */
 671    public $namespace;
 672
 673    /**
 674     * The TTF font file to use to draw the captcha code.
 675     *
 676     * Leave blank for default font AHGBold.ttf
 677     *
 678     * @var string
 679     */
 680    public $ttf_file;
 681
 682    /**
 683     * The path to the wordlist file to use.
 684     *
 685     * Leave blank for default words/words.txt
 686     *
 687     * @var string
 688     */
 689    public $wordlist_file;
 690
 691    /**
 692     * Character encoding of the wordlist file.
 693     * Requires PHP Multibyte String (mbstring) support.
 694     * Allows word list to contain characters other than US-ASCII (requires compatible TTF font).
 695     *
 696     * @var string The character encoding (e.g. UTF-8, UTF-7, EUC-JP, GB2312)
 697     * @see http://php.net/manual/en/mbstring.supported-encodings.php
 698     * @since 3.6.3
 699     */
 700    public $wordlist_file_encoding = null;
 701
 702    /**
 703     * The directory to scan for background images, if set a random background
 704     * will be chosen from this folder
 705     *
 706     * @var string
 707     */
 708    public $background_directory;
 709
 710    /**
 711     * No longer used
 712     *
 713     * The path to the SQLite database file to use
 714     *
 715     * @deprecated 3.2RC4
 716     * @see Securimage::$database_file database_file property
 717     * @var string
 718     */
 719    public $sqlite_database;
 720
 721    /**
 722     * The path to the audio files to be used for audio captchas.
 723     *
 724     * Can also be set in securimage_play.php
 725     *
 726     * Example:
 727     *
 728     *     $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/';
 729     *
 730     * @var string
 731     */
 732    public $audio_path;
 733
 734    /**
 735     * Use SoX (The Swiss Army knife of audio manipulation) for audio effects
 736     * and processing.
 737     *
 738     * Using SoX should make it more difficult for bots to solve audio captchas
 739     *
 740     * @see Securimage::$sox_binary_path sox_binary_path property
 741     * @var bool true to use SoX, false to use PHP
 742     */
 743    public $audio_use_sox = false;
 744
 745    /**
 746     * The path to the SoX binary on your system
 747     *
 748     * @var string
 749     */
 750    public $sox_binary_path = '/usr/bin/sox';
 751
 752    /**
 753     * The path to the lame (mp3 encoder) binary on your system
 754     * Static so that Securimage::getCaptchaHtml() has access to this value.
 755     *
 756     * @since 3.6
 757     * @var string
 758     */
 759    public static $lame_binary_path = '/usr/bin/lame';
 760
 761    /**
 762     * The path to the directory containing audio files that will be selected
 763     * randomly and mixed with the captcha audio.
 764     *
 765     * @var string
 766     */
 767    public $audio_noise_path;
 768
 769    /**
 770     * Whether or not to mix background noise files into captcha audio
 771     *
 772     * Mixing random background audio with noise can help improve security of
 773     * audio captcha.
 774     *
 775     * Default: securimage/audio/noise
 776     *
 777     * @since 3.0.3
 778     * @see Securimage::$audio_noise_path audio_noise_path property
 779     * @var bool true = mix, false = no
 780     */
 781    public $audio_use_noise;
 782
 783    /**
 784     * The method and threshold (or gain factor) used to normalize the mixing
 785     * with background noise.
 786     *
 787     * See http://www.voegler.eu/pub/audio/ for more information.
 788     *
 789     * Default: 0.6
 790     *
 791     * Valid:
 792     *     >= 1
 793     *     Normalize by multiplying by the threshold (boost - positive gain).
 794     *     A value of 1 in effect means no normalization (and results in clipping).
 795     *
 796     *     <= -1
 797     *     Normalize by dividing by the the absolute value of threshold (attenuate - negative gain).
 798     *     A factor of 2 (-2) is about 6dB reduction in volume.
 799     *
 800     *     [0, 1)  (open inverval - not including 1)
 801     *     The threshold above which amplitudes are comressed logarithmically.
 802     *     e.g. 0.6 to leave amplitudes up to 60% "as is" and compressabove.
 803     *
 804     *     (-1, 0) (open inverval - not including -1 and 0)
 805     *     The threshold above which amplitudes are comressed linearly.
 806     *     e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above.
 807     *
 808     * @since 3.0.4
 809     * @var float
 810     */
 811    public $audio_mix_normalization = 0.8;
 812
 813    /**
 814     * Whether or not to degrade audio by introducing random noise.
 815     *
 816     * Current research shows this may not increase the security of audible
 817     * captchas.
 818     *
 819     * Default: true
 820     *
 821     * @since 3.0.3
 822     * @var bool
 823     */
 824    public $degrade_audio;
 825
 826    /**
 827     * Minimum delay to insert between captcha audio letters in milliseconds
 828     *
 829     * @since 3.0.3
 830     * @var float
 831     */
 832    public $audio_gap_min = 0;
 833
 834    /**
 835     * Maximum delay to insert between captcha audio letters in milliseconds
 836     *
 837     * @since 3.0.3
 838     * @var float
 839     */
 840    public $audio_gap_max = 3000;
 841
 842    /**
 843     * The file path for logging errors from audio (default __DIR__)
 844     *
 845     * @var string|null
 846     */
 847    public $log_path = null;
 848
 849    /**
 850     * The name of the log file for logging audio errors
 851     *
 852     * @var string|null (defualt si_error.log)
 853     */
 854    public $log_file = null;
 855
 856    /**
 857     * Captcha ID if using static captcha
 858     * @var string Unique captcha id
 859     */
 860    protected static $_captchaId = null;
 861
 862    /**
 863     * The GD image resource of the captcha image
 864     *
 865     * @var resource
 866     */
 867    protected $im;
 868
 869    /**
 870     * A temporary GD image resource of the captcha image for distortion
 871     *
 872     * @var resource
 873     */
 874    protected $tmpimg;
 875
 876    /**
 877     * The background image GD resource
 878     * @var string
 879     */
 880    protected $bgimg;
 881
 882    /**
 883     * Scale factor for magnification of distorted captcha image
 884     *
 885     * @var int
 886     */
 887    protected $iscale = 2;
 888
 889    /**
 890     * Absolute path to securimage directory.
 891     *
 892     * This is calculated at runtime
 893     *
 894     * @var string
 895     */
 896    public $securimage_path = null;
 897
 898    /**
 899     * The captcha challenge value.
 900     *
 901     * Either the case-sensitive/insensitive word captcha, or the solution to
 902     * the math captcha.
 903     *
 904     * @var string|bool Captcha challenge value
 905     */
 906    protected $code;
 907
 908    /**
 909     * The display value of the captcha to draw on the image
 910     *
 911     * Either the word captcha or the math equation to present to the user
 912     *
 913     * @var string Captcha display value to draw on the image
 914     */
 915    protected $code_display;
 916
 917    /**
 918     * Alternate text to draw as the captcha image text
 919     *
 920     * A value that can be passed to the constructor that can be used to
 921     * generate a captcha image with a given value.
 922     *
 923     * This value does not get stored in the session or database and is only
 924     * used when calling Securimage::show().
 925     *
 926     * If a display_value was passed to the constructor and the captcha image
 927     * is generated, the display_value will be used as the string to draw on
 928     * the captcha image.
 929     *
 930     * Used only if captcha codes are generated and managed by a 3rd party
 931     * app/library
 932     *
 933     * @var string Captcha code value to display on the image
 934     */
 935    public $display_value;
 936
 937    /**
 938     * Captcha code supplied by user [set from Securimage::check()]
 939     *
 940     * @var string
 941     */
 942    protected $captcha_code;
 943
 944    /**
 945     * Time (in seconds) that the captcha was solved in (correctly or incorrectly).
 946     *
 947     * This is from the time of code creation, to when validation was attempted.
 948     *
 949     * @var int
 950     */
 951    protected $_timeToSolve = 0;
 952
 953    /**
 954     * Flag that can be specified telling securimage not to call exit after
 955     * generating a captcha image or audio file
 956     *
 957     * @var bool If true, script will not terminate; if false script will terminate (default)
 958     */
 959    protected $no_exit;
 960
 961    /**
 962     * Flag indicating whether or not a PHP session should be started and used
 963     *
 964     * @var bool If true, no session will be started; if false, session will be started and used to store data (default)
 965     */
 966    protected $no_session;
 967
 968    /**
 969     * Flag indicating whether or not HTTP headers will be sent when outputting
 970     * captcha image/audio
 971     *
 972     * @var bool If true (default) headers will be sent, if false, no headers are sent
 973     */
 974    protected $send_headers;
 975
 976    /**
 977     * PDO connection when a database is used
 978     *
 979     * @var PDO|bool
 980     */
 981    protected $pdo_conn;
 982
 983    /**
 984     * The GD color for the background color
 985     *
 986     * @var int
 987     */
 988    protected $gdbgcolor;
 989
 990    /**
 991     * The GD color for the text color
 992     *
 993     * @var int
 994     */
 995    protected $gdtextcolor;
 996
 997    /**
 998     * The GD color for the line color
 999     *
1000     * @var int
1001     */
1002    protected $gdlinecolor;
1003
1004    /**
1005     * The GD color for the signature text color
1006     *
1007     * @var int
1008     */
1009    protected $gdsignaturecolor;
1010
1011    /**
1012     * Create a new securimage object, pass options to set in the constructor.
1013     *
1014     * The object can then be used to display a captcha, play an audible captcha, or validate a submission.
1015     *
1016     * @param array $options  Options to initialize the class.  May be any class property.
1017     *
1018     *     $options = array(
1019     *         'text_color' => new Securimage_Color('#013020'),
1020     *         'code_length' => 5,
1021     *         'num_lines' => 5,
1022     *         'noise_level' => 3,
1023     *         'font_file' => Securimage::getPath() . '/custom.ttf'
1024     *     );
1025     *
1026     *     $img = new Securimage($options);
1027     *
1028     */
1029    public function __construct($options = array())
1030    {
1031        $this->securimage_path = dirname(__FILE__);
1032
1033        if (!is_array($options)) {
1034            trigger_error(
1035                    '$options passed to Securimage::__construct() must be an array.  ' .
1036                    gettype($options) . ' given',
1037                    E_USER_WARNING
1038            );
1039            $options = array();
1040        }
1041
1042        if (function_exists('mb_internal_encoding')) {
1043            mb_internal_encoding('UTF-8');
1044        }
1045
1046        // check for and load settings from custom config file
1047        $config_file = null;
1048
1049        if (file_exists(dirname(__FILE__) . '/config.inc.php')) {
1050            $config_file = dirname(__FILE__) . '/config.inc.php';
1051        }
1052        if (isset($options['config_file']) && file_exists($options['config_file'])) {
1053            $config_file = $options['config_file'];
1054        }
1055
1056        if ($config_file) {
1057            $settings = include $config_file;
1058
1059            if (is_array($settings)) {
1060                $options = array_merge($settings, $options);
1061            }
1062        }
1063
1064        if (is_array($options) && sizeof($options) > 0) {
1065            foreach($options as $prop => $val) {
1066                if ($prop == 'captchaId') {
1067                    Securimage::$_captchaId = $val;
1068                    $this->use_database     = true;
1069                } else if ($prop == 'use_sqlite_db') {
1070                    trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
1071                } else {
1072                    $this->$prop = $val;
1073                }
1074            }
1075        }
1076
1077        $this->image_bg_color  = $this->initColor($this->image_bg_color,  '#ffffff');
1078        $this->text_color      = $this->initColor($this->text_color,      '#616161');
1079        $this->line_color      = $this->initColor($this->line_color,      '#616161');
1080        $this->noise_color     = $this->initColor($this->noise_color,     '#616161');
1081        $this->signature_color = $this->initColor($this->signature_color, '#616161');
1082
1083        if (is_null($this->ttf_file)) {
1084            $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
1085        }
1086
1087        $this->signature_font = $this->ttf_file;
1088
1089        if (is_null($this->wordlist_file)) {
1090            $this->wordlist_file = $this->securimage_path . '/words/words.txt';
1091        }
1092
1093        if (is_null($this->database_file)) {
1094            $this->database_file = $this->securimage_path . '/database/securimage.sq3';
1095        }
1096
1097        if (is_null($this->audio_path)) {
1098            $this->audio_path = $this->securimage_path . '/audio/en/';
1099        }
1100
1101        if (is_null($this->audio_noise_path)) {
1102            $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
1103        }
1104
1105        if (is_null($this->audio_use_noise)) {
1106            $this->audio_use_noise = true;
1107        }
1108
1109        if (is_null($this->degrade_audio)) {
1110            $this->degrade_audio = true;
1111        }
1112
1113        if (is_null($this->code_length) || (int)$this->code_length < 1) {
1114            $this->code_length = 6;
1115        }
1116
1117        if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
1118            $this->perturbation = 0.75;
1119        }
1120
1121        if (is_null($this->namespace) || !is_string($this->namespace)) {
1122            $this->namespace = 'default';
1123        }
1124
1125        if (is_null($this->no_exit)) {
1126            $this->no_exit = false;
1127        }
1128
1129        if (is_null($this->no_session)) {
1130            $this->no_session = false;
1131        }
1132
1133        if (is_null($this->send_headers)) {
1134            $this->send_headers = true;
1135        }
1136
1137        if (is_null($this->log_path)) {
1138            $this->log_path = __DIR__;
1139        }
1140
1141        if (is_null($this->log_file)) {
1142            $this->log_file = 'securimage.error_log';
1143        }
1144
1145        if ($this->no_session != true) {
1146            // Initialize session or attach to existing
1147            if ( session_id() == '' || (function_exists('session_status') && PHP_SESSION_NONE == session_status()) ) { // no session has been started yet (or it was previousy closed), which is needed for validation
1148                if (!is_null($this->session_name) && trim($this->session_name) != '') {
1149                    session_name(trim($this->session_name)); // set session name if provided
1150                }
1151                session_start();
1152            }
1153        }
1154    }
1155
1156    /**
1157     * Return the absolute path to the Securimage directory.
1158     *
1159     * @return string The path to the securimage base directory
1160     */
1161    public static function getPath()
1162    {
1163        return dirname(__FILE__);
1164    }
1165
1166    /**
1167     * Generate a new captcha ID or retrieve the current ID (if exists).
1168     *
1169     * @param bool $new If true, generates a new challenge and returns and ID.  If false, the existing captcha ID is returned, or null if none exists.
1170     * @param array $options Additional options to be passed to Securimage.
1171     *   $options must include database settings if they are not set directly in securimage.php
1172     *
1173     * @return null|string Returns null if no captcha id set and new was false, or the captcha ID
1174     */
1175    public static function getCaptchaId($new = true, array $options = array())
1176    {
1177        if (is_null($new) || (bool)$new == true) {
1178            $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
1179            $opts = array('no_session'    => true,
1180                          'use_database'  => true);
1181            if (sizeof($options) > 0) $opts = array_merge($options, $opts);
1182            $si = new self($opts);
1183            Securimage::$_captchaId = $id;
1184            $si->createCode();
1185
1186            return $id;
1187        } else {
1188            return Securimage::$_captchaId;
1189        }
1190    }
1191
1192    /**
1193     * Validate a captcha code input against a captcha ID
1194     *
1195     * @param string $id       The captcha ID to check
1196     * @param string $value    The captcha value supplied by the user
1197     * @param array  $options  Array of options to construct Securimage with.
1198     *   Options must include database options if they are not set in securimage.php
1199     *
1200     * @see Securimage::$database_driver
1201     * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open
1202     */
1203    public static function checkByCaptchaId($id, $value, array $options = array())
1204    {
1205        $opts = array('captchaId'    => $id,
1206                      'no_session'   => true,
1207                      'use_database' => true);
1208
1209        if (sizeof($options) > 0) $opts = array_merge($options, $opts);
1210
1211        $si = new self($opts);
1212
1213        if ($si->openDatabase()) {
1214            $code = $si->getCodeFromDatabase();
1215
1216            if (is_array($code)) {
1217                $si->code         = $code['code'];
1218                $si->code_display = $code['code_disp'];
1219            }
1220
1221            if ($si->check($value)) {
1222                $si->clearCodeFromDatabase();
1223
1224                return true;
1225            } else {
1226                return false;
1227            }
1228        } else {
1229            return false;
1230        }
1231    }
1232
1233
1234    /**
1235     * Generates a new challenge and serves a captcha image.
1236     *
1237     * Appropriate headers will be sent to the browser unless the *send_headers* option is false.
1238     *
1239     * @param string $background_image The absolute or relative path to the background image to use as the background of the captcha image.
1240     *
1241     *     $img = new Securimage();
1242     *     $img->code_length = 6;
1243     *     $img->num_lines   = 5;
1244     *     $img->noise_level = 5;
1245     *
1246     *     $img->show(); // sends the image and appropriate headers to browser
1247     *     exit;
1248     */
1249    public function show($background_image = '')
1250    {
1251        set_error_handler(array(&$this, 'errorHandler'));
1252
1253        if($background_image != '' && is_readable($background_image)) {
1254            $this->bgimg = $background_image;
1255        }
1256
1257        $this->doImage();
1258    }
1259
1260    /**
1261     * Checks a given code against the correct value from the session and/or database.
1262     *
1263     * @param string $code  The captcha code to check
1264     *
1265     *     $code = $_POST['code'];
1266     *     $img  = new Securimage();
1267     *     if ($img->check($code) == true) {
1268     *         $captcha_valid = true;
1269     *     } else {
1270     *         $captcha_valid = false;
1271     *     }
1272     *
1273     * @return bool true if the given code was correct, false if not.
1274     */
1275    public function check($code)
1276    {
1277        if (!is_string($code)) {
1278            trigger_error("The \$code parameter passed to Securimage::check() must be a string, " . gettype($code) . " given", E_USER_NOTICE);
1279            $code = '';
1280        }
1281
1282        $this->code_entered = $code;
1283        $this->validate();
1284        return $this->correct_code;
1285    }
1286
1287    /**
1288     * Returns HTML code for displaying the captcha image, audio button, and form text input.
1289     *
1290     * Options can be specified to modify the output of the HTML.  Accepted options:
1291     *
1292     *     'securimage_path':
1293     *         Optional: The URI to where securimage is installed (e.g. /securimage)
1294     *     'show_image_url':
1295     *         Path to the securimage_show.php script (useful when integrating with a framework or moving outside the securimage directory)
1296     *         This will be passed as a urlencoded string to the <img> tag for outputting the captcha image
1297     *     'audio_play_url':
1298     *         Same as show_image_url, except this indicates the URL of the audio playback script
1299     *     'image_id':
1300     *          A string that sets the "id" attribute of the captcha image (default: captcha_image)
1301     *     'image_alt_text':
1302     *         The alt text of the captcha image (default: CAPTCHA Image)
1303     *     'show_audio_button':
1304     *         true/false  Whether or not to show the audio button (default: true)
1305     *     'disable_flash_fallback':)
1306     *         Allow only HTML5 audio and disable Flash fallback
1307     *     'show_refresh_button':
1308     *         true/false  Whether or not to show a button to refresh the image (default: true)
1309     *     'audio_icon_url':
1310     *         URL to the image used for showing the HTML5 audio icon
1311     *     'icon_size':
1312     *         Size (for both height & width) in pixels of the audio and refresh buttons
1313     *     'show_text_input':
1314     *         true/false  Whether or not to show the text input for the captcha (default: true)
1315     *     'refresh_alt_text':
1316     *         Alt text for the refresh image (default: Refresh Image)
1317     *     'refresh_title_text':
1318     *         Title text for the refresh image link (default: Refresh Image)
1319     *     'input_id':
1320     *         A string that sets the "id" attribute of the captcha text input (default: captcha_code)
1321     *     'input_name':
1322     *         A string that sets the "name" attribute of the captcha text input (default: same as input_id)
1323     *     'input_text':
1324     *         A string that sets the text of the label for the captcha text input (default: Type the text:)
1325     *     'input_attributes':
1326     *         An array of additional HTML tag attributes to pass to the text input tag (default: empty)
1327     *     'image_attributes':
1328     *         An array of additional HTML tag attributes to pass to the captcha image tag (default: empty)
1329     *     'error_html':
1330     *         Optional HTML markup to be shown above the text input field
1331     *     'namespace':
1332     *         The optional captcha namespace to use for showing the image and playing back the audio. Namespaces are for using multiple captchas on the same page.
1333     *
1334     * @param array $options Array of options for modifying the HTML code.
1335     * @param int   $parts Securiage::HTML_* constant controlling what component of the captcha HTML to display
1336     *
1337     * @return string  The generated HTML code for displaying the captcha
1338     */
1339    public static function getCaptchaHtml($options = array(), $parts = Securimage::HTML_ALL)
1340    {
1341        static $javascript_init = false;
1342
1343        if (!isset($options['securimage_path'])) {
1344            $docroot = (isset($_SERVER['DOCUMENT_ROOT'])) ? $_SERVER['DOCUMENT_ROOT'] : substr($_SERVER['SCRIPT_FILENAME'], 0, -strlen($_SERVER['SCRIPT_NAME']));
1345            $docroot = realpath($docroot);
1346            $sipath  = dirname(__FILE__);
1347            $securimage_path = str_replace($docroot, '', $sipath);
1348        } else {
1349            $securimage_path = $options['securimage_path'];
1350        }
1351
1352        $show_image_url    = (isset($options['show_image_url'])) ? $options['show_image_url'] : null;
1353        $image_id          = (isset($options['image_id'])) ? $options['image_id'] : 'captcha_image';
1354        $image_alt         = (isset($options['image_alt_text'])) ? $options['image_alt_text'] : 'CAPTCHA Image';
1355        $show_audio_btn    = (isset($options['show_audio_button'])) ? (bool)$options['show_audio_button'] : true;
1356        $disable_flash_fbk = (isset($options['disable_flash_fallback'])) ? (bool)$options['disable_flash_fallback'] : false;
1357        $show_refresh_btn  = (isset($options['show_refresh_button'])) ? (bool)$options['show_refresh_button'] : true;
1358        $refresh_icon_url  = (isset($options['refresh_icon_url'])) ? $options['refresh_icon_url'] : null;
1359        $audio_but_bg_col  = (isset($options['audio_button_bgcol'])) ? $options['audio_button_bgcol'] : '#ffffff';
1360        $audio_icon_url    = (isset($options['audio_icon_url'])) ? $options['audio_icon_url'] : null;
1361        $loading_icon_url  = (isset($options['loading_icon_url'])) ? $options['loading_icon_url'] : null;
1362        $icon_size         = (isset($options['icon_size'])) ? $options['icon_size'] : 32;
1363        $audio_play_url    = (isset($options['audio_play_url'])) ? $options['audio_play_url'] : null;
1364        $audio_swf_url     = (isset($options['audio_swf_url'])) ? $options['audio_swf_url'] : null;
1365        $show_input        = (isset($options['show_text_input'])) ? (bool)$options['show_text_input'] : true;
1366        $refresh_alt       = (isset($options['refresh_alt_text'])) ? $options['refresh_alt_text'] : 'Refresh Image';
1367        $refresh_title     = (isset($options['refresh_title_text'])) ? $options['refresh_title_text'] : 'Refresh Image';
1368        $input_text        = (isset($options['input_text'])) ? $options['input_text'] : 'Type the text:';
1369        $input_id          = (isset($options['input_id'])) ? $options['input_id'] : 'captcha_code';
1370        $input_name        = (isset($options['input_name'])) ? $options['input_name'] :  $input_id;
1371        $input_attrs       = (isset($options['input_attributes'])) ? $options['input_attributes'] : array();
1372        $image_attrs       = (isset($options['image_attributes'])) ? $options['image_attributes'] : array();
1373        $error_html        = (isset($options['error_html'])) ? $options['error_html'] : null;
1374        $namespace         = (isset($options['namespace'])) ? $options['namespace'] : '';
1375
1376        $rand              = md5(uniqid($_SERVER['REMOTE_PORT'], true));
1377        $securimage_path   = rtrim($securimage_path, '/\\');
1378        $securimage_path   = str_replace('\\', '/', $securimage_path);
1379
1380        $image_attr = '';
1381        if (!is_array($image_attrs)) $image_attrs = array();
1382        if (!isset($image_attrs['style'])) $image_attrs['style'] = 'float: left; padding-right: 5px';
1383        $image_attrs['id']  = $image_id;
1384
1385        $show_path = $securimage_path . '/securimage_show.php?';
1386        if ($show_image_url) {
1387            if (parse_url($show_image_url, PHP_URL_QUERY)) {
1388                $show_path = "{$show_image_url}&";
1389            } else {
1390                $show_path = "{$show_image_url}?";
1391            }
1392        }
1393        if (!empty($namespace)) {
1394            $show_path .= sprintf('namespace=%s&amp;', $namespace);
1395        }
1396        $image_attrs['src'] = $show_path . $rand;
1397
1398        $image_attrs['alt'] = $image_alt;
1399
1400        foreach($image_attrs as $name => $val) {
1401            $image_attr .= sprintf('%s="%s" ', $name, htmlspecialchars($val));
1402        }
1403
1404        $swf_path  = $securimage_path . '/securimage_play.swf';
1405        $play_path = $securimage_path . '/securimage_play.php?';
1406        $icon_path = $securimage_path . '/images/audio_icon.png';
1407        $load_path = $securimage_path . '/images/loading.png';
1408        $js_path   = $securimage_path . '/securimage.js';
1409
1410        if (!empty($audio_icon_url)) {
1411            $icon_path = $audio_icon_url;
1412        }
1413
1414        if (!empty($loading_icon_url)) {
1415            $load_path = $loading_icon_url;
1416        }
1417
1418        if (!empty($audio_play_url)) {
1419            if (parse_url($audio_play_url, PHP_URL_QUERY)) {
1420                $play_path = "{$audio_play_url}&";
1421            } else {
1422                $play_path = "{$audio_play_url}?";
1423            }
1424        }
1425
1426        if (!empty($namespace)) {
1427            $play_path .= sprintf('namespace=%s&amp;', $namespace);
1428        }
1429
1430        if (!empty($audio_swf_url)) {
1431            $swf_path = $audio_swf_url;
1432        }
1433
1434        $audio_obj = $image_id . '_audioObj';
1435        $html      = '';
1436
1437        if ( ($parts & Securimage::HTML_IMG) > 0) {
1438            $html .= sprintf('<img %s/>', $image_attr);
1439        }
1440
1441        if ( ($parts & Securimage::HTML_AUDIO) > 0 && $show_audio_btn) {
1442            // html5 audio
1443            $html .= sprintf('<div id="%s_audio_div">', $image_id) . "\n" .
1444                     sprintf('<audio id="%s_audio" preload="none" style="display: none">', $image_id) . "\n";
1445
1446            // check for existence and executability of LAME binary
1447            // prefer mp3 over wav by sourcing it first, if available
1448            if (is_executable(Securimage::$lame_binary_path)) {
1449                $html .= sprintf('<source id="%s_source_mp3" src="%sid=%s&amp;format=mp3" type="audio/mpeg">', $image_id, $play_path, uniqid()) . "\n";
1450            }
1451
1452            // output wav source
1453            $html .= sprintf('<source id="%s_source_wav" src="%sid=%s" type="audio/wav">', $image_id, $play_path, uniqid()) . "\n";
1454
1455            // flash audio button
1456            if (!$disable_flash_fbk) {
1457                $html .= sprintf('<object type="application/x-shockwave-flash" data="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s" height="%d" width="%d">',
1458                        htmlspecialchars($swf_path),
1459                        urlencode($audio_but_bg_col),
1460                        urlencode($icon_path),
1461                        urlencode(html_entity_decode($play_path)),
1462                        $icon_size, $icon_size
1463                );
1464
1465                $html .= sprintf('<param name="movie" value="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s">',
1466                        htmlspecialchars($swf_path),
1467                        urlencode($audio_but_bg_col),
1468                        urlencode($icon_path),
1469                        urlencode(html_entity_decode($play_path))
1470                );
1471
1472                $html .= '</object><br />';
1473            }
1474
1475            // html5 audio close
1476            $html .= "</audio>\n</div>\n";
1477
1478            // html5 audio controls
1479            $html .= sprintf('<div id="%s_audio_controls">', $image_id) . "\n" .

Large files files are truncated, but you can click here to view the full file