securimage.php 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230
  1. <?php
  2. // error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging
  3. /**
  4. * Project: Securimage: A PHP class dealing with CAPTCHA images, audio, and validation
  5. * File: securimage.php
  6. *
  7. * Copyright (c) 2017, Drew Phillips
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without modification,
  11. * are permitted provided that the following conditions are met:
  12. *
  13. * - Redistributions of source code must retain the above copyright notice,
  14. * this list of conditions and the following disclaimer.
  15. * - Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  23. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. * POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. * Any modifications to the library should be indicated clearly in the source code
  32. * to inform users that the changes are not a part of the original software.
  33. *
  34. * If you found this script useful, please take a quick moment to rate it.
  35. * http://www.hotscripts.com/rate/49400.html Thanks.
  36. *
  37. * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
  38. * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
  39. * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
  40. * @copyright 2017 Drew Phillips
  41. * @author Drew Phillips <drew@drew-phillips.com>
  42. * @version 3.6.6 (Nov 20 2017)
  43. * @package Securimage
  44. *
  45. */
  46. /**
  47. ChangeLog
  48. 3.6.6
  49. - Not critical: Fix potential HTML injection in example form via HTTP_USER_AGENT (CVE-2017-14077)
  50. 3.6.5
  51. - Fix regex in replaceElements in securimage.js
  52. - Update examples
  53. - Exclude certain examples from Git autogenerated archives
  54. 3.6.4
  55. - Fix XSS vulnerability in example_form.ajax.php (Discovered by RedTeam. advisory rt-sa-2016-002)
  56. - Update example_form.ajax.php to use Securimage::getCaptchaHtml()
  57. 3.6.3
  58. - Add support for multibyte wordlist files
  59. - Fix code generation issues with UTF-8 charsets
  60. - Add parameter to getCaptchaHtml() method to control display components of captcha HTML
  61. - Fix database audio storage issue with multiple namespaces
  62. 3.6.2
  63. - Support HTTP range requests with audio playback (iOS requirement)
  64. - Add optional config.inc.php for storing global configuration settings
  65. 3.6.1
  66. - Fix copyElement bug in securimage.js for IE Flash fallback
  67. 3.6
  68. - Implement CAPTCHA audio using HTML5 <audio> with optional Flash fallback
  69. - Support MP3 audio using LAME MP3 Encoder (Internet Explorer 9+ does not support WAV format in <audio> tags)
  70. - Add getCaptchaHtml() options to support full framework integration (ruifil)
  71. 3.5.4
  72. - Fix email validation code in example form files
  73. - Fix backslashes in getCaptchaHtml for img attribute on Windows systems
  74. 3.5.3
  75. - Add options for audio button to getCaptchaHtml(), fix urlencoding of flash parameters that was breaking button
  76. 3.5.2
  77. - Add Securimage::getCaptchaHtml() for getting automatically generated captcha html code
  78. - Option for using SoX to add effects to captcha audio to make identification by neural networks more difficult
  79. - Add setNamespace() method
  80. - Add getTimeToSolve() method
  81. - Add session_status() check so session still starts if one had previously been opened and closed
  82. - Add .htaccess file to audio directory to deny access; update audio files
  83. - Option to skip checking of database tables during connection
  84. - Add composer.json to package, submit to packagist
  85. - Add font_ratio variable to determine size of font (github.com/wilkor)
  86. - Add hint if sqlite3 database is not writeable. Improve database error handling, add example database options to securimage_play.php
  87. - Fixed issue regarding database storage and math captcha breaking audio output (github.com/SoftwareAndOutsourcing)
  88. 3.5.1
  89. - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>)
  90. 3.5
  91. - Release new version
  92. - MB string support for charlist
  93. - Modify audio file path to use language directories
  94. - Changed default captcha appearance
  95. 3.2RC4
  96. - Add MySQL, PostgreSQL, and SQLite3 support for database storage
  97. - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions
  98. - Add new captcha type that displays 2 dictionary words on one image
  99. - Update examples
  100. 3.2RC3
  101. - Fix canSendHeaders() check which was breaking if a PHP startup error was issued
  102. 3.2RC2
  103. - Add error handler (https://github.com/dapphp/securimage/issues/15)
  104. - Fix flash examples to use the correct value name for audio parameter
  105. 3.2RC1
  106. - New audio captcha code. Faster, fully dynamic audio, full WAV support
  107. (Paul Voegler, Drew Phillips) <http://voegler.eu/pub/audio>
  108. - New Flash audio streaming button. User defined image and size supported
  109. - Additional options for customizing captcha (noise_level, send_headers,
  110. no_exit, no_session, display_value
  111. - Add captcha ID support. Uses sqlite and unique captcha IDs to track captchas,
  112. no session used
  113. - Add static methods for creating and validating captcha by ID
  114. - Automatic clearing of old codes from SQLite database
  115. 3.0.3Beta
  116. - Add improved mixing function to WavFile class (Paul Voegler)
  117. - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips)
  118. - Add option to use random file as background noise in captcha audio
  119. - Add new securimage options for audio files
  120. 3.0.2Beta
  121. - Fix issue with session variables when upgrading from 2.0 - 3.0
  122. - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work
  123. 3.0.1
  124. - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on
  125. 3.0
  126. - Rewrite class using PHP5 OOP
  127. - Remove support for GD fonts, require FreeType
  128. - Remove support for multi-color codes
  129. - Add option to make codes case-sensitive
  130. - Add namespaces to support multiple captchas on a single page or page specific captchas
  131. - Add option to show simple math problems instead of codes
  132. - Remove support for mp3 files due to vulnerability in decoding mp3 audio files
  133. - Create new flash file to stream wav files instead of mp3
  134. - Changed to BSD license
  135. 2.0.2
  136. - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
  137. 2.0.1
  138. - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
  139. - 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)
  140. - Check for previous definition of image type constants (Mike Challis)
  141. - Fix mime type settings for audio output
  142. - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function
  143. - Ability to let codes expire after a given length of time
  144. - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis)
  145. 2.0.0
  146. - Add mathematical distortion to characters (using code from HKCaptcha)
  147. - Improved session support
  148. - Added Securimage_Color class for easier color definitions
  149. - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
  150. - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
  151. - Audio output is mp3 format by default
  152. - Change font to AlteHaasGrotesk by yann le coroller
  153. - Some code cleanup
  154. 1.0.4 (unreleased)
  155. - Ability to output audible codes in mp3 format to stream from flash
  156. 1.0.3.1
  157. - Error reading from wordlist in some cases caused words to be cut off 1 letter short
  158. 1.0.3
  159. - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
  160. 1.0.2
  161. - Audible CAPTCHA Code wav files
  162. - Create codes from a word list instead of random strings
  163. 1.0
  164. - Added the ability to use a selected character set, rather than a-z0-9 only.
  165. - Added the multi-color text option to use different colors for each letter.
  166. - Switched to automatic session handling instead of using files for code storage
  167. - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones.
  168. - Added the ability to set line thickness
  169. - Added option for drawing arced lines over letters
  170. - Added ability to choose image type for output
  171. */
  172. /**
  173. * Securimage CAPTCHA Class.
  174. *
  175. * A class for creating and validating secure CAPTCHA images and audio.
  176. *
  177. * The class contains many options regarding appearance, security, storage of
  178. * captcha data and image/audio generation options.
  179. *
  180. * @package Securimage
  181. * @subpackage classes
  182. * @author Drew Phillips <drew@drew-phillips.com>
  183. *
  184. */
  185. class Securimage
  186. {
  187. // All of the public variables below are securimage options
  188. // They can be passed as an array to the Securimage constructor, set below,
  189. // or set from securimage_show.php and securimage_play.php
  190. /**
  191. * Constant for rendering captcha as a JPEG image
  192. * @var int
  193. */
  194. const SI_IMAGE_JPEG = 1;
  195. /**
  196. * Constant for rendering captcha as a PNG image (default)
  197. * @var int
  198. */
  199. const SI_IMAGE_PNG = 2;
  200. /**
  201. * Constant for rendering captcha as a GIF image
  202. * @var int
  203. */
  204. const SI_IMAGE_GIF = 3;
  205. /**
  206. * Constant for generating a normal alphanumeric captcha based on the
  207. * character set
  208. *
  209. * @see Securimage::$charset charset property
  210. * @var int
  211. */
  212. const SI_CAPTCHA_STRING = 0;
  213. /**
  214. * Constant for generating a captcha consisting of a simple math problem
  215. *
  216. * @var int
  217. */
  218. const SI_CAPTCHA_MATHEMATIC = 1;
  219. /**
  220. * Constant for generating a word based captcha using 2 words from a list
  221. *
  222. * @var int
  223. */
  224. const SI_CAPTCHA_WORDS = 2;
  225. /**
  226. * MySQL option identifier for database storage option
  227. *
  228. * @var string
  229. */
  230. const SI_DRIVER_MYSQL = 'mysql';
  231. /**
  232. * PostgreSQL option identifier for database storage option
  233. *
  234. * @var string
  235. */
  236. const SI_DRIVER_PGSQL = 'pgsql';
  237. /**
  238. * SQLite option identifier for database storage option
  239. *
  240. * @var string
  241. */
  242. const SI_DRIVER_SQLITE3 = 'sqlite';
  243. /**
  244. * getCaptchaHtml() display constant for HTML Captcha Image
  245. *
  246. * @var integer
  247. */
  248. const HTML_IMG = 1;
  249. /**
  250. * getCaptchaHtml() display constant for HTML5 Audio code
  251. *
  252. * @var integer
  253. */
  254. const HTML_AUDIO = 2;
  255. /**
  256. * getCaptchaHtml() display constant for Captcha Input text box
  257. *
  258. * @var integer
  259. */
  260. const HTML_INPUT = 4;
  261. /**
  262. * getCaptchaHtml() display constant for Captcha Text HTML label
  263. *
  264. * @var integer
  265. */
  266. const HTML_INPUT_LABEL = 8;
  267. /**
  268. * getCaptchaHtml() display constant for HTML Refresh button
  269. *
  270. * @var integer
  271. */
  272. const HTML_ICON_REFRESH = 16;
  273. /**
  274. * getCaptchaHtml() display constant for all HTML elements (default)
  275. *
  276. * @var integer
  277. */
  278. const HTML_ALL = 0xffffffff;
  279. /*%*********************************************************************%*/
  280. // Properties
  281. /**
  282. * The width of the captcha image
  283. * @var int
  284. */
  285. public $image_width = 215;
  286. /**
  287. * The height of the captcha image
  288. * @var int
  289. */
  290. public $image_height = 80;
  291. /**
  292. * Font size is calculated by image height and this ratio. Leave blank for
  293. * default ratio of 0.4.
  294. *
  295. * Valid range: 0.1 - 0.99.
  296. *
  297. * Depending on image_width, values > 0.6 are probably too large and
  298. * values < 0.3 are too small.
  299. *
  300. * @var float
  301. */
  302. public $font_ratio;
  303. /**
  304. * The type of the image, default = png
  305. *
  306. * @see Securimage::SI_IMAGE_PNG SI_IMAGE_PNG
  307. * @see Securimage::SI_IMAGE_JPEG SI_IMAGE_JPEG
  308. * @see Securimage::SI_IMAGE_GIF SI_IMAGE_GIF
  309. * @var int
  310. */
  311. public $image_type = self::SI_IMAGE_PNG;
  312. /**
  313. * The background color of the captcha
  314. * @var Securimage_Color|string
  315. */
  316. public $image_bg_color = '#ffffff';
  317. /**
  318. * The color of the captcha text
  319. * @var Securimage_Color|string
  320. */
  321. public $text_color = '#707070';
  322. /**
  323. * The color of the lines over the captcha
  324. * @var Securimage_Color|string
  325. */
  326. public $line_color = '#707070';
  327. /**
  328. * The color of the noise that is drawn
  329. * @var Securimage_Color|string
  330. */
  331. public $noise_color = '#707070';
  332. /**
  333. * How transparent to make the text.
  334. *
  335. * 0 = completely opaque, 100 = invisible
  336. *
  337. * @var int
  338. */
  339. public $text_transparency_percentage = 20;
  340. /**
  341. * Whether or not to draw the text transparently.
  342. *
  343. * true = use transparency, false = no transparency
  344. *
  345. * @var bool
  346. */
  347. public $use_transparent_text = true;
  348. /**
  349. * The length of the captcha code
  350. * @var int
  351. */
  352. public $code_length = 6;
  353. /**
  354. * Whether the captcha should be case sensitive or not.
  355. *
  356. * Not recommended, use only for maximum protection.
  357. *
  358. * @var bool
  359. */
  360. public $case_sensitive = false;
  361. /**
  362. * The character set to use for generating the captcha code
  363. * @var string
  364. */
  365. public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
  366. /**
  367. * How long in seconds a captcha remains valid, after this time it will be
  368. * considered incorrect.
  369. *
  370. * @var int
  371. */
  372. public $expiry_time = 900;
  373. /**
  374. * The session name securimage should use.
  375. *
  376. * Only use if your application uses a custom session name (e.g. Joomla).
  377. * It is recommended to set this value here so it is used by all securimage
  378. * scripts (i.e. securimage_show.php)
  379. *
  380. * @var string
  381. */
  382. public $session_name = null;
  383. /**
  384. * true to use the wordlist file, false to generate random captcha codes
  385. * @var bool
  386. */
  387. public $use_wordlist = false;
  388. /**
  389. * The level of distortion.
  390. *
  391. * 0.75 = normal, 1.0 = very high distortion
  392. *
  393. * @var double
  394. */
  395. public $perturbation = 0.85;
  396. /**
  397. * How many lines to draw over the captcha code to increase security
  398. * @var int
  399. */
  400. public $num_lines = 5;
  401. /**
  402. * The level of noise (random dots) to place on the image, 0-10
  403. * @var int
  404. */
  405. public $noise_level = 2;
  406. /**
  407. * The signature text to draw on the bottom corner of the image
  408. * @var string
  409. */
  410. public $image_signature = '';
  411. /**
  412. * The color of the signature text
  413. * @var Securimage_Color|string
  414. */
  415. public $signature_color = '#707070';
  416. /**
  417. * The path to the ttf font file to use for the signature text.
  418. * Defaults to $ttf_file (AHGBold.ttf)
  419. *
  420. * @see Securimage::$ttf_file
  421. * @var string
  422. */
  423. public $signature_font;
  424. /**
  425. * No longer used.
  426. *
  427. * Use an SQLite database to store data (for users that do not support cookies)
  428. *
  429. * @var bool
  430. * @see Securimage::$database_driver database_driver property
  431. * @deprecated 3.2RC4
  432. */
  433. public $use_sqlite_db = false;
  434. /**
  435. * Use a database backend for code storage.
  436. * Provides a fallback to users with cookies disabled.
  437. * Required when using captcha IDs.
  438. *
  439. * @see Securimage::$database_driver
  440. * @var bool
  441. */
  442. public $use_database = false;
  443. /**
  444. * Whether or not to skip checking if Securimage tables exist when using a
  445. * database.
  446. *
  447. * Turn this to true once database functionality is working to improve
  448. * performance.
  449. *
  450. * @var bool true to not check if captcha_codes tables are set up, false
  451. * to check (and create if necessary)
  452. */
  453. public $skip_table_check = false;
  454. /**
  455. * Database driver to use for database support.
  456. * Allowable values: *mysql*, *pgsql*, *sqlite*.
  457. * Default: sqlite
  458. *
  459. * @var string
  460. */
  461. public $database_driver = self::SI_DRIVER_SQLITE3;
  462. /**
  463. * Database host to connect to when using mysql or postgres
  464. *
  465. * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP
  466. *
  467. * Does not apply to SQLite
  468. *
  469. * @var string
  470. */
  471. public $database_host = 'localhost';
  472. /**
  473. * Database username for connection (mysql, postgres only)
  474. * Default is an empty string
  475. *
  476. * @var string
  477. */
  478. public $database_user = '';
  479. /**
  480. * Database password for connection (mysql, postgres only)
  481. * Default is empty string
  482. *
  483. * @var string
  484. */
  485. public $database_pass = '';
  486. /**
  487. * Name of the database to select (mysql, postgres only)
  488. *
  489. * @see Securimage::$database_file for SQLite
  490. * @var string
  491. */
  492. public $database_name = '';
  493. /**
  494. * Database table where captcha codes are stored
  495. *
  496. * Note: Securimage will attempt to create this table for you if it does
  497. * not exist. If the table cannot be created, an E_USER_WARNING is emitted
  498. *
  499. * @var string
  500. */
  501. public $database_table = 'captcha_codes';
  502. /**
  503. * Fully qualified path to the database file when using SQLite3.
  504. *
  505. * This value is only used when $database_driver == sqlite and does
  506. * not apply when no database is used, or when using MySQL or PostgreSQL.
  507. *
  508. * On *nix, file must have permissions of 0666.
  509. *
  510. * **Make sure the directory containing this file is NOT web accessible**
  511. *
  512. * @var string
  513. */
  514. public $database_file;
  515. /**
  516. * The type of captcha to create.
  517. *
  518. * Either alphanumeric based on *charset*, a simple math problem, or an
  519. * image consisting of 2 words from the word list.
  520. *
  521. * @see Securimage::SI_CAPTCHA_STRING SI_CAPTCHA_STRING
  522. * @see Securimage::SI_CAPTCHA_MATHEMATIC SI_CAPTCHA_MATHEMATIC
  523. * @see Securimage::SI_CAPTCHA_WORDS SI_CAPTCHA_WORDS
  524. * @see Securimage::$charset charset property
  525. * @see Securimage::$wordlist_file wordlist_file property
  526. * @var int
  527. */
  528. public $captcha_type = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC, or self::SI_CAPTCHA_WORDS;
  529. /**
  530. * The captcha namespace used for having multiple captchas on a page or
  531. * to separate captchas from differen forms on your site.
  532. * Example:
  533. *
  534. * <?php
  535. * // use <img src="securimage_show.php?namespace=contact_form">
  536. * // or manually in securimage_show.php
  537. * $img->setNamespace('contact_form');
  538. *
  539. * // in form validator
  540. * $img->setNamespace('contact_form');
  541. * if ($img->check($code) == true) {
  542. * echo "Valid!";
  543. * }
  544. *
  545. * @var string
  546. */
  547. public $namespace;
  548. /**
  549. * The TTF font file to use to draw the captcha code.
  550. *
  551. * Leave blank for default font AHGBold.ttf
  552. *
  553. * @var string
  554. */
  555. public $ttf_file;
  556. /**
  557. * The path to the wordlist file to use.
  558. *
  559. * Leave blank for default words/words.txt
  560. *
  561. * @var string
  562. */
  563. public $wordlist_file;
  564. /**
  565. * Character encoding of the wordlist file.
  566. * Requires PHP Multibyte String (mbstring) support.
  567. * Allows word list to contain characters other than US-ASCII (requires compatible TTF font).
  568. *
  569. * @var string The character encoding (e.g. UTF-8, UTF-7, EUC-JP, GB2312)
  570. * @see http://php.net/manual/en/mbstring.supported-encodings.php
  571. * @since 3.6.3
  572. */
  573. public $wordlist_file_encoding = null;
  574. /**
  575. * The directory to scan for background images, if set a random background
  576. * will be chosen from this folder
  577. *
  578. * @var string
  579. */
  580. public $background_directory;
  581. /**
  582. * No longer used
  583. *
  584. * The path to the SQLite database file to use
  585. *
  586. * @deprecated 3.2RC4
  587. * @see Securimage::$database_file database_file property
  588. * @var string
  589. */
  590. public $sqlite_database;
  591. /**
  592. * The path to the audio files to be used for audio captchas.
  593. *
  594. * Can also be set in securimage_play.php
  595. *
  596. * Example:
  597. *
  598. * $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/';
  599. *
  600. * @var string
  601. */
  602. public $audio_path;
  603. /**
  604. * Use SoX (The Swiss Army knife of audio manipulation) for audio effects
  605. * and processing.
  606. *
  607. * Using SoX should make it more difficult for bots to solve audio captchas
  608. *
  609. * @see Securimage::$sox_binary_path sox_binary_path property
  610. * @var bool true to use SoX, false to use PHP
  611. */
  612. public $audio_use_sox = false;
  613. /**
  614. * The path to the SoX binary on your system
  615. *
  616. * @var string
  617. */
  618. public $sox_binary_path = '/usr/bin/sox';
  619. /**
  620. * The path to the lame (mp3 encoder) binary on your system
  621. * Static so that Securimage::getCaptchaHtml() has access to this value.
  622. *
  623. * @since 3.6
  624. * @var string
  625. */
  626. public static $lame_binary_path = '/usr/bin/lame';
  627. /**
  628. * The path to the directory containing audio files that will be selected
  629. * randomly and mixed with the captcha audio.
  630. *
  631. * @var string
  632. */
  633. public $audio_noise_path;
  634. /**
  635. * Whether or not to mix background noise files into captcha audio
  636. *
  637. * Mixing random background audio with noise can help improve security of
  638. * audio captcha.
  639. *
  640. * Default: securimage/audio/noise
  641. *
  642. * @since 3.0.3
  643. * @see Securimage::$audio_noise_path audio_noise_path property
  644. * @var bool true = mix, false = no
  645. */
  646. public $audio_use_noise;
  647. /**
  648. * The method and threshold (or gain factor) used to normalize the mixing
  649. * with background noise.
  650. *
  651. * See http://www.voegler.eu/pub/audio/ for more information.
  652. *
  653. * Default: 0.6
  654. *
  655. * Valid:
  656. * >= 1
  657. * Normalize by multiplying by the threshold (boost - positive gain).
  658. * A value of 1 in effect means no normalization (and results in clipping).
  659. *
  660. * <= -1
  661. * Normalize by dividing by the the absolute value of threshold (attenuate - negative gain).
  662. * A factor of 2 (-2) is about 6dB reduction in volume.
  663. *
  664. * [0, 1) (open inverval - not including 1)
  665. * The threshold above which amplitudes are comressed logarithmically.
  666. * e.g. 0.6 to leave amplitudes up to 60% "as is" and compressabove.
  667. *
  668. * (-1, 0) (open inverval - not including -1 and 0)
  669. * The threshold above which amplitudes are comressed linearly.
  670. * e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above.
  671. *
  672. * @since 3.0.4
  673. * @var float
  674. */
  675. public $audio_mix_normalization = 0.8;
  676. /**
  677. * Whether or not to degrade audio by introducing random noise.
  678. *
  679. * Current research shows this may not increase the security of audible
  680. * captchas.
  681. *
  682. * Default: true
  683. *
  684. * @since 3.0.3
  685. * @var bool
  686. */
  687. public $degrade_audio;
  688. /**
  689. * Minimum delay to insert between captcha audio letters in milliseconds
  690. *
  691. * @since 3.0.3
  692. * @var float
  693. */
  694. public $audio_gap_min = 0;
  695. /**
  696. * Maximum delay to insert between captcha audio letters in milliseconds
  697. *
  698. * @since 3.0.3
  699. * @var float
  700. */
  701. public $audio_gap_max = 3000;
  702. /**
  703. * Captcha ID if using static captcha
  704. * @var string Unique captcha id
  705. */
  706. protected static $_captchaId = null;
  707. /**
  708. * The GD image resource of the captcha image
  709. *
  710. * @var resource
  711. */
  712. public $im;
  713. /**
  714. * A temporary GD image resource of the captcha image for distortion
  715. *
  716. * @var resource
  717. */
  718. protected $tmpimg;
  719. /**
  720. * The background image GD resource
  721. * @var string
  722. */
  723. protected $bgimg;
  724. /**
  725. * Scale factor for magnification of distorted captcha image
  726. *
  727. * @var int
  728. */
  729. protected $iscale = 5;
  730. /**
  731. * Absolute path to securimage directory.
  732. *
  733. * This is calculated at runtime
  734. *
  735. * @var string
  736. */
  737. public $securimage_path = null;
  738. /**
  739. * The captcha challenge value.
  740. *
  741. * Either the case-sensitive/insensitive word captcha, or the solution to
  742. * the math captcha.
  743. *
  744. * @var string|bool Captcha challenge value
  745. */
  746. protected $code;
  747. /**
  748. * The display value of the captcha to draw on the image
  749. *
  750. * Either the word captcha or the math equation to present to the user
  751. *
  752. * @var string Captcha display value to draw on the image
  753. */
  754. protected $code_display;
  755. /**
  756. * Alternate text to draw as the captcha image text
  757. *
  758. * A value that can be passed to the constructor that can be used to
  759. * generate a captcha image with a given value.
  760. *
  761. * This value does not get stored in the session or database and is only
  762. * used when calling Securimage::show().
  763. *
  764. * If a display_value was passed to the constructor and the captcha image
  765. * is generated, the display_value will be used as the string to draw on
  766. * the captcha image.
  767. *
  768. * Used only if captcha codes are generated and managed by a 3rd party
  769. * app/library
  770. *
  771. * @var string Captcha code value to display on the image
  772. */
  773. public $display_value;
  774. /**
  775. * Captcha code supplied by user [set from Securimage::check()]
  776. *
  777. * @var string
  778. */
  779. protected $captcha_code;
  780. /**
  781. * Time (in seconds) that the captcha was solved in (correctly or incorrectly).
  782. *
  783. * This is from the time of code creation, to when validation was attempted.
  784. *
  785. * @var int
  786. */
  787. protected $_timeToSolve = 0;
  788. /**
  789. * Flag that can be specified telling securimage not to call exit after
  790. * generating a captcha image or audio file
  791. *
  792. * @var bool If true, script will not terminate; if false script will terminate (default)
  793. */
  794. protected $no_exit;
  795. /**
  796. * Flag indicating whether or not a PHP session should be started and used
  797. *
  798. * @var bool If true, no session will be started; if false, session will be started and used to store data (default)
  799. */
  800. protected $no_session;
  801. /**
  802. * Flag indicating whether or not HTTP headers will be sent when outputting
  803. * captcha image/audio
  804. *
  805. * @var bool If true (default) headers will be sent, if false, no headers are sent
  806. */
  807. protected $send_headers;
  808. /**
  809. * PDO connection when a database is used
  810. *
  811. * @var PDO|bool
  812. */
  813. protected $pdo_conn;
  814. /**
  815. * The GD color for the background color
  816. *
  817. * @var int
  818. */
  819. protected $gdbgcolor;
  820. /**
  821. * The GD color for the text color
  822. *
  823. * @var int
  824. */
  825. protected $gdtextcolor;
  826. /**
  827. * The GD color for the line color
  828. *
  829. * @var int
  830. */
  831. protected $gdlinecolor;
  832. /**
  833. * The GD color for the signature text color
  834. *
  835. * @var int
  836. */
  837. protected $gdsignaturecolor;
  838. /**
  839. * Create a new securimage object, pass options to set in the constructor.
  840. *
  841. * The object can then be used to display a captcha, play an audible captcha, or validate a submission.
  842. *
  843. * @param array $options Options to initialize the class. May be any class property.
  844. *
  845. * $options = array(
  846. * 'text_color' => new Securimage_Color('#013020'),
  847. * 'code_length' => 5,
  848. * 'num_lines' => 5,
  849. * 'noise_level' => 3,
  850. * 'font_file' => Securimage::getPath() . '/custom.ttf'
  851. * );
  852. *
  853. * $img = new Securimage($options);
  854. *
  855. */
  856. public function __construct($options = array())
  857. {
  858. $this->securimage_path = dirname(__FILE__);
  859. if (!is_array($options)) {
  860. trigger_error(
  861. '$options passed to Securimage::__construct() must be an array. ' .
  862. gettype($options) . ' given',
  863. E_USER_WARNING
  864. );
  865. $options = array();
  866. }
  867. // check for and load settings from custom config file
  868. if (file_exists(dirname(__FILE__) . '/config.inc.php')) {
  869. $settings = include dirname(__FILE__) . '/config.inc.php';
  870. if (is_array($settings)) {
  871. $options = array_merge($settings, $options);
  872. }
  873. }
  874. if (is_array($options) && sizeof($options) > 0) {
  875. foreach($options as $prop => $val) {
  876. if ($prop == 'captchaId') {
  877. Securimage::$_captchaId = $val;
  878. $this->use_database = true;
  879. } else if ($prop == 'use_sqlite_db') {
  880. trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
  881. } else {
  882. $this->$prop = $val;
  883. }
  884. }
  885. }
  886. $this->image_bg_color = $this->initColor($this->image_bg_color, '#ffffff');
  887. $this->text_color = $this->initColor($this->text_color, '#616161');
  888. $this->line_color = $this->initColor($this->line_color, '#616161');
  889. $this->noise_color = $this->initColor($this->noise_color, '#616161');
  890. $this->signature_color = $this->initColor($this->signature_color, '#616161');
  891. if (is_null($this->ttf_file)) {
  892. $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
  893. }
  894. $this->signature_font = $this->ttf_file;
  895. if (is_null($this->wordlist_file)) {
  896. $this->wordlist_file = $this->securimage_path . '/words/words.txt';
  897. }
  898. if (is_null($this->database_file)) {
  899. $this->database_file = $this->securimage_path . '/database/securimage.sq3';
  900. }
  901. if (is_null($this->audio_path)) {
  902. $this->audio_path = $this->securimage_path . '/audio/en/';
  903. }
  904. if (is_null($this->audio_noise_path)) {
  905. $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
  906. }
  907. if (is_null($this->audio_use_noise)) {
  908. $this->audio_use_noise = true;
  909. }
  910. if (is_null($this->degrade_audio)) {
  911. $this->degrade_audio = true;
  912. }
  913. if (is_null($this->code_length) || (int)$this->code_length < 1) {
  914. $this->code_length = 6;
  915. }
  916. if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
  917. $this->perturbation = 0.75;
  918. }
  919. if (is_null($this->namespace) || !is_string($this->namespace)) {
  920. $this->namespace = 'default';
  921. }
  922. if (is_null($this->no_exit)) {
  923. $this->no_exit = false;
  924. }
  925. if (is_null($this->no_session)) {
  926. $this->no_session = false;
  927. }
  928. if (is_null($this->send_headers)) {
  929. $this->send_headers = true;
  930. }
  931. if ($this->no_session != true) {
  932. // Initialize session or attach to existing
  933. 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
  934. if (!is_null($this->session_name) && trim($this->session_name) != '') {
  935. session_name(trim($this->session_name)); // set session name if provided
  936. }
  937. session_start();
  938. }
  939. }
  940. }
  941. /**
  942. * Return the absolute path to the Securimage directory.
  943. *
  944. * @return string The path to the securimage base directory
  945. */
  946. public static function getPath()
  947. {
  948. return dirname(__FILE__);
  949. }
  950. /**
  951. * Generate a new captcha ID or retrieve the current ID (if exists).
  952. *
  953. * @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.
  954. * @param array $options Additional options to be passed to Securimage.
  955. * $options must include database settings if they are not set directly in securimage.php
  956. *
  957. * @return null|string Returns null if no captcha id set and new was false, or the captcha ID
  958. */
  959. public static function getCaptchaId($new = true, array $options = array())
  960. {
  961. if (is_null($new) || (bool)$new == true) {
  962. $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
  963. $opts = array('no_session' => true,
  964. 'use_database' => true);
  965. if (sizeof($options) > 0) $opts = array_merge($options, $opts);
  966. $si = new self($opts);
  967. Securimage::$_captchaId = $id;
  968. $si->createCode();
  969. return $id;
  970. } else {
  971. return Securimage::$_captchaId;
  972. }
  973. }
  974. /**
  975. * Validate a captcha code input against a captcha ID
  976. *
  977. * @param string $id The captcha ID to check
  978. * @param string $value The captcha value supplied by the user
  979. * @param array $options Array of options to construct Securimage with.
  980. * Options must include database options if they are not set in securimage.php
  981. *
  982. * @see Securimage::$database_driver
  983. * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open
  984. */
  985. public static function checkByCaptchaId($id, $value, array $options = array())
  986. {
  987. $opts = array('captchaId' => $id,
  988. 'no_session' => true,
  989. 'use_database' => true);
  990. if (sizeof($options) > 0) $opts = array_merge($options, $opts);
  991. $si = new self($opts);
  992. if ($si->openDatabase()) {
  993. $code = $si->getCodeFromDatabase();
  994. if (is_array($code)) {
  995. $si->code = $code['code'];
  996. $si->code_display = $code['code_disp'];
  997. }
  998. if ($si->check($value)) {
  999. $si->clearCodeFromDatabase();
  1000. return true;
  1001. } else {
  1002. return false;
  1003. }
  1004. } else {
  1005. return false;
  1006. }
  1007. }
  1008. /**
  1009. * Generates a new challenge and serves a captcha image.
  1010. *
  1011. * Appropriate headers will be sent to the browser unless the *send_headers* option is false.
  1012. *
  1013. * @param string $background_image The absolute or relative path to the background image to use as the background of the captcha image.
  1014. *
  1015. * $img = new Securimage();
  1016. * $img->code_length = 6;
  1017. * $img->num_lines = 5;
  1018. * $img->noise_level = 5;
  1019. *
  1020. * $img->show(); // sends the image and appropriate headers to browser
  1021. * exit;
  1022. */
  1023. public function show($background_image = '')
  1024. {
  1025. set_error_handler(array(&$this, 'errorHandler'));
  1026. if($background_image != '' && is_readable($background_image)) {
  1027. $this->bgimg = $background_image;
  1028. }
  1029. $this->doImage();
  1030. }
  1031. /**
  1032. * Checks a given code against the correct value from the session and/or database.
  1033. *
  1034. * @param string $code The captcha code to check
  1035. *
  1036. * $code = $_POST['code'];
  1037. * $img = new Securimage();
  1038. * if ($img->check($code) == true) {
  1039. * $captcha_valid = true;
  1040. * } else {
  1041. * $captcha_valid = false;
  1042. * }
  1043. *
  1044. * @return bool true if the given code was correct, false if not.
  1045. */
  1046. public function check($code)
  1047. {
  1048. $this->code_entered = $code;
  1049. $this->validate();
  1050. return $this->correct_code;
  1051. }
  1052. /**
  1053. * Get the time in seconds that it took to solve the captcha.
  1054. *
  1055. * @return int The time in seconds from when the code was created, to when it was solved
  1056. */
  1057. public function getTimeToSolve()
  1058. {
  1059. return $this->_timeToSolve;
  1060. }
  1061. /**
  1062. * Set the namespace for the captcha being stored in the session or database.
  1063. *
  1064. * Namespaces are useful when multiple captchas need to be displayed on a single page.
  1065. *
  1066. * @param string $namespace Namespace value, String consisting of characters "a-zA-Z0-9_-"
  1067. */
  1068. public function setNamespace($namespace)
  1069. {
  1070. $namespace = preg_replace('/[^a-z0-9-_]/i', '', $namespace);
  1071. $namespace = substr($namespace, 0, 64);
  1072. if (!empty($namespace)) {
  1073. $this->namespace = $namespace;
  1074. } else {
  1075. $this->namespace = 'default';
  1076. }
  1077. }
  1078. /**
  1079. * Return the code from the session or database (if configured). If none exists or was found, an empty string is returned.
  1080. *
  1081. * @param bool $array true to receive an array containing the code and properties, false to receive just the code.
  1082. * @param bool $returnExisting If true, and the class property *code* is set, it will be returned instead of getting the code from the session or database.
  1083. * @return array|string Return is an array if $array = true, otherwise a string containing the code
  1084. */
  1085. public function getCode($array = false, $returnExisting = false)
  1086. {
  1087. $code = array();
  1088. $time = 0;
  1089. $disp = 'error';
  1090. if ($returnExisting && strlen($this->code) > 0) {
  1091. if ($array) {
  1092. return array(
  1093. 'code' => $this->code,
  1094. 'display' => $this->code_display,
  1095. 'code_display' => $this->code_display,
  1096. 'time' => 0);
  1097. } else {
  1098. return $this->code;
  1099. }
  1100. }
  1101. if ($this->no_session != true) {
  1102. if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
  1103. trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
  1104. if ($this->isCodeExpired(
  1105. $_SESSION['securimage_code_ctime'][$this->namespace]) == false) {
  1106. $code['code'] = $_SESSION['securimage_code_value'][$this->namespace];
  1107. $code['time'] = $_SESSION['securimage_code_ctime'][$this->namespace];
  1108. $code['display'] = $_SESSION['securimage_code_disp'] [$this->namespace];
  1109. }
  1110. }
  1111. }
  1112. if (empty($code) && $this->use_database) {
  1113. // no code in session - may mean user has cookies turned off
  1114. $this->openDatabase();
  1115. $code = $this->getCodeFromDatabase();
  1116. if (!empty($code)) {
  1117. $code['display'] = $code['code_disp'];
  1118. unset($code['code_disp']);
  1119. }
  1120. } else { /* no code stored in session or sqlite database, validation will fail */ }
  1121. if ($array == true) {
  1122. return $code;
  1123. } else {
  1124. return $code['code'];
  1125. }
  1126. }
  1127. /**
  1128. * The main image drawing routing, responsible for constructing the entire image and serving it
  1129. */
  1130. protected function doImage()
  1131. {
  1132. if( ($this->use_transparent_text == true || $this->bgimg != '') && function_exists('imagecreatetruecolor')) {
  1133. $imagecreate = 'imagecreatetruecolor';
  1134. } else {
  1135. $imagecreate = 'imagecreate';
  1136. }
  1137. $this->im = $imagecreate($this->image_width, $this->image_height);
  1138. $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
  1139. $this->allocateColors();
  1140. imagepalettecopy($this->tmpimg, $this->im);
  1141. $this->setBackground();
  1142. $code = '';
  1143. if ($this->getCaptchaId(false) !== null) {
  1144. // a captcha Id was supplied
  1145. // check to see if a display_value for the captcha image was set
  1146. if (is_string($this->display_value) && strlen($this->display_value) > 0) {
  1147. $this->code_display = $this->display_value;
  1148. $this->code = ($this->case_sensitive) ?
  1149. $this->display_value :
  1150. strtolower($this->display_value);
  1151. $code = $this->code;
  1152. } else if ($this->openDatabase()) {
  1153. // no display_value, check the database for existing captchaId
  1154. $code = $this->getCodeFromDatabase();
  1155. // got back a result from the database with a valid code for captchaId
  1156. if (is_array($code)) {
  1157. $this->code = $code['code'];
  1158. $this->code_display = $code['code_disp'];
  1159. $code = $code['code'];
  1160. }
  1161. }
  1162. }
  1163. if ($code == '') {
  1164. // if the code was not set using display_value or was not found in
  1165. // the database, create a new code
  1166. $this->createCode();
  1167. }
  1168. if ($this->noise_level > 0) {
  1169. $this->drawNoise();
  1170. }
  1171. $this->drawWord();
  1172. if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
  1173. $this->distortedCopy();
  1174. }
  1175. if ($this->num_lines > 0) {
  1176. $this->drawLines();
  1177. }
  1178. if (trim($this->image_signature) != '') {
  1179. $this->addSignature();
  1180. }
  1181. }
  1182. /**
  1183. * Allocate the colors to be used for the image
  1184. */
  1185. protected function allocateColors()
  1186. {
  1187. // allocate bg color first for imagecreate
  1188. $this->gdbgcolor = imagecolorallocate($this->im,
  1189. $this->image_bg_color->r,
  1190. $this->image_bg_color->g,
  1191. $this->image_bg_color->b);
  1192. $alpha = intval($this->text_transparency_percentage / 100 * 127);
  1193. if ($this->use_transparent_text == true) {
  1194. $this->gdtextcolor = imagecolorallocatealpha($this->im,
  1195. $this->text_color->r,
  1196. $this->text_color->g,
  1197. $this->text_color->b,
  1198. $alpha);
  1199. $this->gdlinecolor = imagecolorallocatealpha($this->im,
  1200. $this->line_color->r,
  1201. $this->line_color->g,
  1202. $this->line_color->b,
  1203. $alpha);
  1204. $this->gdnoisecolor = imagecolorallocatealpha($this->im,
  1205. $this->noise_color->r,
  1206. $this->noise_color->g,
  1207. $this->noise_color->b,
  1208. $alpha);
  1209. } else {
  1210. $this->gdtextcolor = imagecolorallocate($this->im,
  1211. $this->text_color->r,
  1212. $this->text_color->g,
  1213. $this->text_color->b);
  1214. $this->gdlinecolor = imagecolorallocate($this->im,
  1215. $this->line_color->r,
  1216. $this->line_color->g,
  1217. $this->line_color->b);
  1218. $this->gdnoisecolor = imagecolorallocate($this->im,
  1219. $this->noise_color->r,
  1220. $this->noise_color->g,
  1221. $this->noise_color->b);
  1222. }
  1223. $this->gdsignaturecolor = imagecolorallocate($this->im,
  1224. $this->signature_color->r,
  1225. $this->signature_color->g,
  1226. $this->signature_color->b);
  1227. }
  1228. /**
  1229. * The the background color, or background image to be used
  1230. */
  1231. protected function setBackground()
  1232. {
  1233. // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
  1234. imagefilledrectangle($this->im, 0, 0,
  1235. $this->image_width, $this->image_height,
  1236. $this->gdbgcolor);
  1237. imagefilledrectangle($this->tmpimg, 0, 0,
  1238. $this->image_width * $this->iscale, $this->image_height * $this->iscale,
  1239. $this->gdbgcolor);
  1240. if ($this->bgimg == '') {
  1241. if ($this->background_directory != null &&
  1242. is_dir($this->background_directory) &&
  1243. is_readable($this->background_directory))
  1244. {
  1245. $img = $this->getBackgroundFromDirectory();
  1246. if ($img != false) {
  1247. $this->bgimg = $img;
  1248. }
  1249. }
  1250. }
  1251. if ($this->bgimg == '') {
  1252. return;
  1253. }
  1254. $dat = @getimagesize($this->bgimg);
  1255. if($dat == false) {
  1256. return;
  1257. }
  1258. switch($dat[2]) {
  1259. case 1: $newim = @imagecreatefromgif($this->bgimg); break;
  1260. case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
  1261. case 3: $newim = @imagecreatefrompng($this->bgimg); break;
  1262. default: return;
  1263. }
  1264. if(!$newim) return;
  1265. imagecopyresized($this->im, $newim, 0, 0, 0, 0,
  1266. $this->image_width, $this->image_height,
  1267. imagesx($newim), imagesy($newim));
  1268. }
  1269. /**
  1270. * Scan the directory for a background image to use
  1271. * @return string|bool
  1272. */
  1273. protected function getBackgroundFromDirectory()
  1274. {
  1275. $images = array();
  1276. if ( ($dh = opendir($this->background_directory)) !== false) {
  1277. while (($file = readdir($dh)) !== false) {
  1278. if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
  1279. }
  1280. closedir($dh);
  1281. if (sizeof($images) > 0) {
  1282. return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)];
  1283. }
  1284. }
  1285. return false;
  1286. }
  1287. /**
  1288. * This method generates a new captcha code.
  1289. *
  1290. * Generates a random captcha code based on *charset*, math problem, or captcha from the wordlist and saves the value to the session and/or database.
  1291. */
  1292. public function createCode()
  1293. {
  1294. $this->code = false;
  1295. switch($this->captcha_type) {
  1296. case self::SI_CAPTCHA_MATHEMATIC:
  1297. {
  1298. do {
  1299. $signs = array('+', '-', 'x');
  1300. $left = mt_rand(1, 10);
  1301. $right = mt_rand(1, 5);
  1302. $sign = $signs[mt_rand(0, 2)];
  1303. switch($sign) {
  1304. case 'x': $c = $left * $right; break;
  1305. case '-': $c = $left - $right; break;
  1306. default: $c = $left + $right; break;
  1307. }
  1308. } while ($c <= 0); // no negative #'s or 0
  1309. $this->code = "$c";
  1310. $this->code_display = "$left $sign $right";
  1311. break;
  1312. }
  1313. case self::SI_CAPTCHA_WORDS:
  1314. $words = $this->readCodeFromFile(2);
  1315. $this->code = implode(' ', $words);
  1316. $this->code_display = $this->code;
  1317. break;
  1318. default:
  1319. {
  1320. if ($this->use_wordlist && is_readable($this->wordlist_file)) {
  1321. $this->code = $this->readCodeFromFile();
  1322. }
  1323. if ($this->code == false) {
  1324. $this->code = $this->generateCode($this->code_length);
  1325. }
  1326. $this->code_display = $this->code;
  1327. $this->code = ($this->case_sensitive) ? $this->code : strtolower($this->code);
  1328. } // default
  1329. }
  1330. $this->saveData();
  1331. }
  1332. /**
  1333. * Draws the captcha code on the image
  1334. */
  1335. protected function drawWord()
  1336. {
  1337. $width2 = $this->image_width * $this->iscale;
  1338. $height2 = $this->image_height * $this->iscale;
  1339. $ratio = ($this->font_ratio) ? $this->font_ratio : 0.4;
  1340. if ((float)$ratio < 0.1 || (float)$ratio >= 1) {
  1341. $ratio = 0.4;
  1342. }
  1343. if (!is_readable($this->ttf_file)) {
  1344. imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
  1345. } else {
  1346. if ($this->perturbation > 0) {
  1347. $font_size = $height2 * $ratio;
  1348. $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
  1349. $tx = $bb[4] - $bb[0];
  1350. $ty = $bb[5] - $bb[1];
  1351. $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
  1352. $y = round($height2 / 2 - $ty / 2 - $bb[1]);
  1353. imagettftext($this->tmpimg, $font_size, 0, (int)$x, (int)$y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
  1354. } else {
  1355. $font_size = $this->image_height * $ratio;
  1356. $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
  1357. $tx = $bb[4] - $bb[0];
  1358. $ty = $bb[5] - $bb[1];
  1359. $x = floor($this->image_width / 2 - $tx / 2 - $bb[0]);
  1360. $y = round($this->image_height / 2 - $ty / 2 - $bb[1]);
  1361. imagettftext($this->im, $font_size, 0, (int)$x, (int)$y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
  1362. }
  1363. }
  1364. // DEBUG
  1365. //$this->im = $this->tmpimg;
  1366. //$this->output();
  1367. }
  1368. /**
  1369. * Copies the captcha image to the final image with distortion applied
  1370. */
  1371. protected function distortedCopy()
  1372. {
  1373. $numpoles = 3; // distortion factor
  1374. // make array of poles AKA attractor points
  1375. for ($i = 0; $i < $numpoles; ++ $i) {
  1376. $px[$i] = mt_rand($this->image_width * 0.2, $this->image_width * 0.8);
  1377. $py[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
  1378. $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
  1379. $tmp = ((- $this->frand()) * 0.15) - .15;
  1380. $amp[$i] = $this->perturbation * $tmp;
  1381. }
  1382. $bgCol = imagecolorat($this->tmpimg, 0, 0);
  1383. $width2 = $this->iscale * $this->image_width;
  1384. $height2 = $this->iscale * $this->image_height;
  1385. imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
  1386. // loop over $img pixels, take pixels from $tmpimg with distortion field
  1387. for ($ix = 0; $ix < $this->image_width; ++ $ix) {
  1388. for ($iy = 0; $iy < $this->image_height; ++ $iy) {
  1389. $x = $ix;
  1390. $y = $iy;
  1391. for ($i = 0; $i < $numpoles; ++ $i) {
  1392. $dx = $ix - $px[$i];
  1393. $dy = $iy - $py[$i];
  1394. if ($dx == 0 && $dy == 0) {
  1395. continue;
  1396. }
  1397. $r = sqrt($dx * $dx + $dy * $dy);
  1398. if ($r > $rad[$i]) {
  1399. continue;
  1400. }
  1401. $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
  1402. $x += $dx * $rscale;
  1403. $y += $dy * $rscale;
  1404. }
  1405. $c = $bgCol;
  1406. $x *= $this->iscale;
  1407. $y *= $this->iscale;
  1408. if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
  1409. $c = imagecolorat($this->tmpimg, $x, $y);
  1410. }
  1411. if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
  1412. imagesetpixel($this->im, $ix, $iy, $c);
  1413. }
  1414. }
  1415. }
  1416. }
  1417. /**
  1418. * Draws distorted lines on the image
  1419. */
  1420. protected function drawLines()
  1421. {
  1422. for ($line = 0; $line < $this->num_lines; ++ $line) {
  1423. $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
  1424. $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
  1425. $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
  1426. $theta = ($this->frand() - 0.5) * M_PI * 0.7;
  1427. $w = $this->image_width;
  1428. $len = mt_rand($w * 0.4, $w * 0.7);
  1429. $lwid = mt_rand(0, 2);
  1430. $k = $this->frand() * 0.6 + 0.2;
  1431. $k = $k * $k * 0.5;
  1432. $phi = $this->frand() * 6.28;
  1433. $step = 0.5;
  1434. $dx = $step * cos($theta);
  1435. $dy = $step * sin($theta);
  1436. $n = $len / $step;
  1437. $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
  1438. $x0 = $x - 0.5 * $len * cos($theta);
  1439. $y0 = $y - 0.5 * $len * sin($theta);
  1440. $ldx = round(- $dy * $lwid);
  1441. $ldy = round($dx * $lwid);
  1442. for ($i = 0; $i < $n; ++ $i) {
  1443. $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
  1444. $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
  1445. imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
  1446. }
  1447. }
  1448. }
  1449. /**
  1450. * Draws random noise on the image
  1451. */
  1452. protected function drawNoise()
  1453. {
  1454. if ($this->noise_level > 10) {
  1455. $noise_level = 10;
  1456. } else {
  1457. $noise_level = $this->noise_level;
  1458. }
  1459. $t0 = microtime(true);
  1460. $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale
  1461. $points = $this->image_width * $this->image_height * $this->iscale;
  1462. $height = $this->image_height * $this->iscale;
  1463. $width = $this->image_width * $this->iscale;
  1464. for ($i = 0; $i < $noise_level; ++$i) {
  1465. $x = mt_rand(10, $width);
  1466. $y = mt_rand(10, $height);
  1467. $size = mt_rand(7, 10);
  1468. if ($x - $size <= 0 && $y - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy
  1469. imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE);
  1470. }
  1471. $t1 = microtime(true);
  1472. $t = $t1 - $t0;
  1473. /*
  1474. // DEBUG
  1475. imagestring($this->tmpimg, 5, 25, 30, "$t", $this->gdnoisecolor);
  1476. header('content-type: image/png');
  1477. imagepng($this->tmpimg);
  1478. exit;
  1479. */
  1480. }
  1481. /**
  1482. * Print signature text on image
  1483. */
  1484. protected function addSignature()
  1485. {
  1486. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1487. $textlen = $bbox[2] - $bbox[0];
  1488. $x = $this->image_width - $textlen - 5;
  1489. $y = $this->image_height - 3;
  1490. imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
  1491. }
  1492. /**
  1493. * Sends the appropriate image and cache headers and outputs image to the browser
  1494. */
  1495. protected function output()
  1496. {
  1497. if ($this->canSendHeaders() || $this->send_headers == false) {
  1498. if ($this->send_headers) {
  1499. // only send the content-type headers if no headers have been output
  1500. // this will ease debugging on misconfigured servers where warnings
  1501. // may have been output which break the image and prevent easily viewing
  1502. // source to see the error.
  1503. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  1504. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
  1505. header("Cache-Control: no-store, no-cache, must-revalidate");
  1506. header("Cache-Control: post-check=0, pre-check=0", false);
  1507. header("Pragma: no-cache");
  1508. }
  1509. switch ($this->image_type) {
  1510. case self::SI_IMAGE_JPEG:
  1511. if ($this->send_headers) header("Content-Type: image/jpeg");
  1512. imagejpeg($this->im, null, 90);
  1513. break;
  1514. case self::SI_IMAGE_GIF:
  1515. if ($this->send_headers) header("Content-Type: image/gif");
  1516. imagegif($this->im);
  1517. break;
  1518. default:
  1519. if ($this->send_headers) header("Content-Type: image/png");
  1520. imagepng($this->im);
  1521. break;
  1522. }
  1523. } else {
  1524. echo '<hr /><strong>'
  1525. .'Failed to generate captcha image, content has already been '
  1526. .'output.<br />This is most likely due to misconfiguration or '
  1527. .'a PHP error was sent to the browser.</strong>';
  1528. }
  1529. imagedestroy($this->im);
  1530. restore_error_handler();
  1531. if (!$this->no_exit) exit;
  1532. }
  1533. /**
  1534. * Generates an audio captcha in WAV format
  1535. *
  1536. * @return string The audio representation of the captcha in Wav format
  1537. */
  1538. protected function getAudibleCode()
  1539. {
  1540. $letters = array();
  1541. $code = $this->getCode(true, true);
  1542. if (empty($code) || $code['code'] == '') {
  1543. if (strlen($this->display_value) > 0) {
  1544. $code = array('code' => $this->display_value, 'display' => $this->display_value);
  1545. } else {
  1546. $this->createCode();
  1547. $code = $this->getCode(true);
  1548. }
  1549. }
  1550. if (empty($code)) {
  1551. $error = 'Failed to get audible code (are database settings correct?). Check the error log for details';
  1552. trigger_error($error, E_USER_WARNING);
  1553. throw new Exception($error);
  1554. }
  1555. if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
  1556. $math = true;
  1557. $left = $eq[1];
  1558. $sign = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
  1559. $right = $eq[3];
  1560. $letters = array($left, $sign, $right);
  1561. } else {
  1562. $math = false;
  1563. $length = strlen($code['display']);
  1564. for($i = 0; $i < $length; ++$i) {
  1565. $letter = $code['display']{$i};
  1566. $letters[] = $letter;
  1567. }
  1568. }
  1569. try {
  1570. return $this->generateWAV($letters);
  1571. } catch(Exception $ex) {
  1572. throw $ex;
  1573. }
  1574. }
  1575. /**
  1576. * Gets a captcha code from a file containing a list of words.
  1577. *
  1578. * Seek to a random offset in the file and reads a block of data and returns a line from the file.
  1579. *
  1580. * @param int $numWords Number of words (lines) to read from the file
  1581. * @return string|array|bool Returns a string if only one word is to be read, or an array of words
  1582. */
  1583. protected function readCodeFromFile($numWords = 1)
  1584. {
  1585. $strpos_func = 'strpos';
  1586. $strlen_func = 'strlen';
  1587. $substr_func = 'substr';
  1588. $strtolower_func = 'strtolower';
  1589. $mb_support = false;
  1590. if (!empty($this->wordlist_file_encoding)) {
  1591. if (!extension_loaded('mbstring')) {
  1592. trigger_error("wordlist_file_encoding option set, but PHP does not have mbstring support", E_USER_WARNING);
  1593. return false;
  1594. }
  1595. // emits PHP warning if not supported
  1596. $mb_support = mb_internal_encoding($this->wordlist_file_encoding);
  1597. if (!$mb_support) {
  1598. return false;
  1599. }
  1600. $strpos_func = 'mb_strpos';
  1601. $strlen_func = 'mb_strlen';
  1602. $substr_func = 'mb_substr';
  1603. $strtolower_func = 'mb_strtolower';
  1604. }
  1605. $fp = fopen($this->wordlist_file, 'rb');
  1606. if (!$fp) return false;
  1607. $fsize = filesize($this->wordlist_file);
  1608. if ($fsize < 128) return false; // too small of a list to be effective
  1609. if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1;
  1610. $words = array();
  1611. $i = 0;
  1612. do {
  1613. fseek($fp, mt_rand(0, $fsize - 128), SEEK_SET); // seek to a random position of file from 0 to filesize-128
  1614. $data = fread($fp, 128); // read a chunk from our random position
  1615. if ($mb_support !== false) {
  1616. $data = mb_ereg_replace("\r?\n", "\n", $data);
  1617. } else {
  1618. $data = preg_replace("/\r?\n/", "\n", $data);
  1619. }
  1620. $start = @$strpos_func($data, "\n", mt_rand(0, 56)) + 1; // random start position
  1621. $end = @$strpos_func($data, "\n", $start); // find end of word
  1622. if ($start === false) {
  1623. // picked start position at end of file
  1624. continue;
  1625. } else if ($end === false) {
  1626. $end = $strlen_func($data);
  1627. }
  1628. $word = $strtolower_func($substr_func($data, $start, $end - $start)); // return a line of the file
  1629. if ($mb_support) {
  1630. // convert to UTF-8 for imagettftext
  1631. $word = mb_convert_encoding($word, 'UTF-8', $this->wordlist_file_encoding);
  1632. }
  1633. $words[] = $word;
  1634. } while (++$i < $numWords);
  1635. fclose($fp);
  1636. if ($numWords < 2) {
  1637. return $words[0];
  1638. } else {
  1639. return $words;
  1640. }
  1641. }
  1642. /**
  1643. * Generates a random captcha code from the set character set
  1644. *
  1645. * @see Securimage::$charset Charset option
  1646. * @return string A randomly generated CAPTCHA code
  1647. */
  1648. protected function generateCode()
  1649. {
  1650. $code = '';
  1651. if (function_exists('mb_strlen')) {
  1652. for($i = 1, $cslen = mb_strlen($this->charset, 'UTF-8'); $i <= $this->code_length; ++$i) {
  1653. $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8');
  1654. }
  1655. } else {
  1656. for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) {
  1657. $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1);
  1658. }
  1659. }
  1660. return $code;
  1661. }
  1662. /**
  1663. * Validate a code supplied by the user
  1664. *
  1665. * Checks the entered code against the value stored in the session and/or database (if configured). Handles case sensitivity.
  1666. * Also removes the code from session/database if the code was entered correctly to prevent re-use attack.
  1667. *
  1668. * This function does not return a value.
  1669. *
  1670. * @see Securimage::$correct_code 'correct_code' property
  1671. */
  1672. protected function validate()
  1673. {
  1674. if (!is_string($this->code) || strlen($this->code) == 0) {
  1675. $code = $this->getCode(true);
  1676. // returns stored code, or an empty string if no stored code was found
  1677. // checks the session and database if enabled
  1678. } else {
  1679. $code = $this->code;
  1680. }
  1681. if (is_array($code)) {
  1682. if (!empty($code)) {
  1683. $ctime = $code['time'];
  1684. $code = $code['code'];
  1685. $this->_timeToSolve = time() - $ctime;
  1686. } else {
  1687. $code = '';
  1688. }
  1689. }
  1690. if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
  1691. // case sensitive was set from securimage_show.php but not in class
  1692. // the code saved in the session has capitals so set case sensitive to true
  1693. $this->case_sensitive = true;
  1694. }
  1695. $code_entered = trim( (($this->case_sensitive) ? $this->code_entered
  1696. : strtolower($this->code_entered))
  1697. );
  1698. $this->correct_code = false;
  1699. if ($code != '') {
  1700. if (strpos($code, ' ') !== false) {
  1701. // for multi word captchas, remove more than once space from input
  1702. $code_entered = preg_replace('/\s+/', ' ', $code_entered);
  1703. $code_entered = strtolower($code_entered);
  1704. }
  1705. if ((string)$code === (string)$code_entered) {
  1706. $this->correct_code = true;
  1707. if ($this->no_session != true) {
  1708. $_SESSION['securimage_code_disp'] [$this->namespace] = '';
  1709. $_SESSION['securimage_code_value'][$this->namespace] = '';
  1710. $_SESSION['securimage_code_ctime'][$this->namespace] = '';
  1711. $_SESSION['securimage_code_audio'][$this->namespace] = '';
  1712. }
  1713. $this->clearCodeFromDatabase();
  1714. }
  1715. }
  1716. }
  1717. /**
  1718. * Save CAPTCHA data to session and database (if configured)
  1719. */
  1720. protected function saveData()
  1721. {
  1722. if ($this->no_session != true) {
  1723. if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
  1724. // fix for migration from v2 - v3
  1725. unset($_SESSION['securimage_code_value']);
  1726. unset($_SESSION['securimage_code_ctime']);
  1727. }
  1728. $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
  1729. $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
  1730. $_SESSION['securimage_code_ctime'][$this->namespace] = time();
  1731. $_SESSION['securimage_code_audio'][$this->namespace] = null; // clear previous audio, if set
  1732. }
  1733. if ($this->use_database) {
  1734. $this->saveCodeToDatabase();
  1735. }
  1736. }
  1737. /**
  1738. * Checks to see if the captcha code has expired and can no longer be used.
  1739. *
  1740. * @see Securimage::$expiry_time expiry_time
  1741. * @param int $creation_time The Unix timestamp of when the captcha code was created
  1742. * @return bool true if the code is expired, false if it is still valid
  1743. */
  1744. protected function isCodeExpired($creation_time)
  1745. {
  1746. $expired = true;
  1747. if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
  1748. $expired = false;
  1749. } else if (time() - $creation_time < $this->expiry_time) {
  1750. $expired = false;
  1751. }
  1752. return $expired;
  1753. }
  1754. /**
  1755. * Checks to see if headers can be sent and if any error has been output
  1756. * to the browser
  1757. *
  1758. * @return bool true if it is safe to send headers, false if not
  1759. */
  1760. protected function canSendHeaders()
  1761. {
  1762. if (headers_sent()) {
  1763. // output has been flushed and headers have already been sent
  1764. return false;
  1765. } else if (strlen((string)ob_get_contents()) > 0) {
  1766. // headers haven't been sent, but there is data in the buffer that will break image and audio data
  1767. return false;
  1768. }
  1769. return true;
  1770. }
  1771. /**
  1772. * Return a random float between 0 and 0.9999
  1773. *
  1774. * @return float Random float between 0 and 0.9999
  1775. */
  1776. function frand()
  1777. {
  1778. return 0.0001 * mt_rand(0,9999);
  1779. }
  1780. /**
  1781. * Convert an html color code to a Securimage_Color
  1782. * @param string $color
  1783. * @param Securimage_Color|string $default The defalt color to use if $color is invalid
  1784. */
  1785. protected function initColor($color, $default)
  1786. {
  1787. if ($color == null) {
  1788. return new Securimage_Color($default);
  1789. } else if (is_string($color)) {
  1790. try {
  1791. return new Securimage_Color($color);
  1792. } catch(Exception $e) {
  1793. return new Securimage_Color($default);
  1794. }
  1795. } else if (is_array($color) && sizeof($color) == 3) {
  1796. return new Securimage_Color($color[0], $color[1], $color[2]);
  1797. } else {
  1798. return new Securimage_Color($default);
  1799. }
  1800. }
  1801. /**
  1802. * The error handling function used when outputting captcha image or audio.
  1803. *
  1804. * This error handler helps determine if any errors raised would
  1805. * prevent captcha image or audio from displaying. If they have
  1806. * no effect on the output buffer or headers, true is returned so
  1807. * the script can continue processing.
  1808. *
  1809. * See https://github.com/dapphp/securimage/issues/15
  1810. *
  1811. * @param int $errno PHP error number
  1812. * @param string $errstr String description of the error
  1813. * @param string $errfile File error occurred in
  1814. * @param int $errline Line the error occurred on in file
  1815. * @param array $errcontext Additional context information
  1816. * @return boolean true if the error was handled, false if PHP should handle the error
  1817. */
  1818. public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
  1819. {
  1820. // get the current error reporting level
  1821. $level = error_reporting();
  1822. // if error was supressed or $errno not set in current error level
  1823. if ($level == 0 || ($level & $errno) == 0) {
  1824. return true;
  1825. }
  1826. return false;
  1827. }
  1828. }
  1829. /**
  1830. * Color object for Securimage CAPTCHA
  1831. *
  1832. * @since 2.0
  1833. * @package Securimage
  1834. * @subpackage classes
  1835. *
  1836. */
  1837. class Securimage_Color
  1838. {
  1839. /**
  1840. * Red value (0-255)
  1841. * @var int
  1842. */
  1843. public $r;
  1844. /**
  1845. * Gree value (0-255)
  1846. * @var int
  1847. */
  1848. public $g;
  1849. /**
  1850. * Blue value (0-255)
  1851. * @var int
  1852. */
  1853. public $b;
  1854. /**
  1855. * Create a new Securimage_Color object.
  1856. *
  1857. * Constructor expects 1 or 3 arguments.
  1858. *
  1859. * When passing a single argument, specify the color using HTML hex format.
  1860. *
  1861. * When passing 3 arguments, specify each RGB component (from 0-255)
  1862. * individually.
  1863. *
  1864. * Examples:
  1865. *
  1866. * $color = new Securimage_Color('#0080FF');
  1867. * $color = new Securimage_Color(0, 128, 255);
  1868. *
  1869. * @param string $color The html color code to use
  1870. * @throws Exception If any color value is not valid
  1871. */
  1872. public function __construct($color = '#ffffff')
  1873. {
  1874. $args = func_get_args();
  1875. if (sizeof($args) == 0) {
  1876. $this->r = 255;
  1877. $this->g = 255;
  1878. $this->b = 255;
  1879. } else if (sizeof($args) == 1) {
  1880. // set based on html code
  1881. if (substr($color, 0, 1) == '#') {
  1882. $color = substr($color, 1);
  1883. }
  1884. if (strlen($color) != 3 && strlen($color) != 6) {
  1885. throw new InvalidArgumentException(
  1886. 'Invalid HTML color code passed to Securimage_Color'
  1887. );
  1888. }
  1889. $this->constructHTML($color);
  1890. } else if (sizeof($args) == 3) {
  1891. $this->constructRGB($args[0], $args[1], $args[2]);
  1892. } else {
  1893. throw new InvalidArgumentException(
  1894. 'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
  1895. );
  1896. }
  1897. }
  1898. /**
  1899. * Construct from an rgb triplet
  1900. *
  1901. * @param int $red The red component, 0-255
  1902. * @param int $green The green component, 0-255
  1903. * @param int $blue The blue component, 0-255
  1904. */
  1905. protected function constructRGB($red, $green, $blue)
  1906. {
  1907. if ($red < 0) $red = 0;
  1908. if ($red > 255) $red = 255;
  1909. if ($green < 0) $green = 0;
  1910. if ($green > 255) $green = 255;
  1911. if ($blue < 0) $blue = 0;
  1912. if ($blue > 255) $blue = 255;
  1913. $this->r = $red;
  1914. $this->g = $green;
  1915. $this->b = $blue;
  1916. }
  1917. /**
  1918. * Construct from an html hex color code
  1919. *
  1920. * @param string $color
  1921. */
  1922. protected function constructHTML($color)
  1923. {
  1924. if (strlen($color) == 3) {
  1925. $red = str_repeat(substr($color, 0, 1), 2);
  1926. $green = str_repeat(substr($color, 1, 1), 2);
  1927. $blue = str_repeat(substr($color, 2, 1), 2);
  1928. } else {
  1929. $red = substr($color, 0, 2);
  1930. $green = substr($color, 2, 2);
  1931. $blue = substr($color, 4, 2);
  1932. }
  1933. $this->r = hexdec($red);
  1934. $this->g = hexdec($green);
  1935. $this->b = hexdec($blue);
  1936. }
  1937. }