diff --git a/application/config/autoload.php b/application/config/autoload.php
index f861094e4..4be764662 100755
--- a/application/config/autoload.php
+++ b/application/config/autoload.php
@@ -58,9 +58,14 @@ $autoload['packages'] = array();
|
| $autoload['libraries'] = array('user_agent' => 'ua');
*/
+
//$autoload['libraries'] = array();
$autoload['libraries'] = array('session', 'FHC_Auth');
+//$autoload['libraries'] = array();
+$autoload['libraries'] = array('session');
+
+
/*
| -------------------------------------------------------------------
| Auto-load Drivers
diff --git a/application/config/rest.php b/application/config/rest.php
index 5f44dc692..f1914a1b3 100644
--- a/application/config/rest.php
+++ b/application/config/rest.php
@@ -301,7 +301,7 @@ $config['rest_keys_table'] = 'ci_apikey';
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
*/
-$config['rest_enable_keys'] = TRUE;
+$config['rest_enable_keys'] = FALSE;
/*
|--------------------------------------------------------------------------
diff --git a/application/controllers/api/v1/Kontakt.php b/application/controllers/api/v1/Kontakt.php
new file mode 100644
index 000000000..68c015804
--- /dev/null
+++ b/application/controllers/api/v1/Kontakt.php
@@ -0,0 +1,80 @@
+load->model('kontakt/Kontakt_model');
+ }
+
+ public function kontaktPerson_get()
+ {
+ $result = $this->Kontakt_model->getKontaktPerson($this->get("person_id"));
+ if($result != FALSE)
+ {
+ $httpstatus = REST_Controller::HTTP_OK;
+ $payload = [
+ 'success' => true,
+ 'message' => 'Kontakt found.'
+ ];
+ $payload['data'] = $result;
+ }
+ else
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Could not find Kontakt.'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+
+ $this->response($payload, $httpstatus);
+ }
+
+ public function kontakt_post()
+ {
+ $result = $this->Kontakt_model->saveKontakt($this->post());
+ if($result != FALSE)
+ {
+ $httpstatus = REST_Controller::HTTP_OK;
+ $payload = [
+ 'success' => true,
+ 'message' => 'Kontakt saved.'
+ ];
+ $payload['data'] = $result;
+ }
+ else
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Could not save Kontakt.'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+
+ $this->response($payload, $httpstatus);
+ }
+
+}
diff --git a/application/controllers/api/v1/Person.php b/application/controllers/api/v1/Person.php
index 73dbda67e..0d1d120bc 100644
--- a/application/controllers/api/v1/Person.php
+++ b/application/controllers/api/v1/Person.php
@@ -11,37 +11,46 @@
* @since Version 1.0
* @filesource
*/
-
// ------------------------------------------------------------------------
defined('BASEPATH') || exit('No direct script access allowed');
class Person extends APIv1_Controller
{
- //public $session;
+
+ //public $session;
/**
* Person API constructor.
*/
public function __construct()
{
- parent::__construct();
+ parent::__construct();
- $this->load->model('person/person_model');
+ $this->load->model('person/person_model');
}
public function person_get()
{
- //if (!$this->session_model->validate($this->get('session_id'), $this->get('device_id')))
- // $this->response(array(['success' => false, 'message' => 'access denied']), REST_Controller::HTTP_UNAUTHORIZED);
+ //if (!$this->session_model->validate($this->get('session_id'), $this->get('device_id')))
+ // $this->response(array(['success' => false, 'message' => 'access denied']), REST_Controller::HTTP_UNAUTHORIZED);
- $code = $this->get('code');
-
- if (!is_null($code))
- $result = $this->person_model->getPersonByCode($code);
- else
- $result = $this->person_model->getPerson();
- // var_dump($result[0]);
+ $code = $this->get('code');
+ $person_id = $this->get('person_id');
+ if (!is_null($code))
+ {
+ $result = $this->person_model->getPersonByCode($code);
+ }
+ elseif (!is_null($person_id))
+ {
+ $result = $this->person_model->getPerson($person_id);
+ }
+ else
+ {
+ $result = $this->person_model->getPerson();
+ }
+
+<<<<<<< HEAD
if ($result['err'])
{
$payload = [
@@ -60,8 +69,114 @@ class Person extends APIv1_Controller
$payload['data'] = $result;
$httpstatus = REST_Controller::HTTP_OK;
}
+=======
+ if (empty($result))
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Person not found'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+ else
+ {
+ // return all available persons
+ $payload = [
+ 'success' => true,
+ 'message' => 'Persons found'
+ ];
+ $payload['data'] = $result;
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+>>>>>>> 97ddc838a8c9e707d3bf5a6a700301252d2f5ed8
- // Set the response and exit
- $this->response($payload, $httpstatus);
+ // Set the response and exit
+ $this->response($payload, $httpstatus);
}
+
+ public function person_post()
+ {
+ $result = $this->person_model->savePerson($this->post());
+ if($result != FALSE)
+ {
+ $httpstatus = REST_Controller::HTTP_OK;
+ $payload = [
+ 'success' => true,
+ 'message' => 'Person saved.'
+ ];
+ $payload['data'] = $result;
+ }
+ else
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Could not save person.'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+
+ $this->response($payload, $httpstatus);
+ }
+
+ public function personUpdate_post()
+ {
+ $result = $this->person_model->updatePerson($this->post());
+ if($result != FALSE)
+ {
+ $httpstatus = REST_Controller::HTTP_OK;
+ $payload = [
+ 'success' => true,
+ 'message' => 'Person updated.'
+ ];
+ $payload['data'] = $result;
+ }
+ else
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Could not update person.'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+//
+ $this->response($payload, $httpstatus);
+ }
+
+ public function checkBewerbung_get()
+ {
+ $result = $this->person_model->checkBewerbung($this->get("email"),$this->get("studiensemester_kurzbz"));
+ $httpstatus = REST_Controller::HTTP_OK;
+ $payload = [
+ 'success' => true,
+ 'message' => 'Bewerbung exists.'
+ ];
+ $payload['data'] = $result;
+
+ $this->response($payload, $httpstatus);
+ }
+
+ public function checkZugangscodePerson_get()
+ {
+ $result = $this->person_model->checkZugangscodePerson($this->get("code"));
+ $httpstatus = REST_Controller::HTTP_OK;
+ if(!empty($result))
+ {
+ $payload = [
+ 'success' => true,
+ 'message' => 'Zugangscode exists.'
+ ];
+ $payload['data'] = $result;
+ }
+ else
+ {
+ $payload = [
+ 'success' => false,
+ 'message' => 'Zugangscode does not exist.'
+ ];
+ $httpstatus = REST_Controller::HTTP_OK;
+ }
+
+ $this->response($payload, $httpstatus);
+ }
+
}
diff --git a/application/controllers/kontakt/Kontakt.php b/application/controllers/kontakt/Kontakt.php
new file mode 100644
index 000000000..dc6ec2e8d
--- /dev/null
+++ b/application/controllers/kontakt/Kontakt.php
@@ -0,0 +1,32 @@
+load->model('kontakt/kontakt_model');
+ }
+
+ public function index()
+ {
+ $data['person'] = $this->person_model->getPersonen();
+ $data['title'] = 'Personen Archiv';
+
+ $this->load->view('templates/header', $data);
+ $this->load->view('kontakt/index', $data);
+ $this->load->view('templates/footer');
+ }
+
+ public function view($slug = null)
+ {
+ $data['person_item'] = $this->person_model->getPersonen($slug);
+ if (empty($data['person_item']))
+ show_404();
+
+ $data['title'] = $data['person_item']->titelpre;
+
+ $this->load->view('templates/header', $data);
+ $this->load->view('kontakt/view', $data);
+ $this->load->view('templates/footer');
+ }
+}
diff --git a/application/models/kontakt/Kontakt_model.php b/application/models/kontakt/Kontakt_model.php
new file mode 100644
index 000000000..54a987370
--- /dev/null
+++ b/application/models/kontakt/Kontakt_model.php
@@ -0,0 +1,47 @@
+dbTable = 'public.tbl_kontakt';
+ }
+
+ public function saveKontakt($kontakt)
+ {
+ //TODO check berechtigung
+// if ($this->fhc_db_acl->bb->isBerechtigt('person', 'sui'))
+// {
+ $data = array(
+ "person_id"=>$kontakt["person_id"],
+ "kontakttyp"=>$kontakt["kontakttyp"],
+ "kontakt"=>$kontakt["kontakt"],
+ "insertvon"=>$kontakt["insertvon"],
+ "insertamum"=>date('Y-m-d H:i:s')
+ );
+ if($this->db->insert("public.tbl_kontakt", $data)){
+ return $this->db->insert_id();
+ }
+ else
+ {
+ return false;
+ }
+// }
+// else
+// {
+// return "Nicht berechtigt";
+// }
+ }
+
+ public function getKontaktPerson($person_id)
+ {
+ $this->db->select("*")
+ ->from("public.tbl_kontakt k")
+ ->where("k.person_id", $person_id);
+
+ return $this->db->get()->result_array();
+ }
+
+}
diff --git a/application/models/person/Person_model.php b/application/models/person/Person_model.php
index 7b5054d06..e8c987fc0 100644
--- a/application/models/person/Person_model.php
+++ b/application/models/person/Person_model.php
@@ -1,24 +1,36 @@
dbTable = 'public.tbl_person';
+ }
+
+ public function getPerson($person_id = null)
+ {
+ if (is_null($person_id))
{
- parent::__construct($uid);
- $this->dbTable='public.tbl_person';
+ $query = $this->db->get_where('public.tbl_person', array());
+ return $query->result_object();
}
- public function getPerson($person_id = null)
+ $query = $this->db->get_where('public.tbl_person', array('person_id' => $person_id));
+ return $query->row_object();
+ }
+
+ public function getPersonByCode($code)
+ {
+ if ($this->fhc_db_acl->bb->isBerechtigt('person', 's'))
{
- if (is_null($person_id))
- {
- $query = $this->db->get_where('public.tbl_person', array('vorname' => 'Christian'));
- return $query->result_object();
- }
-
- $query = $this->db->get_where('public.tbl_person', array('person_id' => $person_id));
- return $query->row_object();
+ $query = $this->db->get_where('public.tbl_person', array('zugangscode' => $code));
+ return $query->result_object();
}
+ }
+<<<<<<< HEAD
/**
* Laedt Personendaten einer Person mittels Code
* @param string $code DB-Attr: tbl_benutzer.zugangscode .
@@ -36,22 +48,117 @@ class Person_model extends DB_Model
return $this->_general_error($this->fhc_db_acl->bb->errormsg);
//return false;
}
- }
-
- /**
- * Laedt Personendaten eine BenutzerUID
- * @param string $uid DB-Attr: tbl_benutzer.uid .
- * @return bool
- */
- public function getPersonFromBenutzerUID($uid)
+=======
+ /**
+ * Laedt Personendaten eine BenutzerUID
+ * @param string $uid DB-Attr: tbl_benutzer.uid .
+ * @return bool
+ */
+ public function getPersonFromBenutzerUID($uid)
+ {
+ if (!$this->fhc_db_acl->bb->isBerechtigt('person', 's'))
{
-
- if (!$this->fhc_db_acl->bb->isBerechtigt('person','s'))
- {
- $this->db->select('tbl_person.*');
- $this->db->from('public.tbl_person JOIN public.tbl_benutzer USING (person_id)');
- $query = $this->db->get_where(null, array('uid' => $uid));
- return $query->result_object();
- }
+ $this->db->select('tbl_person.*');
+ $this->db->from('public.tbl_person JOIN public.tbl_benutzer USING (person_id)');
+ $query = $this->db->get_where(null, array('uid' => $uid));
+ return $query->result_object();
+>>>>>>> 97ddc838a8c9e707d3bf5a6a700301252d2f5ed8
}
+ }
+
+ public function savePerson($person)
+ {
+ //TODO check berechtigung
+// if ($this->fhc_db_acl->bb->isBerechtigt('person', 'sui'))
+// {
+ $data = array(
+ "vorname"=>$person["vorname"],
+ "nachname"=>$person["nachname"],
+ "gebdatum"=>$person["gebdatum"],
+ "aktiv" => true,
+ "zugangscode"=>$person["zugangscode"],
+ "insertamum"=>date('Y-m-d H:i:s'),
+ "insertvon"=>$person["insertvon"],
+ );
+ if($this->db->insert("public.tbl_person", $data)){
+ return $this->db->insert_id();
+ }
+ else
+ {
+ return false;
+ }
+// }
+// else
+// {
+// return "Nicht berechtigt";
+// }
+ }
+
+ public function checkBewerbung($email, $studiensemester_kurzbz=NULL)
+ {
+ $this->db->distinct();
+
+ if(is_null($studiensemester_kurzbz))
+ {
+ $this->db->select("p.person_id, p.zugangscode, p.insertamum")
+ ->from("public.tbl_person p")
+ ->join("public.tbl_kontakt k", "p.person_id=k.person_id")
+ ->join("public.tbl_benutzer b", "p.person_id=b.person_id", "left")
+ ->where("k.kontakttyp", 'email')
+ ->where("(kontakt='".$email."'".
+ " OR alias ||'@technikum-wien.at'='".$email."'".
+ " OR uid ||'@technikum-wien.at'='".$email."')")
+ ->order_by("p.insertamum", "DESC")
+ ->limit(1)
+ ;
+ }
+ else
+ {
+ $this->db->select("p.person_id,p.zugangscode,p.insertamum")
+ ->from("public.tbl_person p")
+ ->join("public.tbl_kontakt k", "p.person_id=k.person_id")
+ ->join("public.tbl_benutzer b", "p.person_id=b.person_id", "left")
+ ->join("public.tbl_prestudent ps", "p.person_id=ps.person_id")
+ ->join("public.tbl_prestudentstatus pst", "pst.prestudent_id=ps.prestudent_id")
+ ->where("k.kontakttyp", 'email')
+ ->where("(kontakt='".$email."'".
+ " OR alias ||'@technikum-wien.at'='".$email."'".
+ " OR uid ||'@technikum-wien.at'='".$email."')")
+ ->where("studiensemester_kurzbz='".$studiensemester_kurzbz."'")
+ ->order_by("p.insertamum", "DESC")
+ ->limit(1)
+ ;
+ }
+ return $this->db->get()->result_array();
+ }
+
+ public function checkZugangscodePerson($code)
+ {
+ $this->db->select("p.person_id")
+ ->from("public.tbl_person p")
+ ->where("p.zugangscode", $code);
+ return $this->db->get()->result_array();
+ }
+
+ public function updatePerson($person)
+ {
+ //TODO check berechtigung
+// if ($this->fhc_db_acl->bb->isBerechtigt('person', 'sui'))
+// {
+ //TODO set other columns to be updated
+ $this->db->set("zugangscode", $person["zugangscode"]);
+ $this->db->where("person_id", $person["person_id"]);
+ if($this->db->update("public.tbl_person")){
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+// }
+// else
+// {
+// return "Nicht berechtigt";
+// }
+ }
}
diff --git a/ci_hack.php b/ci_hack.php
index 1b0f774a6..9d771aab8 100755
--- a/ci_hack.php
+++ b/ci_hack.php
@@ -414,6 +414,11 @@ trait db_extra
return ($var!==''?$var:'null');
}
+ public function db_qoute($var)
+ {
+ return "'".$var."'";
+ }
+
public function db_parse_bool($var)
{
if($var=='t')
diff --git a/include/securimage/securimage.php b/include/securimage/securimage.php
index 955fbf869..810f7ca12 100755
--- a/include/securimage/securimage.php
+++ b/include/securimage/securimage.php
@@ -1,892 +1,892 @@
-
- * File: securimage.php
- *
- * Copyright (c) 2013, Drew Phillips
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Any modifications to the library should be indicated clearly in the source code
- * to inform users that the changes are not a part of the original software.
- *
- * If you found this script useful, please take a quick moment to rate it.
- * http://www.hotscripts.com/rate/49400.html Thanks.
- *
- * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
- * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
- * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
- * @copyright 2013 Drew Phillips
- * @author Drew Phillips
- * @version 3.5 (April 2013)
- * @package Securimage
- *
- */
-
-/**
- ChangeLog
-
- 3.5
- - Release new version
- - MB string support for charlist
- - Modify audio file path to use language directories
- - Changed default captcha appearance
-
- 3.2RC4
- - Add MySQL, PostgreSQL, and SQLite3 support for database storage
- - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions
- - Add new captcha type that displays 2 dictionary words on one image
- - Update examples
-
- 3.2RC3
- - Fix canSendHeaders() check which was breaking if a PHP startup error was issued
-
- 3.2RC2
- - Add error handler (https://github.com/dapphp/securimage/issues/15)
- - Fix flash examples to use the correct value name for audio parameter
-
- 3.2RC1
- - New audio captcha code. Faster, fully dynamic audio, full WAV support
- (Paul Voegler, Drew Phillips)
- - New Flash audio streaming button. User defined image and size supported
- - Additional options for customizing captcha (noise_level, send_headers,
- no_exit, no_session, display_value
- - Add captcha ID support. Uses sqlite and unique captcha IDs to track captchas,
- no session used
- - Add static methods for creating and validating captcha by ID
- - Automatic clearing of old codes from SQLite database
-
- 3.0.3Beta
- - Add improved mixing function to WavFile class (Paul Voegler)
- - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips)
- - Add option to use random file as background noise in captcha audio
- - Add new securimage options for audio files
-
- 3.0.2Beta
- - Fix issue with session variables when upgrading from 2.0 - 3.0
- - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work
-
- 3.0.1
- - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on
-
- 3.0
- - Rewrite class using PHP5 OOP
- - Remove support for GD fonts, require FreeType
- - Remove support for multi-color codes
- - Add option to make codes case-sensitive
- - Add namespaces to support multiple captchas on a single page or page specific captchas
- - Add option to show simple math problems instead of codes
- - Remove support for mp3 files due to vulnerability in decoding mp3 audio files
- - Create new flash file to stream wav files instead of mp3
- - Changed to BSD license
-
- 2.0.2
- - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
-
- 2.0.1
- - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
- - 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)
- - Check for previous definition of image type constants (Mike Challis)
- - Fix mime type settings for audio output
- - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function
- - Ability to let codes expire after a given length of time
- - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis)
-
- 2.0.0
- - Add mathematical distortion to characters (using code from HKCaptcha)
- - Improved session support
- - Added Securimage_Color class for easier color definitions
- - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
- - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
- - Audio output is mp3 format by default
- - Change font to AlteHaasGrotesk by yann le coroller
- - Some code cleanup
-
- 1.0.4 (unreleased)
- - Ability to output audible codes in mp3 format to stream from flash
-
- 1.0.3.1
- - Error reading from wordlist in some cases caused words to be cut off 1 letter short
-
- 1.0.3
- - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
-
- 1.0.2
- - Audible CAPTCHA Code wav files
- - Create codes from a word list instead of random strings
-
- 1.0
- - Added the ability to use a selected character set, rather than a-z0-9 only.
- - Added the multi-color text option to use different colors for each letter.
- - Switched to automatic session handling instead of using files for code storage
- - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones.
- - Added the ability to set line thickness
- - Added option for drawing arced lines over letters
- - Added ability to choose image type for output
-
- */
-
-
-/**
- * Securimage CAPTCHA Class.
- *
- * @version 3.5
- * @package Securimage
- * @subpackage classes
- * @author Drew Phillips
- *
- */
-class Securimage
-{
- // All of the public variables below are securimage options
- // They can be passed as an array to the Securimage constructor, set below,
- // or set from securimage_show.php and securimage_play.php
-
- /**
- * Renders captcha as a JPEG image
- * @var int
- */
- const SI_IMAGE_JPEG = 1;
- /**
- * Renders captcha as a PNG image (default)
- * @var int
- */
- const SI_IMAGE_PNG = 2;
- /**
- * Renders captcha as a GIF image
- * @var int
- */
- const SI_IMAGE_GIF = 3;
-
- /**
- * Create a normal alphanumeric captcha
- * @var int
- */
- const SI_CAPTCHA_STRING = 0;
- /**
- * Create a captcha consisting of a simple math problem
- * @var int
- */
- const SI_CAPTCHA_MATHEMATIC = 1;
- /**
- * Create a word based captcha using 2 words
- * @var int
- */
- const SI_CAPTCHA_WORDS = 2;
-
- /**
- * MySQL option identifier for database storage option
- *
- * @var string
- */
- const SI_DRIVER_MYSQL = 'mysql';
-
- /**
- * PostgreSQL option identifier for database storage option
- *
- * @var string
- */
- const SI_DRIVER_PGSQL = 'pgsql';
-
- /**
- * SQLite option identifier for database storage option
- *
- * @var string
- */
- const SI_DRIVER_SQLITE3 = 'sqlite';
-
- /*%*********************************************************************%*/
- // Properties
-
- /**
- * The width of the captcha image
- * @var int
- */
- public $image_width = 215;
- /**
- * The height of the captcha image
- * @var int
- */
- public $image_height = 80;
- /**
- * The type of the image, default = png
- * @var int
- */
- public $image_type = self::SI_IMAGE_PNG;
-
- /**
- * The background color of the captcha
- * @var Securimage_Color
- */
- public $image_bg_color = '#ffffff';
- /**
- * The color of the captcha text
- * @var Securimage_Color
- */
- public $text_color = '#707070';
- /**
- * The color of the lines over the captcha
- * @var Securimage_Color
- */
- public $line_color = '#707070';
- /**
- * The color of the noise that is drawn
- * @var Securimage_Color
- */
- public $noise_color = '#707070';
-
- /**
- * How transparent to make the text 0 = completely opaque, 100 = invisible
- * @var int
- */
- public $text_transparency_percentage = 20;
- /**
- * Whether or not to draw the text transparently, true = use transparency, false = no transparency
- * @var bool
- */
- public $use_transparent_text = true;
-
- /**
- * The length of the captcha code
- * @var int
- */
- public $code_length = 6;
- /**
- * Whether the captcha should be case sensitive (not recommended, use only for maximum protection)
- * @var bool
- */
- public $case_sensitive = false;
- /**
- * The character set to use for generating the captcha code
- * @var string
- */
- public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
- /**
- * How long in seconds a captcha remains valid, after this time it will not be accepted
- * @var unknown_type
- */
- public $expiry_time = 900;
-
- /**
- * The session name securimage should use, only set this if your application uses a custom session name
- * It is recommended to set this value below so it is used by all securimage scripts
- * @var string
- */
- public $session_name = null;
-
- /**
- * true to use the wordlist file, false to generate random captcha codes
- * @var bool
- */
- public $use_wordlist = false;
-
- /**
- * The level of distortion, 0.75 = normal, 1.0 = very high distortion
- * @var double
- */
- public $perturbation = 0.85;
- /**
- * How many lines to draw over the captcha code to increase security
- * @var int
- */
- public $num_lines = 5;
- /**
- * The level of noise (random dots) to place on the image, 0-10
- * @var int
- */
- public $noise_level = 2;
-
- /**
- * The signature text to draw on the bottom corner of the image
- * @var string
- */
- public $image_signature = '';
- /**
- * The color of the signature text
- * @var Securimage_Color
- */
- public $signature_color = '#707070';
- /**
- * The path to the ttf font file to use for the signature text, defaults to $ttf_file (AHGBold.ttf)
- * @var string
- */
- public $signature_font;
-
- /**
- * DO NOT USE!!!
- * Use an SQLite database to store data (for users that do not support cookies)
- * @var bool
- * @see Securimage::$use_sqlite_db
- * @deprecated 3.2RC4
- */
- public $use_sqlite_db = false;
-
- /**
- * Use a database backend for code storage.
- * Provides a fallback to users with cookies disabled.
- * Required when using captcha IDs.
- *
- * @see Securimage::$database_driver
- * @var bool
- */
- public $use_database = false;
-
- /**
- * Database driver to use for database support.
- * Allowable values: 'mysql', 'pgsql', 'sqlite'.
- * Default: sqlite
- *
- * @var string
- */
- public $database_driver = self::SI_DRIVER_SQLITE3;
-
- /**
- * Database host to connect to when using mysql or postgres
- * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP
- * Does not apply to SQLite
- *
- * @var string
- */
- public $database_host = 'localhost';
-
- /**
- * Database username for connection (mysql, postgres only)
- * Default is an empty string
- *
- * @var string
- */
- public $database_user = '';
-
- /**
- * Database password for connection (mysql, postgres only)
- * Default is empty string
- *
- * @var string
- */
- public $database_pass = '';
-
- /**
- * Name of the database to select (mysql, postgres only)
- *
- * @see Securimage::$database_file for SQLite
- * @var string
- */
- public $database_name = '';
-
- /**
- * Database table where captcha codes are stored
- * Note: Securimage will attempt to create this table for you if it does
- * not exist. If the table cannot be created, an E_USER_WARNING is emitted.
- *
- * @var string
- */
- public $database_table = 'captcha_codes';
-
- /**
- * Fully qualified path to the database file when using SQLite3.
- * This value is only used when $database_driver == sqlite3 and does
- * not apply when no database is used, or when using MySQL or PostgreSQL.
- *
- * @var string
- */
- public $database_file;
-
- /**
- * The type of captcha to create, either alphanumeric, or a math problem
- * Securimage::SI_CAPTCHA_STRING or Securimage::SI_CAPTCHA_MATHEMATIC
- * @var int
- */
- public $captcha_type = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC;
-
- /**
- * The captcha namespace, use this if you have multiple forms on a single page, blank if you do not use multiple forms on one page
- * @var string
- *
- * namespace = 'contact_form';
- *
- * // in form validator
- * $img->namespace = 'contact_form';
- * if ($img->check($code) == true) {
- * echo "Valid!";
- * }
- *
- */
- public $namespace;
-
- /**
- * The font file to use to draw the captcha code, leave blank for default font AHGBold.ttf
- * @var string
- */
- public $ttf_file;
- /**
- * The path to the wordlist file to use, leave blank for default words/words.txt
- * @var string
- */
- public $wordlist_file;
- /**
- * The directory to scan for background images, if set a random background will be chosen from this folder
- * @var string
- */
- public $background_directory;
- /**
- * The path to the SQLite database file to use, if $use_sqlite_database = true, should be chmod 666
- * @deprecated 3.2RC4
- * @var string
- */
- public $sqlite_database;
- /**
- * The path to the securimage audio directory, can be set in securimage_play.php
- * @var string
- *
- * $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/';
- *
- */
- public $audio_path;
- /**
- * The path to the directory containing audio files that will be selected
- * randomly and mixed with the captcha audio.
- *
- * @var string
- */
- public $audio_noise_path;
- /**
- * Whether or not to mix background noise files into captcha audio (true = mix, false = no)
- * Mixing random background audio with noise can help improve security of audio captcha.
- * Default: securimage/audio/noise
- *
- * @since 3.0.3
- * @see Securimage::$audio_noise_path
- * @var bool
- */
- public $audio_use_noise;
- /**
- * The method and threshold (or gain factor) used to normalize the mixing with background noise.
- * See http://www.voegler.eu/pub/audio/ for more information.
- *
- * Valid:
- *
>= 1 - Normalize by multiplying by the threshold (boost - positive gain).
- * A value of 1 in effect means no normalization (and results in clipping).
- *
<= -1 - Normalize by dividing by the the absolute value of threshold (attenuate - negative gain).
- * A factor of 2 (-2) is about 6dB reduction in volume.
- *
[0, 1) - (open inverval - not including 1) - The threshold
- * above which amplitudes are comressed logarithmically.
- * e.g. 0.6 to leave amplitudes up to 60% "as is" and compress above.
- *
(-1, 0) - (open inverval - not including -1 and 0) - The threshold
- * above which amplitudes are comressed linearly.
- * e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above.
- *
- * Default: 0.6
- *
- * @since 3.0.4
- * @var float
- */
- public $audio_mix_normalization = 0.6;
- /**
- * Whether or not to degrade audio by introducing random noise (improves security of audio captcha)
- * Default: true
- *
- * @since 3.0.3
- * @var bool
- */
- public $degrade_audio;
- /**
- * Minimum delay to insert between captcha audio letters in milliseconds
- *
- * @since 3.0.3
- * @var float
- */
- public $audio_gap_min = 0;
- /**
- * Maximum delay to insert between captcha audio letters in milliseconds
- *
- * @since 3.0.3
- * @var float
- */
- public $audio_gap_max = 600;
-
- /**
- * Captcha ID if using static captcha
- * @var string Unique captcha id
- */
- protected static $_captchaId = null;
-
- protected $im;
- protected $tmpimg;
- protected $bgimg;
- protected $iscale = 5;
-
- public $securimage_path = null;
-
- /**
- * The captcha challenge value (either the case-sensitive/insensitive word captcha, or the solution to the math captcha)
- *
- * @var string Captcha challenge value
- */
- protected $code;
-
- /**
- * The display value of the captcha to draw on the image (the word captcha, or the math equation to present to the user)
- *
- * @var string Captcha display value to draw on the image
- */
- protected $code_display;
-
- /**
- * A value that can be passed to the constructor that can be used to generate a captcha image with a given value
- * This value does not get stored in the session or database and is only used when calling Securimage::show().
- * If a display_value was passed to the constructor and the captcha image is generated, the display_value will be used
- * as the string to draw on the captcha image. Used only if captcha codes are generated and managed by a 3rd party app/library
- *
- * @var string Captcha code value to display on the image
- */
- public $display_value;
-
- /**
- * Captcha code supplied by user [set from Securimage::check()]
- *
- * @var string
- */
- protected $captcha_code;
-
- /**
- * Flag that can be specified telling securimage not to call exit after generating a captcha image or audio file
- *
- * @var bool If true, script will not terminate; if false script will terminate (default)
- */
- protected $no_exit;
-
- /**
- * Flag indicating whether or not a PHP session should be started and used
- *
- * @var bool If true, no session will be started; if false, session will be started and used to store data (default)
- */
- protected $no_session;
-
- /**
- * Flag indicating whether or not HTTP headers will be sent when outputting captcha image/audio
- *
- * @var bool If true (default) headers will be sent, if false, no headers are sent
- */
- protected $send_headers;
-
- /**
- * PDO connection when a database is used
- *
- * @var resource
- */
- protected $pdo_conn;
-
- // gd color resources that are allocated for drawing the image
- protected $gdbgcolor;
- protected $gdtextcolor;
- protected $gdlinecolor;
- protected $gdsignaturecolor;
-
- /**
- * Create a new securimage object, pass options to set in the constructor.
- * This can be used to display a captcha, play an audible captcha, or validate an entry
- * @param array $options
- *
- * $options = array(
- * 'text_color' => new Securimage_Color('#013020'),
- * 'code_length' => 5,
- * 'num_lines' => 5,
- * 'noise_level' => 3,
- * 'font_file' => Securimage::getPath() . '/custom.ttf'
- * );
- *
- * $img = new Securimage($options);
- *
- */
- public function __construct($options = array())
- {
- $this->securimage_path = dirname(__FILE__);
-
- if (is_array($options) && sizeof($options) > 0) {
- foreach($options as $prop => $val) {
- if ($prop == 'captchaId') {
- Securimage::$_captchaId = $val;
- $this->use_database = true;
- } else if ($prop == 'use_sqlite_db') {
- trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
- } else {
- $this->$prop = $val;
- }
- }
- }
-
- $this->image_bg_color = $this->initColor($this->image_bg_color, '#ffffff');
- $this->text_color = $this->initColor($this->text_color, '#616161');
- $this->line_color = $this->initColor($this->line_color, '#616161');
- $this->noise_color = $this->initColor($this->noise_color, '#616161');
- $this->signature_color = $this->initColor($this->signature_color, '#616161');
-
- if (is_null($this->ttf_file)) {
- $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
- }
-
- $this->signature_font = $this->ttf_file;
-
- if (is_null($this->wordlist_file)) {
- $this->wordlist_file = $this->securimage_path . '/words/words.txt';
- }
-
- if (is_null($this->database_file)) {
- $this->database_file = $this->securimage_path . '/database/securimage.sq3';
- }
-
- if (is_null($this->audio_path)) {
- $this->audio_path = $this->securimage_path . '/audio/en/';
- }
-
- if (is_null($this->audio_noise_path)) {
- $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
- }
-
- if (is_null($this->audio_use_noise)) {
- $this->audio_use_noise = true;
- }
-
- if (is_null($this->degrade_audio)) {
- $this->degrade_audio = true;
- }
-
- if (is_null($this->code_length) || (int)$this->code_length < 1) {
- $this->code_length = 6;
- }
-
- if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
- $this->perturbation = 0.75;
- }
-
- if (is_null($this->namespace) || !is_string($this->namespace)) {
- $this->namespace = 'default';
- }
-
- if (is_null($this->no_exit)) {
- $this->no_exit = false;
- }
-
- if (is_null($this->no_session)) {
- $this->no_session = false;
- }
-
- if (is_null($this->send_headers)) {
- $this->send_headers = true;
- }
-
- if ($this->no_session != true) {
- // Initialize session or attach to existing
- if ( session_id() == '' ) { // no session has been started yet, which is needed for validation
- if (!is_null($this->session_name) && trim($this->session_name) != '') {
- session_name(trim($this->session_name)); // set session name if provided
- }
- session_start();
- }
- }
- }
-
- /**
- * Return the absolute path to the Securimage directory
- * @return string The path to the securimage base directory
- */
- public static function getPath()
- {
- return dirname(__FILE__);
- }
-
- /**
- * Generate a new captcha ID or retrieve the current ID
- *
- * @param $new bool If true, generates a new challenge and returns and ID
- * @param $options array Additional options to be passed to Securimage.
- * Must include database options if not set directly in securimage.php
- *
- * @return null|string Returns null if no captcha id set and new was false, or string captcha ID
- */
- public static function getCaptchaId($new = true, array $options = array())
- {
- if (is_null($new) || (bool)$new == true) {
- $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
- $opts = array('no_session' => true,
- 'use_database' => true);
- if (sizeof($options) > 0) $opts = array_merge($options, $opts);
- $si = new self($opts);
- Securimage::$_captchaId = $id;
- $si->createCode();
-
- return $id;
- } else {
- return Securimage::$_captchaId;
- }
- }
-
- /**
- * Validate a captcha code input against a captcha ID
- *
- * @param string $id The captcha ID to check
- * @param string $value The captcha value supplied by the user
- * @param array $options Array of options to construct Securimage with.
- * Options must include database options if they are not set in securimage.php
- *
- * @see Securimage::$database_driver
- * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open
- */
- public static function checkByCaptchaId($id, $value, array $options = array())
- {
- $opts = array('captchaId' => $id,
- 'no_session' => true,
- 'use_database' => true);
-
- if (sizeof($options) > 0) $opts = array_merge($options, $opts);
-
- $si = new self($opts);
-
- if ($si->openDatabase()) {
- $code = $si->getCodeFromDatabase();
-
- if (is_array($code)) {
- $si->code = $code['code'];
- $si->code_display = $code['code_disp'];
- }
-
- if ($si->check($value)) {
- $si->clearCodeFromDatabase();
-
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- /**
- * Used to serve a captcha image to the browser
- * @param string $background_image The path to the background image to use
- *
- * $img = new Securimage();
- * $img->code_length = 6;
- * $img->num_lines = 5;
- * $img->noise_level = 5;
- *
- * $img->show(); // sends the image to browser
- * exit;
- *
- */
- public function show($background_image = '')
- {
- set_error_handler(array(&$this, 'errorHandler'));
-
- if($background_image != '' && is_readable($background_image)) {
- $this->bgimg = $background_image;
- }
-
- $this->doImage();
- }
-
- /**
- * Check a submitted code against the stored value
- * @param string $code The captcha code to check
- *
- * $code = $_POST['code'];
- * $img = new Securimage();
- * if ($img->check($code) == true) {
- * $captcha_valid = true;
- * } else {
- * $captcha_valid = false;
- * }
- *
- */
- public function check($code)
- {
- $this->code_entered = $code;
- $this->validate();
- return $this->correct_code;
- }
-
- /**
- * Output a wav file of the captcha code to the browser
- *
- *
- * $img = new Securimage();
- * $img->outputAudioFile(); // outputs a wav file to the browser
- * exit;
- *
- */
- public function outputAudioFile()
- {
- set_error_handler(array(&$this, 'errorHandler'));
-
- require_once dirname(__FILE__) . '/WavFile.php';
-
- try {
- $audio = $this->getAudibleCode();
- } catch (Exception $ex) {
+
+ * File: securimage.php
+ *
+ * Copyright (c) 2013, Drew Phillips
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Any modifications to the library should be indicated clearly in the source code
+ * to inform users that the changes are not a part of the original software.
+ *
+ * If you found this script useful, please take a quick moment to rate it.
+ * http://www.hotscripts.com/rate/49400.html Thanks.
+ *
+ * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
+ * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
+ * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
+ * @copyright 2013 Drew Phillips
+ * @author Drew Phillips
+ * @version 3.5 (April 2013)
+ * @package Securimage
+ *
+ */
+
+/**
+ ChangeLog
+
+ 3.5
+ - Release new version
+ - MB string support for charlist
+ - Modify audio file path to use language directories
+ - Changed default captcha appearance
+
+ 3.2RC4
+ - Add MySQL, PostgreSQL, and SQLite3 support for database storage
+ - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions
+ - Add new captcha type that displays 2 dictionary words on one image
+ - Update examples
+
+ 3.2RC3
+ - Fix canSendHeaders() check which was breaking if a PHP startup error was issued
+
+ 3.2RC2
+ - Add error handler (https://github.com/dapphp/securimage/issues/15)
+ - Fix flash examples to use the correct value name for audio parameter
+
+ 3.2RC1
+ - New audio captcha code. Faster, fully dynamic audio, full WAV support
+ (Paul Voegler, Drew Phillips)
+ - New Flash audio streaming button. User defined image and size supported
+ - Additional options for customizing captcha (noise_level, send_headers,
+ no_exit, no_session, display_value
+ - Add captcha ID support. Uses sqlite and unique captcha IDs to track captchas,
+ no session used
+ - Add static methods for creating and validating captcha by ID
+ - Automatic clearing of old codes from SQLite database
+
+ 3.0.3Beta
+ - Add improved mixing function to WavFile class (Paul Voegler)
+ - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips)
+ - Add option to use random file as background noise in captcha audio
+ - Add new securimage options for audio files
+
+ 3.0.2Beta
+ - Fix issue with session variables when upgrading from 2.0 - 3.0
+ - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work
+
+ 3.0.1
+ - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on
+
+ 3.0
+ - Rewrite class using PHP5 OOP
+ - Remove support for GD fonts, require FreeType
+ - Remove support for multi-color codes
+ - Add option to make codes case-sensitive
+ - Add namespaces to support multiple captchas on a single page or page specific captchas
+ - Add option to show simple math problems instead of codes
+ - Remove support for mp3 files due to vulnerability in decoding mp3 audio files
+ - Create new flash file to stream wav files instead of mp3
+ - Changed to BSD license
+
+ 2.0.2
+ - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
+
+ 2.0.1
+ - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
+ - 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)
+ - Check for previous definition of image type constants (Mike Challis)
+ - Fix mime type settings for audio output
+ - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function
+ - Ability to let codes expire after a given length of time
+ - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis)
+
+ 2.0.0
+ - Add mathematical distortion to characters (using code from HKCaptcha)
+ - Improved session support
+ - Added Securimage_Color class for easier color definitions
+ - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
+ - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
+ - Audio output is mp3 format by default
+ - Change font to AlteHaasGrotesk by yann le coroller
+ - Some code cleanup
+
+ 1.0.4 (unreleased)
+ - Ability to output audible codes in mp3 format to stream from flash
+
+ 1.0.3.1
+ - Error reading from wordlist in some cases caused words to be cut off 1 letter short
+
+ 1.0.3
+ - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
+
+ 1.0.2
+ - Audible CAPTCHA Code wav files
+ - Create codes from a word list instead of random strings
+
+ 1.0
+ - Added the ability to use a selected character set, rather than a-z0-9 only.
+ - Added the multi-color text option to use different colors for each letter.
+ - Switched to automatic session handling instead of using files for code storage
+ - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones.
+ - Added the ability to set line thickness
+ - Added option for drawing arced lines over letters
+ - Added ability to choose image type for output
+
+ */
+
+
+/**
+ * Securimage CAPTCHA Class.
+ *
+ * @version 3.5
+ * @package Securimage
+ * @subpackage classes
+ * @author Drew Phillips
+ *
+ */
+class Securimage
+{
+ // All of the public variables below are securimage options
+ // They can be passed as an array to the Securimage constructor, set below,
+ // or set from securimage_show.php and securimage_play.php
+
+ /**
+ * Renders captcha as a JPEG image
+ * @var int
+ */
+ const SI_IMAGE_JPEG = 1;
+ /**
+ * Renders captcha as a PNG image (default)
+ * @var int
+ */
+ const SI_IMAGE_PNG = 2;
+ /**
+ * Renders captcha as a GIF image
+ * @var int
+ */
+ const SI_IMAGE_GIF = 3;
+
+ /**
+ * Create a normal alphanumeric captcha
+ * @var int
+ */
+ const SI_CAPTCHA_STRING = 0;
+ /**
+ * Create a captcha consisting of a simple math problem
+ * @var int
+ */
+ const SI_CAPTCHA_MATHEMATIC = 1;
+ /**
+ * Create a word based captcha using 2 words
+ * @var int
+ */
+ const SI_CAPTCHA_WORDS = 2;
+
+ /**
+ * MySQL option identifier for database storage option
+ *
+ * @var string
+ */
+ const SI_DRIVER_MYSQL = 'mysql';
+
+ /**
+ * PostgreSQL option identifier for database storage option
+ *
+ * @var string
+ */
+ const SI_DRIVER_PGSQL = 'pgsql';
+
+ /**
+ * SQLite option identifier for database storage option
+ *
+ * @var string
+ */
+ const SI_DRIVER_SQLITE3 = 'sqlite';
+
+ /*%*********************************************************************%*/
+ // Properties
+
+ /**
+ * The width of the captcha image
+ * @var int
+ */
+ public $image_width = 215;
+ /**
+ * The height of the captcha image
+ * @var int
+ */
+ public $image_height = 80;
+ /**
+ * The type of the image, default = png
+ * @var int
+ */
+ public $image_type = self::SI_IMAGE_PNG;
+
+ /**
+ * The background color of the captcha
+ * @var Securimage_Color
+ */
+ public $image_bg_color = '#ffffff';
+ /**
+ * The color of the captcha text
+ * @var Securimage_Color
+ */
+ public $text_color = '#707070';
+ /**
+ * The color of the lines over the captcha
+ * @var Securimage_Color
+ */
+ public $line_color = '#707070';
+ /**
+ * The color of the noise that is drawn
+ * @var Securimage_Color
+ */
+ public $noise_color = '#707070';
+
+ /**
+ * How transparent to make the text 0 = completely opaque, 100 = invisible
+ * @var int
+ */
+ public $text_transparency_percentage = 20;
+ /**
+ * Whether or not to draw the text transparently, true = use transparency, false = no transparency
+ * @var bool
+ */
+ public $use_transparent_text = true;
+
+ /**
+ * The length of the captcha code
+ * @var int
+ */
+ public $code_length = 6;
+ /**
+ * Whether the captcha should be case sensitive (not recommended, use only for maximum protection)
+ * @var bool
+ */
+ public $case_sensitive = false;
+ /**
+ * The character set to use for generating the captcha code
+ * @var string
+ */
+ public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
+ /**
+ * How long in seconds a captcha remains valid, after this time it will not be accepted
+ * @var unknown_type
+ */
+ public $expiry_time = 900;
+
+ /**
+ * The session name securimage should use, only set this if your application uses a custom session name
+ * It is recommended to set this value below so it is used by all securimage scripts
+ * @var string
+ */
+ public $session_name = null;
+
+ /**
+ * true to use the wordlist file, false to generate random captcha codes
+ * @var bool
+ */
+ public $use_wordlist = false;
+
+ /**
+ * The level of distortion, 0.75 = normal, 1.0 = very high distortion
+ * @var double
+ */
+ public $perturbation = 0.85;
+ /**
+ * How many lines to draw over the captcha code to increase security
+ * @var int
+ */
+ public $num_lines = 5;
+ /**
+ * The level of noise (random dots) to place on the image, 0-10
+ * @var int
+ */
+ public $noise_level = 2;
+
+ /**
+ * The signature text to draw on the bottom corner of the image
+ * @var string
+ */
+ public $image_signature = '';
+ /**
+ * The color of the signature text
+ * @var Securimage_Color
+ */
+ public $signature_color = '#707070';
+ /**
+ * The path to the ttf font file to use for the signature text, defaults to $ttf_file (AHGBold.ttf)
+ * @var string
+ */
+ public $signature_font;
+
+ /**
+ * DO NOT USE!!!
+ * Use an SQLite database to store data (for users that do not support cookies)
+ * @var bool
+ * @see Securimage::$use_sqlite_db
+ * @deprecated 3.2RC4
+ */
+ public $use_sqlite_db = false;
+
+ /**
+ * Use a database backend for code storage.
+ * Provides a fallback to users with cookies disabled.
+ * Required when using captcha IDs.
+ *
+ * @see Securimage::$database_driver
+ * @var bool
+ */
+ public $use_database = false;
+
+ /**
+ * Database driver to use for database support.
+ * Allowable values: 'mysql', 'pgsql', 'sqlite'.
+ * Default: sqlite
+ *
+ * @var string
+ */
+ public $database_driver = self::SI_DRIVER_SQLITE3;
+
+ /**
+ * Database host to connect to when using mysql or postgres
+ * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP
+ * Does not apply to SQLite
+ *
+ * @var string
+ */
+ public $database_host = 'localhost';
+
+ /**
+ * Database username for connection (mysql, postgres only)
+ * Default is an empty string
+ *
+ * @var string
+ */
+ public $database_user = '';
+
+ /**
+ * Database password for connection (mysql, postgres only)
+ * Default is empty string
+ *
+ * @var string
+ */
+ public $database_pass = '';
+
+ /**
+ * Name of the database to select (mysql, postgres only)
+ *
+ * @see Securimage::$database_file for SQLite
+ * @var string
+ */
+ public $database_name = '';
+
+ /**
+ * Database table where captcha codes are stored
+ * Note: Securimage will attempt to create this table for you if it does
+ * not exist. If the table cannot be created, an E_USER_WARNING is emitted.
+ *
+ * @var string
+ */
+ public $database_table = 'captcha_codes';
+
+ /**
+ * Fully qualified path to the database file when using SQLite3.
+ * This value is only used when $database_driver == sqlite3 and does
+ * not apply when no database is used, or when using MySQL or PostgreSQL.
+ *
+ * @var string
+ */
+ public $database_file;
+
+ /**
+ * The type of captcha to create, either alphanumeric, or a math problem
+ * Securimage::SI_CAPTCHA_STRING or Securimage::SI_CAPTCHA_MATHEMATIC
+ * @var int
+ */
+ public $captcha_type = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC;
+
+ /**
+ * The captcha namespace, use this if you have multiple forms on a single page, blank if you do not use multiple forms on one page
+ * @var string
+ *
+ * namespace = 'contact_form';
+ *
+ * // in form validator
+ * $img->namespace = 'contact_form';
+ * if ($img->check($code) == true) {
+ * echo "Valid!";
+ * }
+ *
+ */
+ public $namespace;
+
+ /**
+ * The font file to use to draw the captcha code, leave blank for default font AHGBold.ttf
+ * @var string
+ */
+ public $ttf_file;
+ /**
+ * The path to the wordlist file to use, leave blank for default words/words.txt
+ * @var string
+ */
+ public $wordlist_file;
+ /**
+ * The directory to scan for background images, if set a random background will be chosen from this folder
+ * @var string
+ */
+ public $background_directory;
+ /**
+ * The path to the SQLite database file to use, if $use_sqlite_database = true, should be chmod 666
+ * @deprecated 3.2RC4
+ * @var string
+ */
+ public $sqlite_database;
+ /**
+ * The path to the securimage audio directory, can be set in securimage_play.php
+ * @var string
+ *
+ * $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/';
+ *
+ */
+ public $audio_path;
+ /**
+ * The path to the directory containing audio files that will be selected
+ * randomly and mixed with the captcha audio.
+ *
+ * @var string
+ */
+ public $audio_noise_path;
+ /**
+ * Whether or not to mix background noise files into captcha audio (true = mix, false = no)
+ * Mixing random background audio with noise can help improve security of audio captcha.
+ * Default: securimage/audio/noise
+ *
+ * @since 3.0.3
+ * @see Securimage::$audio_noise_path
+ * @var bool
+ */
+ public $audio_use_noise;
+ /**
+ * The method and threshold (or gain factor) used to normalize the mixing with background noise.
+ * See http://www.voegler.eu/pub/audio/ for more information.
+ *
+ * Valid:
+ *
>= 1 - Normalize by multiplying by the threshold (boost - positive gain).
+ * A value of 1 in effect means no normalization (and results in clipping).
+ *
<= -1 - Normalize by dividing by the the absolute value of threshold (attenuate - negative gain).
+ * A factor of 2 (-2) is about 6dB reduction in volume.
+ *
[0, 1) - (open inverval - not including 1) - The threshold
+ * above which amplitudes are comressed logarithmically.
+ * e.g. 0.6 to leave amplitudes up to 60% "as is" and compress above.
+ *
(-1, 0) - (open inverval - not including -1 and 0) - The threshold
+ * above which amplitudes are comressed linearly.
+ * e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above.
+ *
+ * Default: 0.6
+ *
+ * @since 3.0.4
+ * @var float
+ */
+ public $audio_mix_normalization = 0.6;
+ /**
+ * Whether or not to degrade audio by introducing random noise (improves security of audio captcha)
+ * Default: true
+ *
+ * @since 3.0.3
+ * @var bool
+ */
+ public $degrade_audio;
+ /**
+ * Minimum delay to insert between captcha audio letters in milliseconds
+ *
+ * @since 3.0.3
+ * @var float
+ */
+ public $audio_gap_min = 0;
+ /**
+ * Maximum delay to insert between captcha audio letters in milliseconds
+ *
+ * @since 3.0.3
+ * @var float
+ */
+ public $audio_gap_max = 600;
+
+ /**
+ * Captcha ID if using static captcha
+ * @var string Unique captcha id
+ */
+ protected static $_captchaId = null;
+
+ protected $im;
+ protected $tmpimg;
+ protected $bgimg;
+ protected $iscale = 5;
+
+ public $securimage_path = null;
+
+ /**
+ * The captcha challenge value (either the case-sensitive/insensitive word captcha, or the solution to the math captcha)
+ *
+ * @var string Captcha challenge value
+ */
+ protected $code;
+
+ /**
+ * The display value of the captcha to draw on the image (the word captcha, or the math equation to present to the user)
+ *
+ * @var string Captcha display value to draw on the image
+ */
+ protected $code_display;
+
+ /**
+ * A value that can be passed to the constructor that can be used to generate a captcha image with a given value
+ * This value does not get stored in the session or database and is only used when calling Securimage::show().
+ * If a display_value was passed to the constructor and the captcha image is generated, the display_value will be used
+ * as the string to draw on the captcha image. Used only if captcha codes are generated and managed by a 3rd party app/library
+ *
+ * @var string Captcha code value to display on the image
+ */
+ public $display_value;
+
+ /**
+ * Captcha code supplied by user [set from Securimage::check()]
+ *
+ * @var string
+ */
+ protected $captcha_code;
+
+ /**
+ * Flag that can be specified telling securimage not to call exit after generating a captcha image or audio file
+ *
+ * @var bool If true, script will not terminate; if false script will terminate (default)
+ */
+ protected $no_exit;
+
+ /**
+ * Flag indicating whether or not a PHP session should be started and used
+ *
+ * @var bool If true, no session will be started; if false, session will be started and used to store data (default)
+ */
+ protected $no_session;
+
+ /**
+ * Flag indicating whether or not HTTP headers will be sent when outputting captcha image/audio
+ *
+ * @var bool If true (default) headers will be sent, if false, no headers are sent
+ */
+ protected $send_headers;
+
+ /**
+ * PDO connection when a database is used
+ *
+ * @var resource
+ */
+ protected $pdo_conn;
+
+ // gd color resources that are allocated for drawing the image
+ protected $gdbgcolor;
+ protected $gdtextcolor;
+ protected $gdlinecolor;
+ protected $gdsignaturecolor;
+
+ /**
+ * Create a new securimage object, pass options to set in the constructor.
+ * This can be used to display a captcha, play an audible captcha, or validate an entry
+ * @param array $options
+ *
+ * $options = array(
+ * 'text_color' => new Securimage_Color('#013020'),
+ * 'code_length' => 5,
+ * 'num_lines' => 5,
+ * 'noise_level' => 3,
+ * 'font_file' => Securimage::getPath() . '/custom.ttf'
+ * );
+ *
+ * $img = new Securimage($options);
+ *
+ */
+ public function __construct($options = array())
+ {
+ $this->securimage_path = dirname(__FILE__);
+
+ if (is_array($options) && sizeof($options) > 0) {
+ foreach($options as $prop => $val) {
+ if ($prop == 'captchaId') {
+ Securimage::$_captchaId = $val;
+ $this->use_database = true;
+ } else if ($prop == 'use_sqlite_db') {
+ trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
+ } else {
+ $this->$prop = $val;
+ }
+ }
+ }
+
+ $this->image_bg_color = $this->initColor($this->image_bg_color, '#ffffff');
+ $this->text_color = $this->initColor($this->text_color, '#616161');
+ $this->line_color = $this->initColor($this->line_color, '#616161');
+ $this->noise_color = $this->initColor($this->noise_color, '#616161');
+ $this->signature_color = $this->initColor($this->signature_color, '#616161');
+
+ if (is_null($this->ttf_file)) {
+ $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
+ }
+
+ $this->signature_font = $this->ttf_file;
+
+ if (is_null($this->wordlist_file)) {
+ $this->wordlist_file = $this->securimage_path . '/words/words.txt';
+ }
+
+ if (is_null($this->database_file)) {
+ $this->database_file = $this->securimage_path . '/database/securimage.sq3';
+ }
+
+ if (is_null($this->audio_path)) {
+ $this->audio_path = $this->securimage_path . '/audio/en/';
+ }
+
+ if (is_null($this->audio_noise_path)) {
+ $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
+ }
+
+ if (is_null($this->audio_use_noise)) {
+ $this->audio_use_noise = true;
+ }
+
+ if (is_null($this->degrade_audio)) {
+ $this->degrade_audio = true;
+ }
+
+ if (is_null($this->code_length) || (int)$this->code_length < 1) {
+ $this->code_length = 6;
+ }
+
+ if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
+ $this->perturbation = 0.75;
+ }
+
+ if (is_null($this->namespace) || !is_string($this->namespace)) {
+ $this->namespace = 'default';
+ }
+
+ if (is_null($this->no_exit)) {
+ $this->no_exit = false;
+ }
+
+ if (is_null($this->no_session)) {
+ $this->no_session = false;
+ }
+
+ if (is_null($this->send_headers)) {
+ $this->send_headers = true;
+ }
+
+ if ($this->no_session != true) {
+ // Initialize session or attach to existing
+ if ( session_id() == '' ) { // no session has been started yet, which is needed for validation
+ if (!is_null($this->session_name) && trim($this->session_name) != '') {
+ session_name(trim($this->session_name)); // set session name if provided
+ }
+ session_start();
+ }
+ }
+ }
+
+ /**
+ * Return the absolute path to the Securimage directory
+ * @return string The path to the securimage base directory
+ */
+ public static function getPath()
+ {
+ return dirname(__FILE__);
+ }
+
+ /**
+ * Generate a new captcha ID or retrieve the current ID
+ *
+ * @param $new bool If true, generates a new challenge and returns and ID
+ * @param $options array Additional options to be passed to Securimage.
+ * Must include database options if not set directly in securimage.php
+ *
+ * @return null|string Returns null if no captcha id set and new was false, or string captcha ID
+ */
+ public static function getCaptchaId($new = true, array $options = array())
+ {
+ if (is_null($new) || (bool)$new == true) {
+ $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
+ $opts = array('no_session' => true,
+ 'use_database' => true);
+ if (sizeof($options) > 0) $opts = array_merge($options, $opts);
+ $si = new self($opts);
+ Securimage::$_captchaId = $id;
+ $si->createCode();
+
+ return $id;
+ } else {
+ return Securimage::$_captchaId;
+ }
+ }
+
+ /**
+ * Validate a captcha code input against a captcha ID
+ *
+ * @param string $id The captcha ID to check
+ * @param string $value The captcha value supplied by the user
+ * @param array $options Array of options to construct Securimage with.
+ * Options must include database options if they are not set in securimage.php
+ *
+ * @see Securimage::$database_driver
+ * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open
+ */
+ public static function checkByCaptchaId($id, $value, array $options = array())
+ {
+ $opts = array('captchaId' => $id,
+ 'no_session' => true,
+ 'use_database' => true);
+
+ if (sizeof($options) > 0) $opts = array_merge($options, $opts);
+
+ $si = new self($opts);
+
+ if ($si->openDatabase()) {
+ $code = $si->getCodeFromDatabase();
+
+ if (is_array($code)) {
+ $si->code = $code['code'];
+ $si->code_display = $code['code_disp'];
+ }
+
+ if ($si->check($value)) {
+ $si->clearCodeFromDatabase();
+
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Used to serve a captcha image to the browser
+ * @param string $background_image The path to the background image to use
+ *
+ * $img = new Securimage();
+ * $img->code_length = 6;
+ * $img->num_lines = 5;
+ * $img->noise_level = 5;
+ *
+ * $img->show(); // sends the image to browser
+ * exit;
+ *
+ */
+ public function show($background_image = '')
+ {
+ set_error_handler(array(&$this, 'errorHandler'));
+
+ if($background_image != '' && is_readable($background_image)) {
+ $this->bgimg = $background_image;
+ }
+
+ $this->doImage();
+ }
+
+ /**
+ * Check a submitted code against the stored value
+ * @param string $code The captcha code to check
+ *
+ * $code = $_POST['code'];
+ * $img = new Securimage();
+ * if ($img->check($code) == true) {
+ * $captcha_valid = true;
+ * } else {
+ * $captcha_valid = false;
+ * }
+ *
+ */
+ public function check($code)
+ {
+ $this->code_entered = $code;
+ $this->validate();
+ return $this->correct_code;
+ }
+
+ /**
+ * Output a wav file of the captcha code to the browser
+ *
+ *
+ * $img = new Securimage();
+ * $img->outputAudioFile(); // outputs a wav file to the browser
+ * exit;
+ *
+ */
+ public function outputAudioFile()
+ {
+ set_error_handler(array(&$this, 'errorHandler'));
+
+ require_once dirname(__FILE__) . '/WavFile.php';
+
+ try {
+ $audio = $this->getAudibleCode();
+ } catch (Exception $ex) {
if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) {
fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
fclose($fp);
- }
-
- $audio = $this->audioError();
+ }
+
+ $audio = $this->audioError();
}
-
- if ($this->canSendHeaders() || $this->send_headers == false) {
- if ($this->send_headers) {
- $uniq = md5(uniqid(microtime()));
- header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.wav\"");
- header('Cache-Control: no-store, no-cache, must-revalidate');
- header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
- header('Content-type: audio/x-wav');
-
- if (extension_loaded('zlib')) {
- ini_set('zlib.output_compression', true); // compress output if supported by browser
- } else {
- header('Content-Length: ' . strlen($audio));
- }
- }
-
- echo $audio;
- } else {
- echo ''
- .'Failed to generate audio file, content has already been '
- .'output. This is most likely due to misconfiguration or '
- .'a PHP error was sent to the browser.';
- }
-
- restore_error_handler();
-
- if (!$this->no_exit) exit;
- }
-
+
+ if ($this->canSendHeaders() || $this->send_headers == false) {
+ if ($this->send_headers) {
+ $uniq = md5(uniqid(microtime()));
+ header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.wav\"");
+ header('Cache-Control: no-store, no-cache, must-revalidate');
+ header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
+ header('Content-type: audio/x-wav');
+
+ if (extension_loaded('zlib')) {
+ ini_set('zlib.output_compression', true); // compress output if supported by browser
+ } else {
+ header('Content-Length: ' . strlen($audio));
+ }
+ }
+
+ echo $audio;
+ } else {
+ echo ''
+ .'Failed to generate audio file, content has already been '
+ .'output. This is most likely due to misconfiguration or '
+ .'a PHP error was sent to the browser.';
+ }
+
+ restore_error_handler();
+
+ if (!$this->no_exit) exit;
+ }
+
/**
* Return the code from the session or sqlite database if used. If none exists yet, an empty string is returned
*
@@ -896,20 +896,20 @@ class Securimage
public function getCode($array = false, $returnExisting = false)
{
$code = '';
- $time = 0;
- $disp = 'error';
-
- if ($returnExisting && strlen($this->code) > 0) {
- if ($array) {
- return array('code' => $this->code,
- 'display' => $this->code_display,
- 'code_display' => $this->code_display,
- 'time' => 0);
- } else {
- return $this->code;
- }
+ $time = 0;
+ $disp = 'error';
+
+ if ($returnExisting && strlen($this->code) > 0) {
+ if ($array) {
+ return array('code' => $this->code,
+ 'display' => $this->code_display,
+ 'code_display' => $this->code_display,
+ 'time' => 0);
+ } else {
+ return $this->code;
+ }
}
-
+
if ($this->no_session != true) {
if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
@@ -919,9 +919,9 @@ class Securimage
$time = $_SESSION['securimage_code_ctime'][$this->namespace];
$disp = $_SESSION['securimage_code_disp'] [$this->namespace];
}
- }
- }
-
+ }
+ }
+
if (empty($code) && $this->use_database) {
// no code in session - may mean user has cookies turned off
$this->openDatabase();
@@ -933,448 +933,448 @@ class Securimage
} else {
return $code;
}
- }
-
- /**
- * The main image drawing routing, responsible for constructing the entire image and serving it
- */
- protected function doImage()
- {
- if( ($this->use_transparent_text == true || $this->bgimg != '') && function_exists('imagecreatetruecolor')) {
- $imagecreate = 'imagecreatetruecolor';
- } else {
- $imagecreate = 'imagecreate';
- }
-
- $this->im = $imagecreate($this->image_width, $this->image_height);
- $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
-
- $this->allocateColors();
- imagepalettecopy($this->tmpimg, $this->im);
-
- $this->setBackground();
-
- $code = '';
-
- if ($this->getCaptchaId(false) !== null) {
- // a captcha Id was supplied
-
- // check to see if a display_value for the captcha image was set
- if (is_string($this->display_value) && strlen($this->display_value) > 0) {
- $this->code_display = $this->display_value;
- $this->code = ($this->case_sensitive) ?
- $this->display_value :
- strtolower($this->display_value);
- $code = $this->code;
- } else if ($this->openDatabase()) {
- // no display_value, check the database for existing captchaId
- $code = $this->getCodeFromDatabase();
-
- // got back a result from the database with a valid code for captchaId
- if (is_array($code)) {
- $this->code = $code['code'];
- $this->code_display = $code['code_disp'];
- $code = $code['code'];
- }
- }
- }
-
- if ($code == '') {
- // if the code was not set using display_value or was not found in
- // the database, create a new code
- $this->createCode();
- }
-
- if ($this->noise_level > 0) {
- $this->drawNoise();
- }
-
- $this->drawWord();
-
- if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
- $this->distortedCopy();
- }
-
- if ($this->num_lines > 0) {
- $this->drawLines();
- }
-
- if (trim($this->image_signature) != '') {
- $this->addSignature();
- }
-
- $this->output();
- }
-
- /**
- * Allocate the colors to be used for the image
- */
- protected function allocateColors()
- {
- // allocate bg color first for imagecreate
- $this->gdbgcolor = imagecolorallocate($this->im,
- $this->image_bg_color->r,
- $this->image_bg_color->g,
- $this->image_bg_color->b);
-
- $alpha = intval($this->text_transparency_percentage / 100 * 127);
-
- if ($this->use_transparent_text == true) {
- $this->gdtextcolor = imagecolorallocatealpha($this->im,
- $this->text_color->r,
- $this->text_color->g,
- $this->text_color->b,
- $alpha);
- $this->gdlinecolor = imagecolorallocatealpha($this->im,
- $this->line_color->r,
- $this->line_color->g,
- $this->line_color->b,
- $alpha);
- $this->gdnoisecolor = imagecolorallocatealpha($this->im,
- $this->noise_color->r,
- $this->noise_color->g,
- $this->noise_color->b,
- $alpha);
- } else {
- $this->gdtextcolor = imagecolorallocate($this->im,
- $this->text_color->r,
- $this->text_color->g,
- $this->text_color->b);
- $this->gdlinecolor = imagecolorallocate($this->im,
- $this->line_color->r,
- $this->line_color->g,
- $this->line_color->b);
- $this->gdnoisecolor = imagecolorallocate($this->im,
- $this->noise_color->r,
- $this->noise_color->g,
- $this->noise_color->b);
- }
-
- $this->gdsignaturecolor = imagecolorallocate($this->im,
- $this->signature_color->r,
- $this->signature_color->g,
- $this->signature_color->b);
-
- }
-
- /**
- * The the background color, or background image to be used
- */
- protected function setBackground()
- {
- // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
- imagefilledrectangle($this->im, 0, 0,
- $this->image_width, $this->image_height,
- $this->gdbgcolor);
- imagefilledrectangle($this->tmpimg, 0, 0,
- $this->image_width * $this->iscale, $this->image_height * $this->iscale,
- $this->gdbgcolor);
-
- if ($this->bgimg == '') {
- if ($this->background_directory != null &&
- is_dir($this->background_directory) &&
- is_readable($this->background_directory))
- {
- $img = $this->getBackgroundFromDirectory();
- if ($img != false) {
- $this->bgimg = $img;
- }
- }
- }
-
- if ($this->bgimg == '') {
- return;
- }
-
- $dat = @getimagesize($this->bgimg);
- if($dat == false) {
- return;
- }
-
- switch($dat[2]) {
- case 1: $newim = @imagecreatefromgif($this->bgimg); break;
- case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
- case 3: $newim = @imagecreatefrompng($this->bgimg); break;
- default: return;
- }
-
- if(!$newim) return;
-
- imagecopyresized($this->im, $newim, 0, 0, 0, 0,
- $this->image_width, $this->image_height,
- imagesx($newim), imagesy($newim));
- }
-
- /**
- * Scan the directory for a background image to use
- */
- protected function getBackgroundFromDirectory()
- {
- $images = array();
-
- if ( ($dh = opendir($this->background_directory)) !== false) {
- while (($file = readdir($dh)) !== false) {
- if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
- }
-
- closedir($dh);
-
- if (sizeof($images) > 0) {
- return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)];
- }
- }
-
- return false;
- }
-
- /**
- * Generates the code or math problem and saves the value to the session
- */
- public function createCode()
- {
- $this->code = false;
-
- switch($this->captcha_type) {
- case self::SI_CAPTCHA_MATHEMATIC:
- {
- do {
- $signs = array('+', '-', 'x');
- $left = mt_rand(1, 10);
- $right = mt_rand(1, 5);
- $sign = $signs[mt_rand(0, 2)];
-
- switch($sign) {
- case 'x': $c = $left * $right; break;
- case '-': $c = $left - $right; break;
- default: $c = $left + $right; break;
- }
- } while ($c <= 0); // no negative #'s or 0
-
- $this->code = $c;
- $this->code_display = "$left $sign $right";
- break;
- }
-
- case self::SI_CAPTCHA_WORDS:
- $words = $this->readCodeFromFile(2);
- $this->code = implode(' ', $words);
- $this->code_display = $this->code;
- break;
-
- default:
- {
- if ($this->use_wordlist && is_readable($this->wordlist_file)) {
- $this->code = $this->readCodeFromFile();
- }
-
- if ($this->code == false) {
- $this->code = $this->generateCode($this->code_length);
- }
-
- $this->code_display = $this->code;
- $this->code = ($this->case_sensitive) ? $this->code : strtolower($this->code);
- } // default
- }
-
- $this->saveData();
- }
-
- /**
- * Draws the captcha code on the image
- */
- protected function drawWord()
- {
- $width2 = $this->image_width * $this->iscale;
- $height2 = $this->image_height * $this->iscale;
-
- if (!is_readable($this->ttf_file)) {
- imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
- } else {
- if ($this->perturbation > 0) {
- $font_size = $height2 * .4;
- $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
- $tx = $bb[4] - $bb[0];
- $ty = $bb[5] - $bb[1];
- $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
- $y = round($height2 / 2 - $ty / 2 - $bb[1]);
-
- imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
- } else {
- $font_size = $this->image_height * .4;
- $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
- $tx = $bb[4] - $bb[0];
- $ty = $bb[5] - $bb[1];
- $x = floor($this->image_width / 2 - $tx / 2 - $bb[0]);
- $y = round($this->image_height / 2 - $ty / 2 - $bb[1]);
-
- imagettftext($this->im, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
- }
- }
-
- // DEBUG
- //$this->im = $this->tmpimg;
- //$this->output();
-
- }
-
- /**
- * Copies the captcha image to the final image with distortion applied
- */
- protected function distortedCopy()
- {
- $numpoles = 3; // distortion factor
- // make array of poles AKA attractor points
- for ($i = 0; $i < $numpoles; ++ $i) {
- $px[$i] = mt_rand($this->image_width * 0.2, $this->image_width * 0.8);
- $py[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
- $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
- $tmp = ((- $this->frand()) * 0.15) - .15;
- $amp[$i] = $this->perturbation * $tmp;
- }
-
- $bgCol = imagecolorat($this->tmpimg, 0, 0);
- $width2 = $this->iscale * $this->image_width;
- $height2 = $this->iscale * $this->image_height;
- imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
- // loop over $img pixels, take pixels from $tmpimg with distortion field
- for ($ix = 0; $ix < $this->image_width; ++ $ix) {
- for ($iy = 0; $iy < $this->image_height; ++ $iy) {
- $x = $ix;
- $y = $iy;
- for ($i = 0; $i < $numpoles; ++ $i) {
- $dx = $ix - $px[$i];
- $dy = $iy - $py[$i];
- if ($dx == 0 && $dy == 0) {
- continue;
- }
- $r = sqrt($dx * $dx + $dy * $dy);
- if ($r > $rad[$i]) {
- continue;
- }
- $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
- $x += $dx * $rscale;
- $y += $dy * $rscale;
- }
- $c = $bgCol;
- $x *= $this->iscale;
- $y *= $this->iscale;
- if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
- $c = imagecolorat($this->tmpimg, $x, $y);
- }
- if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
- imagesetpixel($this->im, $ix, $iy, $c);
- }
- }
- }
- }
-
- /**
- * Draws distorted lines on the image
- */
- protected function drawLines()
- {
- for ($line = 0; $line < $this->num_lines; ++ $line) {
- $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
- $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
- $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
-
- $theta = ($this->frand() - 0.5) * M_PI * 0.7;
- $w = $this->image_width;
- $len = mt_rand($w * 0.4, $w * 0.7);
- $lwid = mt_rand(0, 2);
-
- $k = $this->frand() * 0.6 + 0.2;
- $k = $k * $k * 0.5;
- $phi = $this->frand() * 6.28;
- $step = 0.5;
- $dx = $step * cos($theta);
- $dy = $step * sin($theta);
- $n = $len / $step;
- $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
- $x0 = $x - 0.5 * $len * cos($theta);
- $y0 = $y - 0.5 * $len * sin($theta);
-
- $ldx = round(- $dy * $lwid);
- $ldy = round($dx * $lwid);
-
- for ($i = 0; $i < $n; ++ $i) {
- $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
- $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
- imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
- }
- }
- }
-
- /**
- * Draws random noise on the image
- */
- protected function drawNoise()
- {
- if ($this->noise_level > 10) {
- $noise_level = 10;
- } else {
- $noise_level = $this->noise_level;
- }
-
- $t0 = microtime(true);
-
- $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale
-
- $points = $this->image_width * $this->image_height * $this->iscale;
- $height = $this->image_height * $this->iscale;
- $width = $this->image_width * $this->iscale;
- for ($i = 0; $i < $noise_level; ++$i) {
- $x = mt_rand(10, $width);
- $y = mt_rand(10, $height);
- $size = mt_rand(7, 10);
- if ($x - $size <= 0 && $y - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy
- imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE);
- }
-
- $t1 = microtime(true);
-
- $t = $t1 - $t0;
-
- /*
- // DEBUG
- imagestring($this->tmpimg, 5, 25, 30, "$t", $this->gdnoisecolor);
- header('content-type: image/png');
- imagepng($this->tmpimg);
- exit;
- */
- }
-
- /**
- * Print signature text on image
- */
- protected function addSignature()
- {
- $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
- $textlen = $bbox[2] - $bbox[0];
- $x = $this->image_width - $textlen - 5;
- $y = $this->image_height - 3;
-
- imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
- }
-
- /**
- * Sends the appropriate image and cache headers and outputs image to the browser
- */
- protected function output()
- {
- if ($this->canSendHeaders() || $this->send_headers == false) {
- if ($this->send_headers) {
- // only send the content-type headers if no headers have been output
- // this will ease debugging on misconfigured servers where warnings
- // may have been output which break the image and prevent easily viewing
- // source to see the error.
- header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
- header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
- header("Cache-Control: no-store, no-cache, must-revalidate");
- header("Cache-Control: post-check=0, pre-check=0", false);
- header("Pragma: no-cache");
- }
-
+ }
+
+ /**
+ * The main image drawing routing, responsible for constructing the entire image and serving it
+ */
+ protected function doImage()
+ {
+ if( ($this->use_transparent_text == true || $this->bgimg != '') && function_exists('imagecreatetruecolor')) {
+ $imagecreate = 'imagecreatetruecolor';
+ } else {
+ $imagecreate = 'imagecreate';
+ }
+
+ $this->im = $imagecreate($this->image_width, $this->image_height);
+ $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
+
+ $this->allocateColors();
+ imagepalettecopy($this->tmpimg, $this->im);
+
+ $this->setBackground();
+
+ $code = '';
+
+ if ($this->getCaptchaId(false) !== null) {
+ // a captcha Id was supplied
+
+ // check to see if a display_value for the captcha image was set
+ if (is_string($this->display_value) && strlen($this->display_value) > 0) {
+ $this->code_display = $this->display_value;
+ $this->code = ($this->case_sensitive) ?
+ $this->display_value :
+ strtolower($this->display_value);
+ $code = $this->code;
+ } else if ($this->openDatabase()) {
+ // no display_value, check the database for existing captchaId
+ $code = $this->getCodeFromDatabase();
+
+ // got back a result from the database with a valid code for captchaId
+ if (is_array($code)) {
+ $this->code = $code['code'];
+ $this->code_display = $code['code_disp'];
+ $code = $code['code'];
+ }
+ }
+ }
+
+ if ($code == '') {
+ // if the code was not set using display_value or was not found in
+ // the database, create a new code
+ $this->createCode();
+ }
+
+ if ($this->noise_level > 0) {
+ $this->drawNoise();
+ }
+
+ $this->drawWord();
+
+ if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
+ $this->distortedCopy();
+ }
+
+ if ($this->num_lines > 0) {
+ $this->drawLines();
+ }
+
+ if (trim($this->image_signature) != '') {
+ $this->addSignature();
+ }
+
+ $this->output();
+ }
+
+ /**
+ * Allocate the colors to be used for the image
+ */
+ protected function allocateColors()
+ {
+ // allocate bg color first for imagecreate
+ $this->gdbgcolor = imagecolorallocate($this->im,
+ $this->image_bg_color->r,
+ $this->image_bg_color->g,
+ $this->image_bg_color->b);
+
+ $alpha = intval($this->text_transparency_percentage / 100 * 127);
+
+ if ($this->use_transparent_text == true) {
+ $this->gdtextcolor = imagecolorallocatealpha($this->im,
+ $this->text_color->r,
+ $this->text_color->g,
+ $this->text_color->b,
+ $alpha);
+ $this->gdlinecolor = imagecolorallocatealpha($this->im,
+ $this->line_color->r,
+ $this->line_color->g,
+ $this->line_color->b,
+ $alpha);
+ $this->gdnoisecolor = imagecolorallocatealpha($this->im,
+ $this->noise_color->r,
+ $this->noise_color->g,
+ $this->noise_color->b,
+ $alpha);
+ } else {
+ $this->gdtextcolor = imagecolorallocate($this->im,
+ $this->text_color->r,
+ $this->text_color->g,
+ $this->text_color->b);
+ $this->gdlinecolor = imagecolorallocate($this->im,
+ $this->line_color->r,
+ $this->line_color->g,
+ $this->line_color->b);
+ $this->gdnoisecolor = imagecolorallocate($this->im,
+ $this->noise_color->r,
+ $this->noise_color->g,
+ $this->noise_color->b);
+ }
+
+ $this->gdsignaturecolor = imagecolorallocate($this->im,
+ $this->signature_color->r,
+ $this->signature_color->g,
+ $this->signature_color->b);
+
+ }
+
+ /**
+ * The the background color, or background image to be used
+ */
+ protected function setBackground()
+ {
+ // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
+ imagefilledrectangle($this->im, 0, 0,
+ $this->image_width, $this->image_height,
+ $this->gdbgcolor);
+ imagefilledrectangle($this->tmpimg, 0, 0,
+ $this->image_width * $this->iscale, $this->image_height * $this->iscale,
+ $this->gdbgcolor);
+
+ if ($this->bgimg == '') {
+ if ($this->background_directory != null &&
+ is_dir($this->background_directory) &&
+ is_readable($this->background_directory))
+ {
+ $img = $this->getBackgroundFromDirectory();
+ if ($img != false) {
+ $this->bgimg = $img;
+ }
+ }
+ }
+
+ if ($this->bgimg == '') {
+ return;
+ }
+
+ $dat = @getimagesize($this->bgimg);
+ if($dat == false) {
+ return;
+ }
+
+ switch($dat[2]) {
+ case 1: $newim = @imagecreatefromgif($this->bgimg); break;
+ case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
+ case 3: $newim = @imagecreatefrompng($this->bgimg); break;
+ default: return;
+ }
+
+ if(!$newim) return;
+
+ imagecopyresized($this->im, $newim, 0, 0, 0, 0,
+ $this->image_width, $this->image_height,
+ imagesx($newim), imagesy($newim));
+ }
+
+ /**
+ * Scan the directory for a background image to use
+ */
+ protected function getBackgroundFromDirectory()
+ {
+ $images = array();
+
+ if ( ($dh = opendir($this->background_directory)) !== false) {
+ while (($file = readdir($dh)) !== false) {
+ if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
+ }
+
+ closedir($dh);
+
+ if (sizeof($images) > 0) {
+ return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Generates the code or math problem and saves the value to the session
+ */
+ public function createCode()
+ {
+ $this->code = false;
+
+ switch($this->captcha_type) {
+ case self::SI_CAPTCHA_MATHEMATIC:
+ {
+ do {
+ $signs = array('+', '-', 'x');
+ $left = mt_rand(1, 10);
+ $right = mt_rand(1, 5);
+ $sign = $signs[mt_rand(0, 2)];
+
+ switch($sign) {
+ case 'x': $c = $left * $right; break;
+ case '-': $c = $left - $right; break;
+ default: $c = $left + $right; break;
+ }
+ } while ($c <= 0); // no negative #'s or 0
+
+ $this->code = $c;
+ $this->code_display = "$left $sign $right";
+ break;
+ }
+
+ case self::SI_CAPTCHA_WORDS:
+ $words = $this->readCodeFromFile(2);
+ $this->code = implode(' ', $words);
+ $this->code_display = $this->code;
+ break;
+
+ default:
+ {
+ if ($this->use_wordlist && is_readable($this->wordlist_file)) {
+ $this->code = $this->readCodeFromFile();
+ }
+
+ if ($this->code == false) {
+ $this->code = $this->generateCode($this->code_length);
+ }
+
+ $this->code_display = $this->code;
+ $this->code = ($this->case_sensitive) ? $this->code : strtolower($this->code);
+ } // default
+ }
+
+ $this->saveData();
+ }
+
+ /**
+ * Draws the captcha code on the image
+ */
+ protected function drawWord()
+ {
+ $width2 = $this->image_width * $this->iscale;
+ $height2 = $this->image_height * $this->iscale;
+
+ if (!is_readable($this->ttf_file)) {
+ imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
+ } else {
+ if ($this->perturbation > 0) {
+ $font_size = $height2 * .4;
+ $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
+ $tx = $bb[4] - $bb[0];
+ $ty = $bb[5] - $bb[1];
+ $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
+ $y = round($height2 / 2 - $ty / 2 - $bb[1]);
+
+ imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
+ } else {
+ $font_size = $this->image_height * .4;
+ $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
+ $tx = $bb[4] - $bb[0];
+ $ty = $bb[5] - $bb[1];
+ $x = floor($this->image_width / 2 - $tx / 2 - $bb[0]);
+ $y = round($this->image_height / 2 - $ty / 2 - $bb[1]);
+
+ imagettftext($this->im, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
+ }
+ }
+
+ // DEBUG
+ //$this->im = $this->tmpimg;
+ //$this->output();
+
+ }
+
+ /**
+ * Copies the captcha image to the final image with distortion applied
+ */
+ protected function distortedCopy()
+ {
+ $numpoles = 3; // distortion factor
+ // make array of poles AKA attractor points
+ for ($i = 0; $i < $numpoles; ++ $i) {
+ $px[$i] = mt_rand($this->image_width * 0.2, $this->image_width * 0.8);
+ $py[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
+ $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
+ $tmp = ((- $this->frand()) * 0.15) - .15;
+ $amp[$i] = $this->perturbation * $tmp;
+ }
+
+ $bgCol = imagecolorat($this->tmpimg, 0, 0);
+ $width2 = $this->iscale * $this->image_width;
+ $height2 = $this->iscale * $this->image_height;
+ imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
+ // loop over $img pixels, take pixels from $tmpimg with distortion field
+ for ($ix = 0; $ix < $this->image_width; ++ $ix) {
+ for ($iy = 0; $iy < $this->image_height; ++ $iy) {
+ $x = $ix;
+ $y = $iy;
+ for ($i = 0; $i < $numpoles; ++ $i) {
+ $dx = $ix - $px[$i];
+ $dy = $iy - $py[$i];
+ if ($dx == 0 && $dy == 0) {
+ continue;
+ }
+ $r = sqrt($dx * $dx + $dy * $dy);
+ if ($r > $rad[$i]) {
+ continue;
+ }
+ $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
+ $x += $dx * $rscale;
+ $y += $dy * $rscale;
+ }
+ $c = $bgCol;
+ $x *= $this->iscale;
+ $y *= $this->iscale;
+ if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
+ $c = imagecolorat($this->tmpimg, $x, $y);
+ }
+ if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
+ imagesetpixel($this->im, $ix, $iy, $c);
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws distorted lines on the image
+ */
+ protected function drawLines()
+ {
+ for ($line = 0; $line < $this->num_lines; ++ $line) {
+ $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
+ $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
+ $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
+
+ $theta = ($this->frand() - 0.5) * M_PI * 0.7;
+ $w = $this->image_width;
+ $len = mt_rand($w * 0.4, $w * 0.7);
+ $lwid = mt_rand(0, 2);
+
+ $k = $this->frand() * 0.6 + 0.2;
+ $k = $k * $k * 0.5;
+ $phi = $this->frand() * 6.28;
+ $step = 0.5;
+ $dx = $step * cos($theta);
+ $dy = $step * sin($theta);
+ $n = $len / $step;
+ $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
+ $x0 = $x - 0.5 * $len * cos($theta);
+ $y0 = $y - 0.5 * $len * sin($theta);
+
+ $ldx = round(- $dy * $lwid);
+ $ldy = round($dx * $lwid);
+
+ for ($i = 0; $i < $n; ++ $i) {
+ $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
+ $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
+ imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
+ }
+ }
+ }
+
+ /**
+ * Draws random noise on the image
+ */
+ protected function drawNoise()
+ {
+ if ($this->noise_level > 10) {
+ $noise_level = 10;
+ } else {
+ $noise_level = $this->noise_level;
+ }
+
+ $t0 = microtime(true);
+
+ $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale
+
+ $points = $this->image_width * $this->image_height * $this->iscale;
+ $height = $this->image_height * $this->iscale;
+ $width = $this->image_width * $this->iscale;
+ for ($i = 0; $i < $noise_level; ++$i) {
+ $x = mt_rand(10, $width);
+ $y = mt_rand(10, $height);
+ $size = mt_rand(7, 10);
+ if ($x - $size <= 0 && $y - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy
+ imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE);
+ }
+
+ $t1 = microtime(true);
+
+ $t = $t1 - $t0;
+
+ /*
+ // DEBUG
+ imagestring($this->tmpimg, 5, 25, 30, "$t", $this->gdnoisecolor);
+ header('content-type: image/png');
+ imagepng($this->tmpimg);
+ exit;
+ */
+ }
+
+ /**
+ * Print signature text on image
+ */
+ protected function addSignature()
+ {
+ $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
+ $textlen = $bbox[2] - $bbox[0];
+ $x = $this->image_width - $textlen - 5;
+ $y = $this->image_height - 3;
+
+ imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
+ }
+
+ /**
+ * Sends the appropriate image and cache headers and outputs image to the browser
+ */
+ protected function output()
+ {
+ if ($this->canSendHeaders() || $this->send_headers == false) {
+ if ($this->send_headers) {
+ // only send the content-type headers if no headers have been output
+ // this will ease debugging on misconfigured servers where warnings
+ // may have been output which break the image and prevent easily viewing
+ // source to see the error.
+ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
+ header("Cache-Control: no-store, no-cache, must-revalidate");
+ header("Cache-Control: post-check=0, pre-check=0", false);
+ header("Pragma: no-cache");
+ }
+
switch ($this->image_type) {
case self::SI_IMAGE_JPEG:
if ($this->send_headers) header("Content-Type: image/jpeg");
@@ -1388,653 +1388,653 @@ class Securimage
if ($this->send_headers) header("Content-Type: image/png");
imagepng($this->im);
break;
- }
+ }
} else {
echo ''
.'Failed to generate captcha image, content has already been '
.'output. This is most likely due to misconfiguration or '
- .'a PHP error was sent to the browser.';
- }
-
- imagedestroy($this->im);
- restore_error_handler();
-
- if (!$this->no_exit) exit;
- }
-
- /**
- * Gets the code and returns the binary audio file for the stored captcha code
- *
- * @return The audio representation of the captcha in Wav format
- */
- protected function getAudibleCode()
- {
- $letters = array();
- $code = $this->getCode(true, true);
-
- if ($code['code'] == '') {
- if (strlen($this->display_value) > 0) {
- $code = array('code' => $this->display_value, 'display' => $this->display_value);
- } else {
- $this->createCode();
- $code = $this->getCode(true);
- }
- }
-
- if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
- $math = true;
-
- $left = $eq[1];
- $sign = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
- $right = $eq[3];
-
- $letters = array($left, $sign, $right);
- } else {
- $math = false;
-
- $length = strlen($code['display']);
-
- for($i = 0; $i < $length; ++$i) {
- $letter = $code['display']{$i};
- $letters[] = $letter;
- }
- }
-
- try {
- return $this->generateWAV($letters);
- } catch(Exception $ex) {
- throw $ex;
- }
- }
-
- /**
- * Gets a captcha code from a wordlist
- */
- protected function readCodeFromFile($numWords = 1)
- {
- $fp = fopen($this->wordlist_file, 'rb');
- if (!$fp) return false;
-
- $fsize = filesize($this->wordlist_file);
- if ($fsize < 128) return false; // too small of a list to be effective
-
- if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1;
-
- $words = array();
- $i = 0;
- do {
- fseek($fp, mt_rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64
- $data = fread($fp, 64); // read a chunk from our random position
- $data = preg_replace("/\r?\n/", "\n", $data);
-
- $start = @strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position
- $end = @strpos($data, "\n", $start); // find end of word
-
- if ($start === false) {
- // picked start position at end of file
- continue;
- } else if ($end === false) {
- $end = strlen($data);
- }
-
- $word = strtolower(substr($data, $start, $end - $start)); // return a line of the file
- $words[] = $word;
- } while (++$i < $numWords);
-
- fclose($fp);
-
- if ($numWords < 2) {
- return $words[0];
- } else {
- return $words;
- }
- }
-
- /**
- * Generates a random captcha code from the set character set
- */
- protected function generateCode()
- {
- $code = '';
-
- if (function_exists('mb_strlen')) {
- for($i = 1, $cslen = mb_strlen($this->charset); $i <= $this->code_length; ++$i) {
- $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8');
- }
- } else {
- for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) {
- $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1);
- }
- }
-
- return $code;
- }
-
- /**
- * Checks the entered code against the value stored in the session or sqlite database, handles case sensitivity
- * Also clears the stored codes if the code was entered correctly to prevent re-use
- */
- protected function validate()
- {
- if (!is_string($this->code) || strlen($this->code) == 0) {
- $code = $this->getCode();
- // returns stored code, or an empty string if no stored code was found
- // checks the session and database if enabled
- } else {
- $code = $this->code;
- }
-
- if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
- // case sensitive was set from securimage_show.php but not in class
- // the code saved in the session has capitals so set case sensitive to true
- $this->case_sensitive = true;
- }
-
- $code_entered = trim( (($this->case_sensitive) ? $this->code_entered
- : strtolower($this->code_entered))
- );
- $this->correct_code = false;
-
- if ($code != '') {
- if (strpos($code, ' ') !== false) {
- // for multi word captchas, remove more than once space from input
- $code_entered = preg_replace('/\s+/', ' ', $code_entered);
- $code_entered = strtolower($code_entered);
- }
-
- if ($code == $code_entered) {
- $this->correct_code = true;
- if ($this->no_session != true) {
- $_SESSION['securimage_code_value'][$this->namespace] = '';
- $_SESSION['securimage_code_ctime'][$this->namespace] = '';
- }
- $this->clearCodeFromDatabase();
- }
- }
- }
-
- /**
- * Save data to session namespace and database if used
- */
- protected function saveData()
- {
- if ($this->no_session != true) {
- if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
- // fix for migration from v2 - v3
- unset($_SESSION['securimage_code_value']);
- unset($_SESSION['securimage_code_ctime']);
- }
-
- $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
- $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
- $_SESSION['securimage_code_ctime'][$this->namespace] = time();
- }
-
- if ($this->use_database) {
- $this->saveCodeToDatabase();
- }
- }
-
- /**
- * Saves the code to the sqlite database
- */
- protected function saveCodeToDatabase()
- {
- $success = false;
- $this->openDatabase();
-
- if ($this->use_database && $this->pdo_conn) {
- $id = $this->getCaptchaId(false);
- $ip = $_SERVER['REMOTE_ADDR'];
-
- if (empty($id)) {
- $id = $ip;
- }
-
- $time = time();
- $code = $this->code;
- $code_disp = $this->code_display;
-
- // This is somewhat expensive in PDO Sqlite3 (when there is something to delete)
- $this->clearCodeFromDatabase();
-
- $query = "INSERT INTO {$this->database_table} ("
- ."id, code, code_display, namespace, created) "
- ."VALUES(?, ?, ?, ?, ?)";
-
- $stmt = $this->pdo_conn->prepare($query);
- $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
-
- if (!$success) {
- $err = $stmt->errorInfo();
- trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING);
- }
- }
-
- return $success !== false;
- }
-
- /**
- * Open sqlite database
- */
- protected function openDatabase()
- {
- $this->pdo_conn = false;
-
- if ($this->use_database) {
- $pdo_extension = 'PDO_' . strtoupper($this->database_driver);
-
- if (!extension_loaded($pdo_extension)) {
- trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING);
- return false;
- }
- }
-
- if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
- if (!file_exists($this->database_file)) {
- $fp = fopen($this->database_file, 'w+');
- if (!$fp) {
- $err = error_get_last();
- trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING);
- return false;
- }
- fclose($fp);
- chmod($this->database_file, 0666);
- } else if (!is_writeable($this->database_file)) {
- trigger_error("Securimage does not have read/write access to database file '{$this->database_file}. Make sure permissions are 0666 and writeable by user '" . get_current_user() . "'", E_USER_WARNING);
- return false;
- }
- }
-
- $dsn = $this->getDsn();
-
- try {
- $options = array();
- $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
- } catch (PDOException $pdoex) {
- trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
- return false;
- }
-
- try {
- if (!$this->checkTablesExist()) {
- // create tables...
- $this->createDatabaseTables();
- }
- } catch (Exception $ex) {
- trigger_error($ex->getMessage(), E_USER_WARNING);
- $this->pdo_conn = null;
- return false;
- }
-
- if (mt_rand(0, 100) / 100.0 == 1.0) {
- $this->purgeOldCodesFromDatabase();
- }
-
- return $this->pdo_conn;
- }
-
- protected function getDsn()
- {
- $dsn = sprintf('%s:', $this->database_driver);
-
- switch($this->database_driver) {
- case self::SI_DRIVER_SQLITE3:
- $dsn .= $this->database_file;
- break;
-
- case self::SI_DRIVER_MYSQL:
- case self::SI_DRIVER_PGSQL:
- $dsn .= sprintf('host=%s;dbname=%s',
- $this->database_host,
- $this->database_name);
- break;
-
- }
-
- return $dsn;
- }
-
- protected function checkTablesExist()
- {
- $table = $this->pdo_conn->quote($this->database_table);
-
- switch($this->database_driver) {
- case self::SI_DRIVER_SQLITE3:
- // query row count for sqlite, PRAGMA queries seem to return no
- // rowCount using PDO even if there are rows returned
- $query = "SELECT COUNT(id) FROM $table";
- break;
-
- case self::SI_DRIVER_MYSQL:
- $query = "SHOW TABLES LIKE $table";
- break;
-
- case self::SI_DRIVER_PGSQL:
- $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;";
- break;
- }
-
- $result = $this->pdo_conn->query($query);
-
- if (!$result) {
- $err = $this->pdo_conn->errorInfo();
-
- if ($this->database_driver == self::SI_DRIVER_SQLITE3 &&
- $err[1] === 1 && strpos($err[2], 'no such table') !== false)
- {
- return false;
- }
-
- throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}");
- } else if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
- // successful here regardless of row count for sqlite
- return true;
- } else if ($result->rowCount() == 0) {
- return false;
- } else {
- return true;
- }
- }
-
- protected function createDatabaseTables()
- {
- $queries = array();
-
- switch($this->database_driver) {
- case self::SI_DRIVER_SQLITE3:
- $queries[] = "CREATE TABLE \"{$this->database_table}\" (
- id VARCHAR(40),
- namespace VARCHAR(32) NOT NULL,
- code VARCHAR(32) NOT NULL,
- code_display VARCHAR(32) NOT NULL,
- created INTEGER NOT NULL,
- PRIMARY KEY(id, namespace)
- )";
-
- $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)";
- break;
-
- case self::SI_DRIVER_MYSQL:
- $queries[] = "CREATE TABLE `{$this->database_table}` (
- `id` VARCHAR(40) NOT NULL,
- `namespace` VARCHAR(32) NOT NULL,
- `code` VARCHAR(32) NOT NULL,
- `code_display` VARCHAR(32) NOT NULL,
- `created` INT NOT NULL,
- PRIMARY KEY(id, namespace),
- INDEX(created)
- )";
- break;
-
- case self::SI_DRIVER_PGSQL:
- $queries[] = "CREATE TABLE {$this->database_table} (
- id character varying(40) NOT NULL,
- namespace character varying(32) NOT NULL,
- code character varying(32) NOT NULL,
- code_display character varying(32) NOT NULL,
- created integer NOT NULL,
- CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace)
- )";
-
- $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);";
- break;
- }
-
- $this->pdo_conn->beginTransaction();
-
- foreach($queries as $query) {
- $result = $this->pdo_conn->query($query);
-
- if (!$result) {
- $err = $this->pdo_conn->errorInfo();
- trigger_error("Failed to create table. {$err[1]}: {$err[2]}", E_USER_WARNING);
- $this->pdo_conn->rollBack();
- $this->pdo_conn = false;
- return false;
- }
- }
-
- $this->pdo_conn->commit();
-
- return true;
- }
-
- /**
- * Get a code from the sqlite database for ip address/captchaId.
- *
- * @return string|array Empty string if no code was found or has expired,
- * otherwise returns the stored captcha code. If a captchaId is set, this
- * returns an array with indices "code" and "code_disp"
- */
- protected function getCodeFromDatabase()
- {
- $code = '';
-
- if ($this->use_database == true && $this->pdo_conn) {
- if (Securimage::$_captchaId !== null) {
- $query = "SELECT * FROM {$this->database_table} WHERE id = ?";
- $stmt = $this->pdo_conn->prepare($query);
- $result = $stmt->execute(array(Securimage::$_captchaId));
- } else {
- $ip = $_SERVER['REMOTE_ADDR'];
- $ns = $this->namespace;
-
- // ip is stored in id column when no captchaId
- $query = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?";
- $stmt = $this->pdo_conn->prepare($query);
- $result = $stmt->execute(array($ip, $ns));
- }
-
- if (!$result) {
- $err = $this->pdo_conn->errorInfo();
- trigger_error("Failed to select code from database. {$err[0]}: {$err[1]}", E_USER_WARNING);
- } else {
- if ( ($row = $stmt->fetch()) !== false ) {
- if (false == $this->isCodeExpired($row['created'])) {
- if (Securimage::$_captchaId !== null) {
- // return an array when using captchaId
- $code = array('code' => $row['code'],
- 'code_disp' => $row['code_display']);
- } else {
- $code = $row['code'];
- }
- }
- }
- }
- }
-
- return $code;
- }
-
- /**
- * Remove an entered code from the database
- */
- protected function clearCodeFromDatabase()
- {
- if ($this->pdo_conn) {
- $ip = $_SERVER['REMOTE_ADDR'];
- $ns = $this->pdo_conn->quote($this->namespace);
- $id = Securimage::$_captchaId;
-
- if (empty($id)) {
- $id = $ip; // if no captchaId set, IP address is captchaId.
- }
-
- $id = $this->pdo_conn->quote($id);
-
- $query = sprintf("DELETE FROM %s WHERE id = %s AND namespace = %s",
- $this->database_table, $id, $ns);
-
- $result = $this->pdo_conn->query($query);
- if (!$result) {
- trigger_error("Failed to delete code from database.", E_USER_WARNING);
- }
- }
- }
-
- /**
- * Deletes old codes from sqlite database
- */
- protected function purgeOldCodesFromDatabase()
- {
- if ($this->use_database && $this->pdo_conn) {
- $now = time();
- $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
-
- $query = sprintf("DELETE FROM %s WHERE %s - created > %s",
- $this->database_table,
- $this->pdo_conn->quote($now, PDO::PARAM_INT),
- $this->pdo_conn->quote($limit, PDO::PARAM_INT));
-
- $result = $this->pdo_conn->query($query);
- }
- }
-
- /**
- * Checks to see if the captcha code has expired and cannot be used
- * @param unknown_type $creation_time
- */
- protected function isCodeExpired($creation_time)
- {
- $expired = true;
-
- if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
- $expired = false;
- } else if (time() - $creation_time < $this->expiry_time) {
- $expired = false;
- }
-
- return $expired;
- }
-
- /**
- * Generate a wav file given the $letters in the code
- * @todo Add ability to merge 2 sound files together to have random background sounds
- * @param array $letters
- * @return string The binary contents of the wav file
- */
- protected function generateWAV($letters)
- {
- $wavCaptcha = new WavFile();
- $first = true; // reading first wav file
-
- foreach ($letters as $letter) {
- $letter = strtoupper($letter);
-
- try {
- $l = new WavFile($this->audio_path . '/' . $letter . '.wav');
-
- if ($first) {
- // set sample rate, bits/sample, and # of channels for file based on first letter
- $wavCaptcha->setSampleRate($l->getSampleRate())
- ->setBitsPerSample($l->getBitsPerSample())
- ->setNumChannels($l->getNumChannels());
- $first = false;
- }
-
- // append letter to the captcha audio
- $wavCaptcha->appendWav($l);
-
- // random length of silence between $audio_gap_min and $audio_gap_max
- if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
- $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
- }
+ .'a PHP error was sent to the browser.';
+ }
+
+ imagedestroy($this->im);
+ restore_error_handler();
+
+ if (!$this->no_exit) exit;
+ }
+
+ /**
+ * Gets the code and returns the binary audio file for the stored captcha code
+ *
+ * @return The audio representation of the captcha in Wav format
+ */
+ protected function getAudibleCode()
+ {
+ $letters = array();
+ $code = $this->getCode(true, true);
+
+ if ($code['code'] == '') {
+ if (strlen($this->display_value) > 0) {
+ $code = array('code' => $this->display_value, 'display' => $this->display_value);
+ } else {
+ $this->createCode();
+ $code = $this->getCode(true);
+ }
+ }
+
+ if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
+ $math = true;
+
+ $left = $eq[1];
+ $sign = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
+ $right = $eq[3];
+
+ $letters = array($left, $sign, $right);
+ } else {
+ $math = false;
+
+ $length = strlen($code['display']);
+
+ for($i = 0; $i < $length; ++$i) {
+ $letter = $code['display']{$i};
+ $letters[] = $letter;
+ }
+ }
+
+ try {
+ return $this->generateWAV($letters);
+ } catch(Exception $ex) {
+ throw $ex;
+ }
+ }
+
+ /**
+ * Gets a captcha code from a wordlist
+ */
+ protected function readCodeFromFile($numWords = 1)
+ {
+ $fp = fopen($this->wordlist_file, 'rb');
+ if (!$fp) return false;
+
+ $fsize = filesize($this->wordlist_file);
+ if ($fsize < 128) return false; // too small of a list to be effective
+
+ if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1;
+
+ $words = array();
+ $i = 0;
+ do {
+ fseek($fp, mt_rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64
+ $data = fread($fp, 64); // read a chunk from our random position
+ $data = preg_replace("/\r?\n/", "\n", $data);
+
+ $start = @strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position
+ $end = @strpos($data, "\n", $start); // find end of word
+
+ if ($start === false) {
+ // picked start position at end of file
+ continue;
+ } else if ($end === false) {
+ $end = strlen($data);
+ }
+
+ $word = strtolower(substr($data, $start, $end - $start)); // return a line of the file
+ $words[] = $word;
+ } while (++$i < $numWords);
+
+ fclose($fp);
+
+ if ($numWords < 2) {
+ return $words[0];
+ } else {
+ return $words;
+ }
+ }
+
+ /**
+ * Generates a random captcha code from the set character set
+ */
+ protected function generateCode()
+ {
+ $code = '';
+
+ if (function_exists('mb_strlen')) {
+ for($i = 1, $cslen = mb_strlen($this->charset); $i <= $this->code_length; ++$i) {
+ $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8');
+ }
+ } else {
+ for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) {
+ $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1);
+ }
+ }
+
+ return $code;
+ }
+
+ /**
+ * Checks the entered code against the value stored in the session or sqlite database, handles case sensitivity
+ * Also clears the stored codes if the code was entered correctly to prevent re-use
+ */
+ protected function validate()
+ {
+ if (!is_string($this->code) || strlen($this->code) == 0) {
+ $code = $this->getCode();
+ // returns stored code, or an empty string if no stored code was found
+ // checks the session and database if enabled
+ } else {
+ $code = $this->code;
+ }
+
+ if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
+ // case sensitive was set from securimage_show.php but not in class
+ // the code saved in the session has capitals so set case sensitive to true
+ $this->case_sensitive = true;
+ }
+
+ $code_entered = trim( (($this->case_sensitive) ? $this->code_entered
+ : strtolower($this->code_entered))
+ );
+ $this->correct_code = false;
+
+ if ($code != '') {
+ if (strpos($code, ' ') !== false) {
+ // for multi word captchas, remove more than once space from input
+ $code_entered = preg_replace('/\s+/', ' ', $code_entered);
+ $code_entered = strtolower($code_entered);
+ }
+
+ if ($code == $code_entered) {
+ $this->correct_code = true;
+ if ($this->no_session != true) {
+ $_SESSION['securimage_code_value'][$this->namespace] = '';
+ $_SESSION['securimage_code_ctime'][$this->namespace] = '';
+ }
+ $this->clearCodeFromDatabase();
+ }
+ }
+ }
+
+ /**
+ * Save data to session namespace and database if used
+ */
+ protected function saveData()
+ {
+ if ($this->no_session != true) {
+ if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
+ // fix for migration from v2 - v3
+ unset($_SESSION['securimage_code_value']);
+ unset($_SESSION['securimage_code_ctime']);
+ }
+
+ $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
+ $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
+ $_SESSION['securimage_code_ctime'][$this->namespace] = time();
+ }
+
+ if ($this->use_database) {
+ $this->saveCodeToDatabase();
+ }
+ }
+
+ /**
+ * Saves the code to the sqlite database
+ */
+ protected function saveCodeToDatabase()
+ {
+ $success = false;
+ $this->openDatabase();
+
+ if ($this->use_database && $this->pdo_conn) {
+ $id = $this->getCaptchaId(false);
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ if (empty($id)) {
+ $id = $ip;
+ }
+
+ $time = time();
+ $code = $this->code;
+ $code_disp = $this->code_display;
+
+ // This is somewhat expensive in PDO Sqlite3 (when there is something to delete)
+ $this->clearCodeFromDatabase();
+
+ $query = "INSERT INTO {$this->database_table} ("
+ ."id, code, code_display, namespace, created) "
+ ."VALUES(?, ?, ?, ?, ?)";
+
+ $stmt = $this->pdo_conn->prepare($query);
+ $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
+
+ if (!$success) {
+ $err = $stmt->errorInfo();
+ trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING);
+ }
+ }
+
+ return $success !== false;
+ }
+
+ /**
+ * Open sqlite database
+ */
+ protected function openDatabase()
+ {
+ $this->pdo_conn = false;
+
+ if ($this->use_database) {
+ $pdo_extension = 'PDO_' . strtoupper($this->database_driver);
+
+ if (!extension_loaded($pdo_extension)) {
+ trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING);
+ return false;
+ }
+ }
+
+ if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
+ if (!file_exists($this->database_file)) {
+ $fp = fopen($this->database_file, 'w+');
+ if (!$fp) {
+ $err = error_get_last();
+ trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING);
+ return false;
+ }
+ fclose($fp);
+ chmod($this->database_file, 0666);
+ } else if (!is_writeable($this->database_file)) {
+ trigger_error("Securimage does not have read/write access to database file '{$this->database_file}. Make sure permissions are 0666 and writeable by user '" . get_current_user() . "'", E_USER_WARNING);
+ return false;
+ }
+ }
+
+ $dsn = $this->getDsn();
+
+ try {
+ $options = array();
+ $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
+ } catch (PDOException $pdoex) {
+ trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
+ return false;
+ }
+
+ try {
+ if (!$this->checkTablesExist()) {
+ // create tables...
+ $this->createDatabaseTables();
+ }
+ } catch (Exception $ex) {
+ trigger_error($ex->getMessage(), E_USER_WARNING);
+ $this->pdo_conn = null;
+ return false;
+ }
+
+ if (mt_rand(0, 100) / 100.0 == 1.0) {
+ $this->purgeOldCodesFromDatabase();
+ }
+
+ return $this->pdo_conn;
+ }
+
+ protected function getDsn()
+ {
+ $dsn = sprintf('%s:', $this->database_driver);
+
+ switch($this->database_driver) {
+ case self::SI_DRIVER_SQLITE3:
+ $dsn .= $this->database_file;
+ break;
+
+ case self::SI_DRIVER_MYSQL:
+ case self::SI_DRIVER_PGSQL:
+ $dsn .= sprintf('host=%s;dbname=%s',
+ $this->database_host,
+ $this->database_name);
+ break;
+
+ }
+
+ return $dsn;
+ }
+
+ protected function checkTablesExist()
+ {
+ $table = $this->pdo_conn->quote($this->database_table);
+
+ switch($this->database_driver) {
+ case self::SI_DRIVER_SQLITE3:
+ // query row count for sqlite, PRAGMA queries seem to return no
+ // rowCount using PDO even if there are rows returned
+ $query = "SELECT COUNT(id) FROM $table";
+ break;
+
+ case self::SI_DRIVER_MYSQL:
+ $query = "SHOW TABLES LIKE $table";
+ break;
+
+ case self::SI_DRIVER_PGSQL:
+ $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;";
+ break;
+ }
+
+ $result = $this->pdo_conn->query($query);
+
+ if (!$result) {
+ $err = $this->pdo_conn->errorInfo();
+
+ if ($this->database_driver == self::SI_DRIVER_SQLITE3 &&
+ $err[1] === 1 && strpos($err[2], 'no such table') !== false)
+ {
+ return false;
+ }
+
+ throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}");
+ } else if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
+ // successful here regardless of row count for sqlite
+ return true;
+ } else if ($result->rowCount() == 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ protected function createDatabaseTables()
+ {
+ $queries = array();
+
+ switch($this->database_driver) {
+ case self::SI_DRIVER_SQLITE3:
+ $queries[] = "CREATE TABLE \"{$this->database_table}\" (
+ id VARCHAR(40),
+ namespace VARCHAR(32) NOT NULL,
+ code VARCHAR(32) NOT NULL,
+ code_display VARCHAR(32) NOT NULL,
+ created INTEGER NOT NULL,
+ PRIMARY KEY(id, namespace)
+ )";
+
+ $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)";
+ break;
+
+ case self::SI_DRIVER_MYSQL:
+ $queries[] = "CREATE TABLE `{$this->database_table}` (
+ `id` VARCHAR(40) NOT NULL,
+ `namespace` VARCHAR(32) NOT NULL,
+ `code` VARCHAR(32) NOT NULL,
+ `code_display` VARCHAR(32) NOT NULL,
+ `created` INT NOT NULL,
+ PRIMARY KEY(id, namespace),
+ INDEX(created)
+ )";
+ break;
+
+ case self::SI_DRIVER_PGSQL:
+ $queries[] = "CREATE TABLE {$this->database_table} (
+ id character varying(40) NOT NULL,
+ namespace character varying(32) NOT NULL,
+ code character varying(32) NOT NULL,
+ code_display character varying(32) NOT NULL,
+ created integer NOT NULL,
+ CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace)
+ )";
+
+ $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);";
+ break;
+ }
+
+ $this->pdo_conn->beginTransaction();
+
+ foreach($queries as $query) {
+ $result = $this->pdo_conn->query($query);
+
+ if (!$result) {
+ $err = $this->pdo_conn->errorInfo();
+ trigger_error("Failed to create table. {$err[1]}: {$err[2]}", E_USER_WARNING);
+ $this->pdo_conn->rollBack();
+ $this->pdo_conn = false;
+ return false;
+ }
+ }
+
+ $this->pdo_conn->commit();
+
+ return true;
+ }
+
+ /**
+ * Get a code from the sqlite database for ip address/captchaId.
+ *
+ * @return string|array Empty string if no code was found or has expired,
+ * otherwise returns the stored captcha code. If a captchaId is set, this
+ * returns an array with indices "code" and "code_disp"
+ */
+ protected function getCodeFromDatabase()
+ {
+ $code = '';
+
+ if ($this->use_database == true && $this->pdo_conn) {
+ if (Securimage::$_captchaId !== null) {
+ $query = "SELECT * FROM {$this->database_table} WHERE id = ?";
+ $stmt = $this->pdo_conn->prepare($query);
+ $result = $stmt->execute(array(Securimage::$_captchaId));
+ } else {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $ns = $this->namespace;
+
+ // ip is stored in id column when no captchaId
+ $query = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?";
+ $stmt = $this->pdo_conn->prepare($query);
+ $result = $stmt->execute(array($ip, $ns));
+ }
+
+ if (!$result) {
+ $err = $this->pdo_conn->errorInfo();
+ trigger_error("Failed to select code from database. {$err[0]}: {$err[1]}", E_USER_WARNING);
+ } else {
+ if ( ($row = $stmt->fetch()) !== false ) {
+ if (false == $this->isCodeExpired($row['created'])) {
+ if (Securimage::$_captchaId !== null) {
+ // return an array when using captchaId
+ $code = array('code' => $row['code'],
+ 'code_disp' => $row['code_display']);
+ } else {
+ $code = $row['code'];
+ }
+ }
+ }
+ }
+ }
+
+ return $code;
+ }
+
+ /**
+ * Remove an entered code from the database
+ */
+ protected function clearCodeFromDatabase()
+ {
+ if ($this->pdo_conn) {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $ns = $this->pdo_conn->quote($this->namespace);
+ $id = Securimage::$_captchaId;
+
+ if (empty($id)) {
+ $id = $ip; // if no captchaId set, IP address is captchaId.
+ }
+
+ $id = $this->pdo_conn->quote($id);
+
+ $query = sprintf("DELETE FROM %s WHERE id = %s AND namespace = %s",
+ $this->database_table, $id, $ns);
+
+ $result = $this->pdo_conn->query($query);
+ if (!$result) {
+ trigger_error("Failed to delete code from database.", E_USER_WARNING);
+ }
+ }
+ }
+
+ /**
+ * Deletes old codes from sqlite database
+ */
+ protected function purgeOldCodesFromDatabase()
+ {
+ if ($this->use_database && $this->pdo_conn) {
+ $now = time();
+ $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
+
+ $query = sprintf("DELETE FROM %s WHERE %s - created > %s",
+ $this->database_table,
+ $this->pdo_conn->quote($now, PDO::PARAM_INT),
+ $this->pdo_conn->quote($limit, PDO::PARAM_INT));
+
+ $result = $this->pdo_conn->query($query);
+ }
+ }
+
+ /**
+ * Checks to see if the captcha code has expired and cannot be used
+ * @param unknown_type $creation_time
+ */
+ protected function isCodeExpired($creation_time)
+ {
+ $expired = true;
+
+ if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
+ $expired = false;
+ } else if (time() - $creation_time < $this->expiry_time) {
+ $expired = false;
+ }
+
+ return $expired;
+ }
+
+ /**
+ * Generate a wav file given the $letters in the code
+ * @todo Add ability to merge 2 sound files together to have random background sounds
+ * @param array $letters
+ * @return string The binary contents of the wav file
+ */
+ protected function generateWAV($letters)
+ {
+ $wavCaptcha = new WavFile();
+ $first = true; // reading first wav file
+
+ foreach ($letters as $letter) {
+ $letter = strtoupper($letter);
+
+ try {
+ $l = new WavFile($this->audio_path . '/' . $letter . '.wav');
+
+ if ($first) {
+ // set sample rate, bits/sample, and # of channels for file based on first letter
+ $wavCaptcha->setSampleRate($l->getSampleRate())
+ ->setBitsPerSample($l->getBitsPerSample())
+ ->setNumChannels($l->getNumChannels());
+ $first = false;
+ }
+
+ // append letter to the captcha audio
+ $wavCaptcha->appendWav($l);
+
+ // random length of silence between $audio_gap_min and $audio_gap_max
+ if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
+ $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
+ }
} catch (Exception $ex) {
- // failed to open file, or the wav file is broken or not supported
- // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
+ // failed to open file, or the wav file is broken or not supported
+ // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
throw $ex;
- }
- }
-
- /********* Set up audio filters *****************************/
- $filters = array();
-
- if ($this->audio_use_noise == true) {
- // use background audio - find random file
- $noiseFile = $this->getRandomNoiseFile();
-
- if ($noiseFile !== false && is_readable($noiseFile)) {
- try {
- $wavNoise = new WavFile($noiseFile, false);
- } catch(Exception $ex) {
- throw $ex;
- }
-
- // start at a random offset from the beginning of the wavfile
- // in order to add more randomness
- $randOffset = 0;
- if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
- $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
- $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
- } else {
- $wavNoise->readWavData();
- $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
- }
-
-
- $mixOpts = array('wav' => $wavNoise,
- 'loop' => true,
- 'blockOffset' => $randOffset);
-
- $filters[WavFile::FILTER_MIX] = $mixOpts;
- $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization;
- }
- }
-
- if ($this->degrade_audio == true) {
- // add random noise.
- // any noise level below 95% is intensely distorted and not pleasant to the ear
- $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0;
- }
-
- if (!empty($filters)) {
- $wavCaptcha->filter($filters); // apply filters to captcha audio
- }
-
- return $wavCaptcha->__toString();
- }
-
- public function getRandomNoiseFile()
- {
- $return = false;
-
- if ( ($dh = opendir($this->audio_noise_path)) !== false ) {
- $list = array();
-
- while ( ($file = readdir($dh)) !== false ) {
- if ($file == '.' || $file == '..') continue;
- if (strtolower(substr($file, -4)) != '.wav') continue;
-
- $list[] = $file;
- }
-
- closedir($dh);
-
- if (sizeof($list) > 0) {
- $file = $list[array_rand($list, 1)];
- $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
- }
- }
-
- return $return;
- }
-
- /**
- * Return a wav file saying there was an error generating file
- *
- * @return string The binary audio contents
- */
- protected function audioError()
- {
- return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav');
- }
-
- /**
- * Checks to see if headers can be sent and if any error has been output to the browser
- *
- * @return bool true if headers haven't been sent and no output/errors will break audio/images, false if unsafe
- */
+ }
+ }
+
+ /********* Set up audio filters *****************************/
+ $filters = array();
+
+ if ($this->audio_use_noise == true) {
+ // use background audio - find random file
+ $noiseFile = $this->getRandomNoiseFile();
+
+ if ($noiseFile !== false && is_readable($noiseFile)) {
+ try {
+ $wavNoise = new WavFile($noiseFile, false);
+ } catch(Exception $ex) {
+ throw $ex;
+ }
+
+ // start at a random offset from the beginning of the wavfile
+ // in order to add more randomness
+ $randOffset = 0;
+ if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
+ $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
+ $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
+ } else {
+ $wavNoise->readWavData();
+ $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
+ }
+
+
+ $mixOpts = array('wav' => $wavNoise,
+ 'loop' => true,
+ 'blockOffset' => $randOffset);
+
+ $filters[WavFile::FILTER_MIX] = $mixOpts;
+ $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization;
+ }
+ }
+
+ if ($this->degrade_audio == true) {
+ // add random noise.
+ // any noise level below 95% is intensely distorted and not pleasant to the ear
+ $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0;
+ }
+
+ if (!empty($filters)) {
+ $wavCaptcha->filter($filters); // apply filters to captcha audio
+ }
+
+ return $wavCaptcha->__toString();
+ }
+
+ public function getRandomNoiseFile()
+ {
+ $return = false;
+
+ if ( ($dh = opendir($this->audio_noise_path)) !== false ) {
+ $list = array();
+
+ while ( ($file = readdir($dh)) !== false ) {
+ if ($file == '.' || $file == '..') continue;
+ if (strtolower(substr($file, -4)) != '.wav') continue;
+
+ $list[] = $file;
+ }
+
+ closedir($dh);
+
+ if (sizeof($list) > 0) {
+ $file = $list[array_rand($list, 1)];
+ $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Return a wav file saying there was an error generating file
+ *
+ * @return string The binary audio contents
+ */
+ protected function audioError()
+ {
+ return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav');
+ }
+
+ /**
+ * Checks to see if headers can be sent and if any error has been output to the browser
+ *
+ * @return bool true if headers haven't been sent and no output/errors will break audio/images, false if unsafe
+ */
protected function canSendHeaders()
{
if (headers_sent()) {
@@ -2047,163 +2047,163 @@ class Securimage
return true;
}
-
- /**
- * Return a random float between 0 and 0.9999
- *
- * @return float Random float between 0 and 0.9999
- */
- function frand()
- {
- return 0.0001 * mt_rand(0,9999);
- }
-
- /**
- * Convert an html color code to a Securimage_Color
- * @param string $color
- * @param Securimage_Color $default The defalt color to use if $color is invalid
- */
- protected function initColor($color, $default)
- {
- if ($color == null) {
- return new Securimage_Color($default);
- } else if (is_string($color)) {
- try {
- return new Securimage_Color($color);
- } catch(Exception $e) {
- return new Securimage_Color($default);
- }
- } else if (is_array($color) && sizeof($color) == 3) {
- return new Securimage_Color($color[0], $color[1], $color[2]);
- } else {
- return new Securimage_Color($default);
- }
- }
-
- /**
- * Error handler used when outputting captcha image or audio.
- * This error handler helps determine if any errors raised would
- * prevent captcha image or audio from displaying. If they have
- * no effect on the output buffer or headers, true is returned so
- * the script can continue processing.
- * See https://github.com/dapphp/securimage/issues/15
- *
- * @param int $errno
- * @param string $errstr
- * @param string $errfile
- * @param int $errline
- * @param array $errcontext
- * @return boolean true if handled, false if PHP should handle
- */
- public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
- {
- // get the current error reporting level
- $level = error_reporting();
-
- // if error was supressed or $errno not set in current error level
- if ($level == 0 || ($level & $errno) == 0) {
- return true;
- }
-
- return false;
- }
-}
-
-
-/**
- * Color object for Securimage CAPTCHA
- *
- * @version 3.0
- * @since 2.0
- * @package Securimage
- * @subpackage classes
- *
- */
-class Securimage_Color
-{
- public $r;
- public $g;
- public $b;
-
- /**
- * Create a new Securimage_Color object.
- * Constructor expects 1 or 3 arguments.
- * When passing a single argument, specify the color using HTML hex format,
- * when passing 3 arguments, specify each RGB component (from 0-255) individually.
- * $color = new Securimage_Color('#0080FF') or
- * $color = new Securimage_Color(0, 128, 255)
- *
- * @param string $color
- * @throws Exception
- */
- public function __construct($color = '#ffffff')
- {
- $args = func_get_args();
-
- if (sizeof($args) == 0) {
- $this->r = 255;
- $this->g = 255;
- $this->b = 255;
- } else if (sizeof($args) == 1) {
- // set based on html code
- if (substr($color, 0, 1) == '#') {
- $color = substr($color, 1);
- }
-
- if (strlen($color) != 3 && strlen($color) != 6) {
- throw new InvalidArgumentException(
- 'Invalid HTML color code passed to Securimage_Color'
- );
- }
-
- $this->constructHTML($color);
- } else if (sizeof($args) == 3) {
- $this->constructRGB($args[0], $args[1], $args[2]);
- } else {
- throw new InvalidArgumentException(
- 'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
- );
- }
- }
-
- /**
- * Construct from an rgb triplet
- * @param int $red The red component, 0-255
- * @param int $green The green component, 0-255
- * @param int $blue The blue component, 0-255
- */
- protected function constructRGB($red, $green, $blue)
- {
- if ($red < 0) $red = 0;
- if ($red > 255) $red = 255;
- if ($green < 0) $green = 0;
- if ($green > 255) $green = 255;
- if ($blue < 0) $blue = 0;
- if ($blue > 255) $blue = 255;
-
- $this->r = $red;
- $this->g = $green;
- $this->b = $blue;
- }
-
- /**
- * Construct from an html hex color code
- * @param string $color
- */
- protected function constructHTML($color)
- {
- if (strlen($color) == 3) {
- $red = str_repeat(substr($color, 0, 1), 2);
- $green = str_repeat(substr($color, 1, 1), 2);
- $blue = str_repeat(substr($color, 2, 1), 2);
- } else {
- $red = substr($color, 0, 2);
- $green = substr($color, 2, 2);
- $blue = substr($color, 4, 2);
- }
-
- $this->r = hexdec($red);
- $this->g = hexdec($green);
- $this->b = hexdec($blue);
- }
-}
+
+ /**
+ * Return a random float between 0 and 0.9999
+ *
+ * @return float Random float between 0 and 0.9999
+ */
+ function frand()
+ {
+ return 0.0001 * mt_rand(0,9999);
+ }
+
+ /**
+ * Convert an html color code to a Securimage_Color
+ * @param string $color
+ * @param Securimage_Color $default The defalt color to use if $color is invalid
+ */
+ protected function initColor($color, $default)
+ {
+ if ($color == null) {
+ return new Securimage_Color($default);
+ } else if (is_string($color)) {
+ try {
+ return new Securimage_Color($color);
+ } catch(Exception $e) {
+ return new Securimage_Color($default);
+ }
+ } else if (is_array($color) && sizeof($color) == 3) {
+ return new Securimage_Color($color[0], $color[1], $color[2]);
+ } else {
+ return new Securimage_Color($default);
+ }
+ }
+
+ /**
+ * Error handler used when outputting captcha image or audio.
+ * This error handler helps determine if any errors raised would
+ * prevent captcha image or audio from displaying. If they have
+ * no effect on the output buffer or headers, true is returned so
+ * the script can continue processing.
+ * See https://github.com/dapphp/securimage/issues/15
+ *
+ * @param int $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ * @param array $errcontext
+ * @return boolean true if handled, false if PHP should handle
+ */
+ public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
+ {
+ // get the current error reporting level
+ $level = error_reporting();
+
+ // if error was supressed or $errno not set in current error level
+ if ($level == 0 || ($level & $errno) == 0) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
+
+/**
+ * Color object for Securimage CAPTCHA
+ *
+ * @version 3.0
+ * @since 2.0
+ * @package Securimage
+ * @subpackage classes
+ *
+ */
+class Securimage_Color
+{
+ public $r;
+ public $g;
+ public $b;
+
+ /**
+ * Create a new Securimage_Color object.
+ * Constructor expects 1 or 3 arguments.
+ * When passing a single argument, specify the color using HTML hex format,
+ * when passing 3 arguments, specify each RGB component (from 0-255) individually.
+ * $color = new Securimage_Color('#0080FF') or
+ * $color = new Securimage_Color(0, 128, 255)
+ *
+ * @param string $color
+ * @throws Exception
+ */
+ public function __construct($color = '#ffffff')
+ {
+ $args = func_get_args();
+
+ if (sizeof($args) == 0) {
+ $this->r = 255;
+ $this->g = 255;
+ $this->b = 255;
+ } else if (sizeof($args) == 1) {
+ // set based on html code
+ if (substr($color, 0, 1) == '#') {
+ $color = substr($color, 1);
+ }
+
+ if (strlen($color) != 3 && strlen($color) != 6) {
+ throw new InvalidArgumentException(
+ 'Invalid HTML color code passed to Securimage_Color'
+ );
+ }
+
+ $this->constructHTML($color);
+ } else if (sizeof($args) == 3) {
+ $this->constructRGB($args[0], $args[1], $args[2]);
+ } else {
+ throw new InvalidArgumentException(
+ 'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
+ );
+ }
+ }
+
+ /**
+ * Construct from an rgb triplet
+ * @param int $red The red component, 0-255
+ * @param int $green The green component, 0-255
+ * @param int $blue The blue component, 0-255
+ */
+ protected function constructRGB($red, $green, $blue)
+ {
+ if ($red < 0) $red = 0;
+ if ($red > 255) $red = 255;
+ if ($green < 0) $green = 0;
+ if ($green > 255) $green = 255;
+ if ($blue < 0) $blue = 0;
+ if ($blue > 255) $blue = 255;
+
+ $this->r = $red;
+ $this->g = $green;
+ $this->b = $blue;
+ }
+
+ /**
+ * Construct from an html hex color code
+ * @param string $color
+ */
+ protected function constructHTML($color)
+ {
+ if (strlen($color) == 3) {
+ $red = str_repeat(substr($color, 0, 1), 2);
+ $green = str_repeat(substr($color, 1, 1), 2);
+ $blue = str_repeat(substr($color, 2, 1), 2);
+ } else {
+ $red = substr($color, 0, 2);
+ $green = substr($color, 2, 2);
+ $blue = substr($color, 4, 2);
+ }
+
+ $this->r = hexdec($red);
+ $this->g = hexdec($green);
+ $this->b = hexdec($blue);
+ }
+}
diff --git a/skin/images/logo2.jpg b/skin/images/logo2.jpg
new file mode 100644
index 000000000..db432e716
Binary files /dev/null and b/skin/images/logo2.jpg differ
diff --git a/vendor/FHC-vendor/angular-tablesort/.gitignore b/vendor/FHC-vendor/angular-tablesort/.gitignore
deleted file mode 100644
index 2db6e8241..000000000
--- a/vendor/FHC-vendor/angular-tablesort/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.idea
-bower_components
\ No newline at end of file
diff --git a/vendor/FHC-vendor/angular-tablesort/LICENSE b/vendor/FHC-vendor/angular-tablesort/LICENSE
deleted file mode 100644
index 7627d9f60..000000000
--- a/vendor/FHC-vendor/angular-tablesort/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2013 Mattias Holmlund, http://www.holmlund.se/mattias
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/vendor/FHC-vendor/angular-tablesort/README.md b/vendor/FHC-vendor/angular-tablesort/README.md
deleted file mode 100644
index b556f977d..000000000
--- a/vendor/FHC-vendor/angular-tablesort/README.md
+++ /dev/null
@@ -1,119 +0,0 @@
-AngularJS Tablesort
-===================
-
-Allow tables to be sorted by clicking their headings.
-
-Web site: [http://mattiash.github.io/angular-tablesort](http://mattiash.github.io/angular-tablesort)
-
-Background
-----------
-
-When you use jquery to build your web-pages, it is very easy to add sorting-functionality to your tables - include [tablesorter](http://tablesorter.com) and annotate your column headings slightly to tell it what type of data your table contains.
-
-The goal with this module is to make it just as easy to add sorting to AngularJS tables, but with proper use of angular features and not jquery.
-
-Click once on a heading to sort ascending, twice for descending. Use shift-click to sort on more than one column.
-
-Additionally, these directives also makes it easy to add a default row that is shown in empty tables to make
-it explicit that the table is intentionally empty and not just broken.
-
-Installation
-------------
-
- bower install angular-tablesort
-
-or
-
- npm install angular-tablesort
-
-
-Usage
------
-
-Include the script in your markup
-
-```html
-
-```
-
-Include the module in your app
-
-```js
-angular.module('myApp', ['tableSort']);
-```
-
-The following code generates a table that can be sorted by clicking on the table headings:
-
-```html
-
-
-
-
Id
-
Name
-
Price
-
Quantity
-
-
-
-
-
{{item.Name}}
-
{{item.Price | currency}}
-
{{item.Quantity}}
-
-
-
-```
-
-The `ts-wrapper` attribute must be set on element that surrounds both the headings and the ng-repeat statement.
-
-The `ts-criteria` attribute tells tablesort which expression it should sort on when that element is clicked. Normally, the ts-criteria is the same as the expression that is shown in the column, but it doesn't have to be. The ts-criteria can also be filtered using the normal AngularJS filter syntax. Tablesort includes two filters parseInt and parseFloat that use the javascript functions of the same name, but any filter can be used.
-
-The `ts-default` attribute can be set on one or more columns to sort on them in ascending order by default.
-To sort in descending order, set ts-default to "descending"
-
-The `ts-repeat` attribute must be set on the element with ng-repeat.
-
-```html
-
-```
-
-Alternatively, `ts-repeat-start` and `ts-repeat-end` may be used to compliment the `ng-repeat-start` and `ng-repeat-end` directives.
-
-```html
-
-
-
{{ item.Name }}
-
-
-
{{ item.Description }}
-
-```
-
-By default, the sorting will be done as the last operation in the ng-repeat expression. To override this behavior, use an explicit `tablesort` directive as part of your ng-repeat expression. E.g.
-
-```html
-
-```
-
-This will first select the first 10 items in `items` and then sort them. Alternatively, you can insert an explicit tablesort in the pipe:
-
-```html
-
-```
-
-This will first sort the rows according to your specification and then only show the first 10 rows.
-
-If the `ng-repeat` expression contains a `track by` statement (which is generally a good idea), that expression will
-be used to provide a [stable](http://en.wikipedia.org/wiki/Sorting_algorithm#Stability) sort result.
-
-CSS
----
-
-All table headings that can be sorted on is styled with css-class `tablesort-sortable`. The table headings that the table is currently sorted on is styled with `tablesort-asc` or `tablesort-desc` classes depending on the sort-direction. A stylesheet is included to show that it works, but you probably want to build your own.
-
-By default the content and look of the data for empty tables is controlled via css. It is inserted as one empty `
` spanning
-all columns and placed inside a `
` with class `showIfLast` The `
` is placed at the top of each table.
-To disable this feature add the attribute `ts-hide-no-data` to the `ts-repeat` row:
-```html
-
');
- noDataRow = $compile(noDataRow)(scope);
- element.parent().prepend(noDataRow);
- }
-
- angular.element(element[0]).attr(ngRepeatDirective, repeatExpr);
- $compile(element, null, 1000000)(scope);
- }
- };
-}]);
-
-tableSortModule.filter( 'tablesortOrderBy', function(){
- return function(array, sortfun ) {
- if(!array) return;
- var arrayCopy = [];
- for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
- return arrayCopy.sort( sortfun );
- };
-} );
-
-tableSortModule.filter( 'parseInt', function(){
- return function(input) {
- return parseInt( input ) || null;
- };
-} );
-
-tableSortModule.filter( 'parseFloat', function(){
- return function(input) {
- return parseFloat( input ) || null;
- };
-} );
diff --git a/vendor/FHC-vendor/angular-tablesort/package.json b/vendor/FHC-vendor/angular-tablesort/package.json
deleted file mode 100644
index 20a4ad16d..000000000
--- a/vendor/FHC-vendor/angular-tablesort/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "angular-tablesort",
- "version": "1.1.2",
- "description": "Sort angularjs tables easily",
- "main": "index.js",
- "repository": {
- "type": "git",
- "url": "https://github.com/mattiash/angular-tablesort.git"
- },
- "license": "MIT",
- "homepage": "https://github.com/mattiash/angular-tablesort"
-}
diff --git a/vendor/FHC-vendor/angular-tablesort/tablesort.css b/vendor/FHC-vendor/angular-tablesort/tablesort.css
deleted file mode 100644
index d6c11656c..000000000
--- a/vendor/FHC-vendor/angular-tablesort/tablesort.css
+++ /dev/null
@@ -1,56 +0,0 @@
-th.tablesort-sortable {
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -o-user-select: none;
- user-select: none;
- cursor: pointer;
-}
-
-table .tablesort-sortable:after{
- content:"";
- float:right;
- margin-top:7px;
- visibility:hidden;
- border-left:4px solid transparent;
- border-right:4px solid transparent;
-
- border-top:none;
- border-bottom:4px solid #000;
-}
-
-table .tablesort-desc:after{
- border-top:4px solid #000;
- border-bottom:none;
-}
-
-table .tablesort-asc,table .tablesort-desc{
- background-color:rgba(141, 192, 219, 0.25);
-}
-
-table .tablesort-sortable:hover:after, table .tablesort-asc:after, table .tablesort-desc:after {
- visibility:visible;
-}
-
-/*
- * Styling for the table row shown in empty tables
- */
-
-/* The row is always added as the first row in a table
- Hide it by default */
-.showIfLast {
- display: none;
-}
-
-/* Only show it if it is also the last row of the table. */
-.showIfLast:last-child {
- display: table-row;
-}
-
-.showIfLast td {
- text-align: center;
-}
-
-.showIfLast td:after {
- content: "No data";
-}
diff --git a/vendor/FHC-vendor/easyui/changelog.txt b/vendor/FHC-vendor/easyui/changelog.txt
deleted file mode 100644
index cb2ecd37d..000000000
--- a/vendor/FHC-vendor/easyui/changelog.txt
+++ /dev/null
@@ -1,491 +0,0 @@
-Version 1.4.4
--------------
-* Bug
- * filebox: The 'clear' and 'reset' methods do not work properly in IE9. fixed.
- * messager: After calling $.messager.progress() with no arguments, the $.messager.progress('close') does not work properly. fixed.
- * timespinner: The value does not display properly in IE8 while clicking the spin buttons. fixed.
- * window: The window does not display when calling 'options' method in 'onMove' event. fixed.
- * treegrid: The 'getLevel' method does not accept the parameter value of 0. fixed.
-* Improvement
- * layout: The 'collapsedContent','expandMode' and 'hideExpandTool' properties are supported in region panel.
- * layout: The 'hideCollapsedContent' property can be set to display the vertical title bar on collapsed panel.
- * layout: Add 'onCollapse','onExpand','onAdd','onRemove' events.
- * datagrid: Display the 'up-down' icon on the sortable columns.
- * datagrid: Add 'gotoPage' method.
- * propertygrid: Add 'groups' method that allows to get all the data groups.
- * messager: Auto scroll feature is supported when displaying long messages.
- * tabs: The 'disabled' property is supported when defining a disabled tab panel.
- * tabs: The percentange size is supported now.
-
-Version 1.4.3
--------------
-* Bug
- * textbox: The 'setText' method does not accept value 0. fixed.
- * timespinner: When running in IE11, the error occurs when clicking on the empty textbox. fixed.
- * tabs: The 'update' method can not update only the panel body. fixed.
-* Improvement
- * combobox: Improve the performance of displaying the drop-down panel.
- * combogrid: Remember the displaying text when the drop-down datagrid go to other pages.
- * combogrid: The 'setValue' and 'setValues' methods accept a key-value object.
- * window: The inline window's mask can auto-stretch its size to fill parent container.
- * tabs: The 'showTool' and 'hideTool' methods are available for users to show or hide the tools.
- * layout: Allow the user to override the 'cls','headerCls' and 'bodyCls' property values.
-* New Plugins
- * switchbutton: The switch button with two states:'on' and 'off'.
-
-Version 1.4.2
--------------
-* Bug
- * treegrid: The column will restore its size to original size after recreating the treegrid. fixed.
-* Improvement
- * draggable: Add 'delay' property that allows the user to delay the drag operation.
- * tree: Add 'filter' property and 'doFilter' method.
- * tabs: The 'add' method allows the user to insert a tab panel at a specified index.
- * tabs: The user can determine what tab panel can be selected.
- * tabs: Add 'justified' and 'narrow' properties.
- * layout: Add 'unsplit' and 'split' methods.
- * messager: Keyboard navigation features are supported now.
- * form: Add 'onChange' event.
- * combobox: Add 'queryParams' property.
- * slider: Add 'range' property.
- * menu: Add 'itemHeight','inline','noline' properties.
- * panel: The 'header' property allows the user to customize the panel header.
- * menubutton: Add 'hasDownArrow' property.
-* New Plugins
- * datalist: The plugin to render items in a list.
- * navpanel: The root component for the mobile page.
- * mobile: The plugin to provide the mobile page stack management and navigation.
-
-Version 1.4.1
--------------
-* Bug
- * combogrid: The combogrid has different height than other combo components. fixed.
- * datagrid: The row element loses some class style value after calling 'updateRow' method. fixed.
- * menubutton: Calling 'enable' method on a disabled button can not work well. fixed.
- * form: The filebox components in the form do not work correctly after calling 'clear' method. fixed.
-* Improvement
- * tabs: The 'update' method accepts 'type' option that allows the user to update the header,body,or both.
- * panel: Add 'openAnimation','openDuration','closeAnimation' and 'closeDuration' properties to set the animation for opening or closing a panel.
- * panel: Add 'footer' property that allows the user to add a footer bar to the bottom of panel.
- * datagrid: Calling 'endEdit' method will accept the editing value correctly.
- * datagrid: Add 'onBeforeSelect','onBeforeCheck','onBeforeUnselect','onBeforeUncheck' events.
- * propertygrid: The user can edit a row by calling 'beginEdit' method.
- * datebox: Add 'cloneFrom' method to create the datebox component quickly.
- * datetimebox: Add 'cloneFrom' method to create the datetimebox component quickly.
-
-Version 1.4
--------------
-* Bug
- * menu: The menu should not has a correct height when removed a menu item. fixed.
- * datagrid: The 'fitColumns' method does not work normally when the datarid width is too small. fixed.
-* Improvement
- * The fluid/percentange size is supported now for all easyui components.
- * menu: Add 'showItem', 'hideItem' and 'resize' methods.
- * menu: Auto resize the height upon the window size.
- * menu: Add 'duration' property that allows the user to define duration time in milliseconds to hide menu.
- * validatebox: Add 'onBeforeValidate' and 'onValidate' events.
- * combo: Extended from textbox now.
- * combo: Add 'panelMinWidth','panelMaxWidth','panelMinHeight' and 'panelMaxHeight' properties.
- * searchbox: Extended from textbox now.
- * tree: The 'getRoot' method will return the top parent node of a specified node if pass a 'nodeEl' parameter.
- * tree: Add 'queryParams' property.
- * datetimebox: Add 'spinnerWidth' property.
- * panel: Add 'doLayout' method to cause the panel to lay out its components.
- * panel: Add 'clear' method to clear the panel's content.
- * datagrid: The user is allowed to assign percent width to columns.
- * form: Add 'ajax','novalidate' and 'queryParams' properties.
- * linkbutton: Add 'resize' method.
-* New Plugins
- * textbox: A enhanced input field that allows users build their form easily.
- * datetimespinner: A date and time spinner that allows to pick a specific day.
- * filebox: The filebox component represents a file field of the forms.
-
-Version 1.3.6
--------------
-* Bug
- * treegrid: The 'getChecked' method can not return correct checked rows. fixed.
- * tree: The checkbox does not display properly on async tree when 'onlyLeafCheck' property is true. fixed.
-* Improvement
- * treegrid: All the selecting and checking methods are extended from datagrid component.
- * linkbutton: The icon alignment is fully supported, possible values are: 'top','bottom','left','right'.
- * linkbutton: Add 'size' property, possible values are: 'small','large'.
- * linkbutton: Add 'onClick' event.
- * menubutton: Add 'menuAlign' property that allows the user set top level menu alignment.
- * combo: Add 'panelAlign' property, possible values are: 'left','right'.
- * calendar: The 'formatter','styler' and 'validator' options are available to custom the calendar dates.
- * calendar: Add 'onChange' event.
- * panel: Add 'method','queryParams' and 'loader' options.
- * panel: Add 'onLoadError' event.
- * datagrid: Add 'onBeginEdit' event that fires when a row goes into edit mode.
- * datagrid: Add 'onEndEdit' event that fires when finishing editing but before destroying editors.
- * datagrid: Add 'sort' method and 'onBeforeSortColumn' event.
- * datagrid: The 'combogrid' editor has been integrated into datagrid.
- * datagrid: Add 'ctrlSelect' property that only allows multi-selection when ctrl+click is used.
- * slider: Add 'converter' option that allows users determine how to convert a value to the slider position or the slider position to the value.
- * searchbox: Add 'disabled' property.
- * searchbox: Add 'disable','enable','clear','reset' methods.
- * spinner: Add 'readonly' property, 'readonly' method and 'onChange' event.
-
-Version 1.3.5
--------------
-* Bug
- * searchbox: The 'searcher' function can not offer 'name' parameter value correctly. fixed.
- * combo: The 'isValid' method can not return boolean value. fixed.
- * combo: Clicking combo will trigger the 'onHidePanel' event of other combo components that have hidden drop-down panels. fixed.
- * combogrid: Some methods can not inherit from combo. fixed.
-* Improvement
- * datagrid: Improve performance on checking rows.
- * menu: Allows to append a menu separator.
- * menu: Add 'hideOnUnhover' property to indicate if the menu should be hidden when mouse exits it.
- * slider: Add 'clear' and 'reset' methods.
- * tabs: Add 'unselect' method that will trigger 'onUnselect' event.
- * tabs: Add 'selected' property to specify what tab panel will be opened.
- * tabs: The 'collapsible' property of tab panel is supported to determine if the tab panel can be collapsed.
- * tabs: Add 'showHeader' property, 'showHeader' and 'hideHeader' methods.
- * combobox: The 'disabled' property can be used to disable some items.
- * tree: Improve loading performance.
- * pagination: The 'layout' property can be used to customize the pagination layout.
- * accordion: Add 'unselect' method that will trigger 'onUnselect' event.
- * accordion: Add 'selected' and 'multiple' properties.
- * accordion: Add 'getSelections' method.
- * datebox: Add 'sharedCalendar' property that allows multiple datebox components share one calendar component.
-
-Version 1.3.4
--------------
-* Bug
- * combobox: The onLoadSuccess event fires when parsing empty local data. fixed.
- * form: Calling 'reset' method can not reset datebox field. fixed.
-* Improvement
- * mobile: The context menu and double click features are supported on mobile devices.
- * combobox: The 'groupField' and 'groupFormatter' options are available to display items in groups.
- * tree: When append or insert nodes, the 'data' parameter accepts one or more nodes data.
- * tree: The 'getChecked' method accepts a single 'state' or an array of 'state'.
- * tree: Add 'scrollTo' method.
- * datagrid: The 'multiSort' property is added to support multiple column sorting.
- * datagrid: The 'rowStyler' and column 'styler' can return CSS class name or inline styles.
- * treegrid: Add 'load' method to load data and navigate to the first page.
- * tabs: Add 'tabWidth' and 'tabHeight' properties.
- * validatebox: The 'novalidate' property is available to indicate whether to perform the validation.
- * validatebox: Add 'enableValidation' and 'disableValidation' methods.
- * form: Add 'enableValidation' and 'disableValidation' methods.
- * slider: Add 'onComplete' event.
- * pagination: The 'buttons' property accepts the existing element.
-
-Version 1.3.3
--------------
-* Bug
- * datagrid: Some style features are not supported by column styler function. fixed.
- * datagrid: IE 31 stylesheet limit. fixed.
- * treegrid: Some style features are not supported by column styler function. fixed.
- * menu: The auto width of menu item displays incorrect in ie6. fixed.
- * combo: The 'onHidePanel' event can not fire when clicked outside the combo area. fixed.
-* Improvement
- * datagrid: Add 'scrollTo' and 'highlightRow' methods.
- * treegrid: Enable treegrid to parse data from
element.
- * combo: Add 'selectOnNavigation' and 'readonly' options.
- * combobox: Add 'loadFilter' option to allow users to change data format before loading into combobox.
- * tree: Add 'onBeforeDrop' callback event.
- * validatebox: Dependent on tooltip plugin now, add 'deltaX' property.
- * numberbox: The 'filter' options can be used to determine if the key pressed was accepted.
- * linkbutton: The group button is available.
- * layout: The 'minWidth','maxWidth','minHeight','maxHeight' and 'collapsible' properties are available for region panel.
-* New Plugins
- * tooltip: Display a popup message when moving mouse over an element.
-
-Version 1.3.2
--------------
-* Bug
- * datagrid: The loading message window can not be centered when changing the width of datagrid. fixed.
- * treegrid: The 'mergeCells' method can not work normally. fixed.
- * propertygrid: Calling 'endEdit' method to stop editing a row will cause errors. fixed.
- * tree: Can not load empty data when 'lines' property set to true. fixed.
-* Improvement
- * RTL feature is supported now.
- * tabs: Add 'scrollBy' method to scroll the tab header by the specified amount of pixels
- * tabs: Add 'toolPosition' property to set tab tools to left or right.
- * tabs: Add 'tabPosition' property to define the tab position, possible values are: 'top','bottom','left','right'.
- * datagrid: Add a column level property 'order' that allows users to define different default sort order per column.
- * datagrid: Add a column level property 'halign' that allows users to define how to align the column header.
- * datagrid: Add 'resizeHandle' property to define the resizing column position, by grabbing the left or right edge of the column.
- * datagrid: Add 'freezeRow' method to freeze some rows that will always be displayed at the top when the datagrid is scrolled down.
- * datagrid: Add 'clearChecked' method to clear all checked records.
- * datagrid: Add 'data' property to initialize the datagrid data.
- * linkbutton: Add 'iconAlgin' property to define the icon position, supported values are: 'left','right'.
- * menu: Add 'minWidth' property.
- * menu: The menu width can be automatically calculated.
- * tree: New events are available including 'onBeforeDrag','onStartDrag','onDragEnter','onDragOver','onDragLeave',etc.
- * combo: Add 'height' property to allow users to define the height of combo.
- * combo: Add 'reset' method.
- * numberbox: Add 'reset' method.
- * spinner: Add 'reset' method.
- * spinner: Add 'height' property to allow users to define the height of spinner.
- * searchbox: Add 'height' property to allow users to define the height of searchbox.
- * form: Add 'reset' method.
- * validatebox: Add 'delay' property to delay validating from the last inputting value.
- * validatebox: Add 'tipPosition' property to define the tip position, supported values are: 'left','right'.
- * validatebox: Multiple validate rules on a field is supported now.
- * slider: Add 'reversed' property to determine if the min value and max value will switch their positions.
- * progressbar: Add 'height' property to allow users to define the height of progressbar.
-
-Version 1.3.1
--------------
-* Bug
- * datagrid: Setting the 'pageNumber' property is not valid. fixed.
- * datagrid: The id attribute of rows isn't adjusted properly while calling 'insertRow' or 'deleteRow' method.
- * dialog: When load content from 'href', the script will run twice. fixed.
- * propertygrid: The editors that extended from combo can not accept its changed value. fixed.
-* Improvement
- * droppable: Add 'disabled' property.
- * droppable: Add 'options','enable' and 'disable' methods.
- * tabs: The tab panel tools can be changed by calling 'update' method.
- * messager: When show a message window, the user can define the window position by applying 'style' property.
- * window: Prevent script on window body from running twice.
- * window: Add 'hcenter','vcenter' and 'center' methods.
- * tree: Add 'onBeforeCheck' callback event.
- * tree: Extend the 'getChecked' method to allow users to get 'checked','unchecked' or 'indeterminate' nodes.
- * treegrid: Add 'update' method to update a specified node.
- * treegrid: Add 'insert' method to insert a new node.
- * treegrid: Add 'pop' method to remove a node and get the removed node data.
-
-Version 1.3
------------
-* Bug
- * combogrid: When set to 'remote' query mode, the 'queryParams' parameters can't be sent to server. fixed.
- * combotree: The tree nodes on drop-down panel can not be unchecked while calling 'clear' method. fixed.
- * datetimebox: Setting 'showSeconds' property to false cannot hide seconds info. fixed.
- * datagrid: Calling 'mergeCells' method can't auto resize the merged cell while header is hidden. fixed.
- * dialog: Set cache to false and load data via ajax, the content cannot be refreshed. fixed.
-* Improvement
- * The HTML5 'data-options' attribute is available for components to declare all custom options, including properties and events.
- * More detailed documentation is available.
- * panel: Prevent script on panel body from running twice.
- * accordion: Add 'getPanelIndex' method.
- * accordion: The tools can be added on panel header.
- * datetimebox: Add 'timeSeparator' option that allows users to define the time separator.
- * pagination: Add 'refresh' and 'select' methods.
- * datagrid: Auto resize the column width to fit the contents when the column width is not defined.
- * datagrid: Double click on the right border of columns to auto resize the columns to the contents in the columns.
- * datagrid: Add 'autoSizeColumn' method that allows users to adjust the column width to fit the contents.
- * datagrid: Add 'getChecked' method to get all rows where the checkbox has been checked.
- * datagrid: Add 'selectOnCheck' and 'checkOnSelect' properties and some checking methods to enhance the row selections.
- * datagrid: Add 'pagePosition' property to allow users to display pager bar at either top,bottom or both places of the grid.
- * datagrid: The buffer view and virtual scroll view are supported to display large amounts of records without pagination.
- * tabs: Add 'disableTab' and 'enableTab' methods to allow users to disable or enable a tab panel.
-
-Version 1.2.6
--------------
-* Bug
- * tabs: Call 'add' method with 'selected:false' option, the added tab panel is always selected. fixed.
- * treegrid: The 'onSelect' and 'onUnselect' events can't be triggered. fixed.
- * treegrid: Cannot display zero value field. fixed.
-* Improvement
- * propertygrid: Add 'expandGroup' and 'collapseGroup' methods.
- * layout: Allow users to create collapsed layout panels by assigning 'collapsed' property to true.
- * layout: Add 'add' and 'remove' methods that allow users to dynamically add or remove region panel.
- * layout: Additional tool icons can be added on region panel header.
- * calendar: Add 'firstDay' option that allow users to set first day of week. Sunday is 0, Monday is 1, ...
- * tree: Add 'lines' option, true to display tree lines.
- * tree: Add 'loadFilter' option that allow users to change data format before loading into the tree.
- * tree: Add 'loader' option that allow users to define how to load data from remote server.
- * treegrid: Add 'onClickCell' and 'onDblClickCell' callback function options.
- * datagrid: Add 'autoRowHeight' property that allow users to determine if set the row height based on the contents of that row.
- * datagrid: Improve performance to load large data set.
- * datagrid: Add 'loader' option that allow users to define how to load data from remote server.
- * treegrid: Add 'loader' option that allow users to define how to load data from remote server.
- * combobox: Add 'onBeforeLoad' callback event function.
- * combobox: Add 'loader' option that allow users to define how to load data from remote server.
- * Add support for other loading mode such as dwr,xml,etc.
-* New Plugins
- * slider: Allows the user to choose a numeric value from a finite range.
-
-Version 1.2.5
--------------
-* Bug
- * tabs: When add a new tab panel with href property, the content page is loaded twice. fixed.
- * form: Failed to call 'load' method to load form input with complex name. fixed.
- * draggable: End drag in ie9, the cursor cannot be restored. fixed.
-* Improvement
- * panel: The tools can be defined via html markup.
- * tabs: Call 'close' method to close specified tab panel, users can pass tab title or index of tab panel. Other methods such 'select','getTab' and 'exists' are similar to 'close' method.
- * tabs: Add 'getTabIndex' method.
- * tabs: Users can define mini tools on tabs.
- * tree: The mouse must move a specified distance to begin drag and drop operation.
- * resizable: Add 'options','enable' and 'disable' methods.
- * numberbox: Allow users to change number format.
- * datagrid: The subgrid is supported now.
- * searchbox: Add 'selectName' method to select searching type name.
-
-Version 1.2.4
--------------
-* Bug
- * menu: The menu position is wrong when scroll bar appears. fixed.
- * accordion: Cannot display the default selected panel in jQuery 1.6.2. fixed.
- * tabs: Cannot display the default selected tab panel in jQuery 1.6.2. fixed.
-* Improvement
- * menu: Allow users to disable or enable menu item.
- * combo: Add 'delay' property to set the delay time to do searching from the last key input event.
- * treegrid: The 'getEditors' and 'getEditor' methods are supported now.
- * treegrid: The 'loadFilter' option is supported now.
- * messager: Add 'progress' method to display a message box with a progress bar.
- * panel: Add 'extractor' option to allow users to extract panel content from ajax response.
-* New Plugins
- * searchbox: Allow users to type words into box and do searching operation.
- * progressbar: To display the progress of a task.
-
-Version 1.2.3
--------------
-* Bug
- * window: Cannot resize the window with iframe content. fixed.
- * tree: The node will be removed when dragging to its child. fixed.
- * combogrid: The onChange event fires multiple times. fixed.
- * accordion: Cannot add batch new panels when animate property is set to true. fixed.
-* Improvement
- * treegrid: The footer row and row styler features are supported now.
- * treegrid: Add 'getLevel','reloadFooter','getFooterRows' methods.
- * treegrid: Support root nodes pagination and editable features.
- * datagrid: Add 'getFooterRows','reloadFooter','insertRow' methods and improve editing performance.
- * datagrid: Add 'loadFilter' option that allow users to change original source data to standard data format.
- * draggable: Add 'onBeforeDrag' callback event function.
- * validatebox: Add 'remote' validation type.
- * combobox: Add 'method' option.
-* New Plugins
- * propertygrid: Allow users to edit property value in datagrid.
-
-Version 1.2.2
--------------
-* Bug
- * datagrid: Apply fitColumns cannot work fine while set checkbox column. fixed.
- * datagrid: The validateRow method cannot return boolean type value. fixed.
- * numberbox: Cannot fix value in chrome when min or max property isn't defined. fixed.
-* Improvement
- * menu: Add some crud methods.
- * combo: Add hasDownArrow property to determine whether to display the down arrow button.
- * tree: Supports inline editing.
- * calendar: Add some useful methods such as 'resize', 'moveTo' etc.
- * timespinner: Add some useful methods.
- * datebox: Refactoring based on combo and calendar plugin now.
- * datagrid: Allow users to change row style in some conditions.
- * datagrid: Users can use the footer row to display summary information.
-* New Plugins
- * datetimebox: Combines datebox with timespinner component.
-
-Version 1.2.1
--------------
-* Bug
- * easyloader: Some dependencies cannot be loaded by their order. fixed.
- * tree: The checkbox is setted incorrectly when removing a node. fixed.
- * dialog: The dialog layout incorrectly when 'closed' property is setted to true. fixed.
-* Improvement
- * parser: Add onComplete callback function that can indicate whether the parse action is complete.
- * menu: Add onClick callback function and some other methods.
- * tree: Add some useful methods.
- * tree: Drag and Drop feature is supported now.
- * tree: Add onContextMenu callback function.
- * tabs: Add onContextMenu callback function.
- * tabs: Add 'tools' property that can create buttons on right bar.
- * datagrid: Add onHeaderContextMenu and onRowContextMenu callback functions.
- * datagrid: Custom view is supported.
- * treegrid: Add onContextMenu callback function and append,remove methods.
-
-Version 1.2
--------------
-* Improvement
- * tree: Add cascadeCheck,onlyLeafCheck properties and select event.
- * combobox: Enable multiple selection.
- * combotree: Enable multiple selection.
- * tabs: Remember the trace of selection, when current tab panel is closed, the previous selected tab will be selected.
- * datagrid: Extend from panel, so many properties defined in panel can be used for datagrid.
-* New Plugins
- * treegrid: Represent tabular data in hierarchical view, combines tree view and datagrid.
- * combo: The basic component that allow user to extend their combo component such as combobox,combotree,etc.
- * combogrid: Combines combobox with drop-down datagrid component.
- * spinner: The basic plugin to create numberspinner,timespinner,etc.
- * numberspinner: The numberbox that allow user to change value by clicking up and down spin buttons.
- * timespinner: The time selector that allow user to quickly inc/dec a time.
-
-Version 1.1.2
--------------
-* Bug
- * messager: When call show method in layout, the message window will be blocked. fixed.
-* Improvement
- * datagrid: Add validateRow method, remember the current editing row status when do editing action.
- * datagrid: Add the ability to create merged cells.
- * form: Add callback functions when loading data.
- * panel,window,dialog: Add maximize,minimize,restore,collapse,expand methods.
- * panel,tabs,accordion: The lazy loading feature is supported.
- * tabs: Add getSelected,update,getTab methods.
- * accordion: Add crud methods.
- * linkbutton: Accept an id option to set the id attribute.
- * tree: Enhance tree node operation.
-
-Version 1.1.1
--------------
-* Bug
- * form: Cannot clear the value of combobox and combotree component. fixed.
-* Improvement
- * tree: Add some useful methods such as 'getRoot','getChildren','update',etc.
- * datagrid: Add editable feature, improve performance while loading data.
- * datebox: Add destroy method.
- * combobox: Add destroy and clear method.
- * combotree: Add destroy and clear method.
-
-Version 1.1
--------------
-* Bug
- * messager: When call show method with timeout property setted, an error occurs while clicking the close button. fixed.
- * combobox: The editable property of combobox plugin is invalid. fixed.
- * window: The proxy box will not be removed when dragging or resizing exceed browser border in ie. fixed.
-* Improvement
- * menu: The menu item can use markup to display a different page.
- * tree: The tree node can use markup to act as a tree menu.
- * pagination: Add some event on refresh button and page list.
- * datagrid: Add a 'param' parameter for reload method, with which users can pass query parameter when reload data.
- * numberbox: Add required validation support, the usage is same as validatebox plugin.
- * combobox: Add required validation support.
- * combotree: Add required validation support.
- * layout: Add some method that can get a region panel and attach event handlers.
-* New Plugins
- * droppable: A droppable plugin that supports drag drop operation.
- * calendar: A calendar plugin that can either be embedded within a page or popup.
- * datebox: Combines a textbox with a calendar that let users to select date.
- * easyloader: A JavaScript loader that allows you to load plugin and their dependencies into your page.
-
-Version 1.0.5
-* Bug
- * panel: The fit property of panel performs incorrectly. fixed.
-* Improvement
- * menu: Add a href attribute for menu item, with which user can display a different page in the current browser window.
- * form: Add a validate method to do validation for validatebox component.
- * dialog: The dialog can read collapsible,minimizable,maximizable and resizable attribute from markup.
-* New Plugins
- * validatebox: A validation plugin that checks to make sure the user's input value is valid.
-
-Version 1.0.4
--------------
-* Bug
- * panel: When panel is invisible, it is abnormal when resized. fixed.
- * panel: Memory leak in method 'destroy'. fixed.
- * messager: Memory leak when messager box is closed. fixed.
- * dialog: No onLoad event occurs when loading remote data. fixed.
-* Improvement
- * panel: Add method 'setTitle'.
- * window: Add method 'setTitle'.
- * dialog: Add method 'setTitle'.
- * combotree: Add method 'getValue'.
- * combobox: Add method 'getValue'.
- * form: The 'load' method can load data and fill combobox and combotree field correctly.
-
-Version 1.0.3
--------------
-* Bug
- * menu: When menu is show in a DIV container, it will be cropped. fixed.
- * layout: If you collpase a region panel and then expand it immediately, the region panel will not show normally. fixed.
- * accordion: If no panel selected then the first one will become selected and the first panel's body height will not set correctly. fixed.
-* Improvement
- * tree: Add some methods to support CRUD operation.
- * datagrid: Toolbar can accept a new property named 'disabled' to disable the specified tool button.
-* New Plugins
- * combobox: Combines a textbox with a list of options that users are able to choose from.
- * combotree: Combines combobox with drop-down tree component.
- * numberbox: Make input element can only enter number char.
- * dialog: rewrite the dialog plugin, dialog can contains toolbar and buttons.
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/accordion/_content.html b/vendor/FHC-vendor/easyui/demo-mobile/accordion/_content.html
deleted file mode 100644
index f7b8e2ee7..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/accordion/_content.html
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
- AJAX Content
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/animation/pop.html b/vendor/FHC-vendor/easyui/demo-mobile/animation/pop.html
deleted file mode 100644
index 6bcebccd1..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/animation/pop.html
+++ /dev/null
@@ -1 +0,0 @@
-
Pop Animation - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/badge/button.html b/vendor/FHC-vendor/easyui/demo-mobile/badge/button.html
deleted file mode 100644
index db5d5310f..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/badge/button.html
+++ /dev/null
@@ -1 +0,0 @@
-
Button Badge - jQuery EasyUI Mobile Demo
Button Badge
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/badge/list.html b/vendor/FHC-vendor/easyui/demo-mobile/badge/list.html
deleted file mode 100644
index c06f18b3c..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/badge/list.html
+++ /dev/null
@@ -1 +0,0 @@
-
List Badge - jQuery EasyUI Mobile Demo
List Badge
Large
234
Spotted Adult Female
215
Venomless
12
Rattleless
6
Green Adult
Tailless
With tail
Adult Female
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/badge/tabs.html b/vendor/FHC-vendor/easyui/demo-mobile/badge/tabs.html
deleted file mode 100644
index 51cd85977..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/badge/tabs.html
+++ /dev/null
@@ -1 +0,0 @@
-
Tabs Badge - jQuery EasyUI Mobile Demo
Tabs Badge
Modem
A modem (modulator-demodulator) is a device that modulates an analog carrier signal to encode digital information, and also demodulates such a carrier signal to decode the transmitted information.
Scanner
In computing, an image scanner—often abbreviated to just scanner—is a device that optically scans images, printed text, handwriting, or an object, and converts it to a digital image.
Pda
23
A personal digital assistant (PDA), also known as a palmtop computer, or personal data assistant, is a mobile device that functions as a personal information manager. PDAs are largely considered obsolete with the widespread adoption of smartphones.
Pda
13
A tablet computer, or simply tablet, is a one-piece mobile computer. Devices typically have a touchscreen, with finger or stylus gestures replacing the conventional computer mouse.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/button/basic.html b/vendor/FHC-vendor/easyui/demo-mobile/button/basic.html
deleted file mode 100644
index f5f525563..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/button/basic.html
+++ /dev/null
@@ -1 +0,0 @@
-
Basic LinkButton - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/button/group.html b/vendor/FHC-vendor/easyui/demo-mobile/button/group.html
deleted file mode 100644
index 8bd165da9..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/button/group.html
+++ /dev/null
@@ -1 +0,0 @@
-
Group LinkButton - jQuery EasyUI Mobile Demo
A modem (modulator-demodulator) is a device that modulates an analog carrier signal to encode digital information, and also demodulates such a carrier signal to decode the transmitted information.
In computing, an image scanner—often abbreviated to just scanner—is a device that optically scans images, printed text, handwriting, or an object, and converts it to a digital image.
A tablet computer, or simply tablet, is a one-piece mobile computer. Devices typically have a touchscreen, with finger or stylus gestures replacing the conventional computer mouse.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/button/style.html b/vendor/FHC-vendor/easyui/demo-mobile/button/style.html
deleted file mode 100644
index 66620940a..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/button/style.html
+++ /dev/null
@@ -1 +0,0 @@
-
Button Style - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/datalist/group.html b/vendor/FHC-vendor/easyui/demo-mobile/datalist/group.html
deleted file mode 100644
index d9e644352..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/datalist/group.html
+++ /dev/null
@@ -1 +0,0 @@
-
Group DataList - jQuery EasyUI Mobile Demo
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/basic.html b/vendor/FHC-vendor/easyui/demo-mobile/simplelist/basic.html
deleted file mode 100644
index 906d94261..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/basic.html
+++ /dev/null
@@ -1 +0,0 @@
-
Simple List - jQuery EasyUI Mobile Demo
Simple List
Large
Spotted Adult Female
Venomless
Rattleless
Green Adult
Tailless
With tail
Adult Female
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/button.html b/vendor/FHC-vendor/easyui/demo-mobile/simplelist/button.html
deleted file mode 100644
index f326ba970..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/button.html
+++ /dev/null
@@ -1 +0,0 @@
-
Button on List - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/group.html b/vendor/FHC-vendor/easyui/demo-mobile/simplelist/group.html
deleted file mode 100644
index e64c2e4aa..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/group.html
+++ /dev/null
@@ -1 +0,0 @@
-
Group List - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/image.html b/vendor/FHC-vendor/easyui/demo-mobile/simplelist/image.html
deleted file mode 100644
index ddf79e592..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/image.html
+++ /dev/null
@@ -1 +0,0 @@
-
List with Image - jQuery EasyUI Mobile Demo
List with Image
modem
modulates an analog carrier signal to encode digital information.
scanner
scans images, printed text, handwriting, or an object.
pda
A personal digital assistant.
tablet
one-piece mobile computer.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/link.html b/vendor/FHC-vendor/easyui/demo-mobile/simplelist/link.html
deleted file mode 100644
index c630576da..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/simplelist/link.html
+++ /dev/null
@@ -1 +0,0 @@
-
Link List - jQuery EasyUI Mobile Demo
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/tabs/basic.html b/vendor/FHC-vendor/easyui/demo-mobile/tabs/basic.html
deleted file mode 100644
index 4654e1256..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/tabs/basic.html
+++ /dev/null
@@ -1 +0,0 @@
-
Basic Tabs - jQuery EasyUI Mobile Demo
Java is a general-purpose, concurrent, class-based, object-oriented computer programming language that is specifically designed to have as few implementation dependencies as possible.
Java applications are typically compiled to bytecode (class file) that can run on any Java virtual machine (JVM) regardless of computer architecture.
Fortran (previously FORTRAN) is a general-purpose, imperative programming language that is especially suited to numeric computation and scientific computing. Originally developed by IBM at their campus in south San Jose, California[1] in the 1950s for scientific and engineering applications.
Perl is a family of high-level, general-purpose, interpreted, dynamic programming languages. The languages in this family include Perl 5 and Perl 6.
Though Perl is not officially an acronym, there are various backronyms in use, such as: Practical Extraction and Reporting Language. Perl was originally developed by Larry Wall in 1987 as a general-purpose Unix scripting language to make report processing easier. Since then, it has undergone many changes and revisions.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/tabs/nav.html b/vendor/FHC-vendor/easyui/demo-mobile/tabs/nav.html
deleted file mode 100644
index 061218f35..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/tabs/nav.html
+++ /dev/null
@@ -1 +0,0 @@
-
Navigation Tabs - jQuery EasyUI Mobile Demo
Devices
Modem
A modem (modulator-demodulator) is a device that modulates an analog carrier signal to encode digital information, and also demodulates such a carrier signal to decode the transmitted information.
Scanner
In computing, an image scanner—often abbreviated to just scanner—is a device that optically scans images, printed text, handwriting, or an object, and converts it to a digital image.
Pda
A personal digital assistant (PDA), also known as a palmtop computer, or personal data assistant, is a mobile device that functions as a personal information manager. PDAs are largely considered obsolete with the widespread adoption of smartphones.
Pda
A tablet computer, or simply tablet, is a one-piece mobile computer. Devices typically have a touchscreen, with finger or stylus gestures replacing the conventional computer mouse.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/tabs/pill.html b/vendor/FHC-vendor/easyui/demo-mobile/tabs/pill.html
deleted file mode 100644
index ead286386..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/tabs/pill.html
+++ /dev/null
@@ -1 +0,0 @@
-
Pill Tabs - jQuery EasyUI Mobile Demo
Java is a general-purpose, concurrent, class-based, object-oriented computer programming language that is specifically designed to have as few implementation dependencies as possible.
Java applications are typically compiled to bytecode (class file) that can run on any Java virtual machine (JVM) regardless of computer architecture.
Fortran (previously FORTRAN) is a general-purpose, imperative programming language that is especially suited to numeric computation and scientific computing. Originally developed by IBM at their campus in south San Jose, California[1] in the 1950s for scientific and engineering applications.
Perl is a family of high-level, general-purpose, interpreted, dynamic programming languages. The languages in this family include Perl 5 and Perl 6.
Though Perl is not officially an acronym, there are various backronyms in use, such as: Practical Extraction and Reporting Language. Perl was originally developed by Larry Wall in 1987 as a general-purpose Unix scripting language to make report processing easier. Since then, it has undergone many changes and revisions.
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo-mobile/toolbar/basic.html b/vendor/FHC-vendor/easyui/demo-mobile/toolbar/basic.html
deleted file mode 100644
index 6497d6812..000000000
--- a/vendor/FHC-vendor/easyui/demo-mobile/toolbar/basic.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
- Basic Toolbar - jQuery EasyUI Mobile Demo
-
-
-
-
-
-
-
-
-
Accordion is a part of easyui framework for jQuery. It lets you define your accordion component on web page more easily.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/accordion/ajax.html b/vendor/FHC-vendor/easyui/demo/accordion/ajax.html
deleted file mode 100644
index 87c1a01c4..000000000
--- a/vendor/FHC-vendor/easyui/demo/accordion/ajax.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
- Loading Accordion Content with AJAX - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Loading Accordion Content with AJAX
-
Click AJAX panel header to load content via AJAX.
-
-
-
-
Accordion for jQuery
-
Accordion is a part of easyui framework for jQuery. It lets you define your accordion component on web page more easily.
-
-
-
The accordion allows you to provide multiple panels and display one or more at a time. Each panel has built-in support for expanding and collapsing. Clicking on a panel header to expand or collapse that panel body. The panel content can be loaded via ajax by specifying a 'href' property. Users can define a panel to be selected. If it is not specified, then the first panel is taken by default.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/accordion/basic.html b/vendor/FHC-vendor/easyui/demo/accordion/basic.html
deleted file mode 100644
index 1f6c8f53c..000000000
--- a/vendor/FHC-vendor/easyui/demo/accordion/basic.html
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
- Basic Accordion - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Basic Accordion
-
Click on panel header to show its content.
-
-
-
-
Accordion for jQuery
-
Accordion is a part of easyui framework for jQuery. It lets you define your accordion component on web page more easily.
-
-
-
The accordion allows you to provide multiple panels and display one or more at a time. Each panel has built-in support for expanding and collapsing. Clicking on a panel header to expand or collapse that panel body. The panel content can be loaded via ajax by specifying a 'href' property. Users can define a panel to be selected. If it is not specified, then the first panel is taken by default.
Keep a expandable panel and prevent it from collapsing.
-
-
-
-
-
-
-
Accordion for jQuery
-
Accordion is a part of easyui framework for jQuery. It lets you define your accordion component on web page more easily.
-
-
-
Content1
-
-
-
Content2
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/accordion/fluid.html b/vendor/FHC-vendor/easyui/demo/accordion/fluid.html
deleted file mode 100644
index aaeb994af..000000000
--- a/vendor/FHC-vendor/easyui/demo/accordion/fluid.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
- Fluid Accordion - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Fluid Accordion
-
This example shows how to set the width of accordion to a percentage of its parent container.
-
-
-
-
width: 100%
-
-
-
-
-
-
-
-
-
width: 50%
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/accordion/multiple.html b/vendor/FHC-vendor/easyui/demo/accordion/multiple.html
deleted file mode 100644
index d75bfbc4f..000000000
--- a/vendor/FHC-vendor/easyui/demo/accordion/multiple.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
- Multiple Accordion Panels - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Multiple Accordion Panels
-
Enable 'multiple' mode to expand multiple panels at one time.
-
-
-
-
A programming language is a formal language designed to communicate instructions to a machine, particularly a computer. Programming languages can be used to create programs that control the behavior of a machine and/or to express algorithms precisely.
-
-
-
Java (Indonesian: Jawa) is an island of Indonesia. With a population of 135 million (excluding the 3.6 million on the island of Madura which is administered as part of the provinces of Java), Java is the world's most populous island, and one of the most densely populated places in the world.
-
-
-
C# is a multi-paradigm programming language encompassing strong typing, imperative, declarative, functional, generic, object-oriented (class-based), and component-oriented programming disciplines.
-
-
-
A dynamic, reflective, general-purpose object-oriented programming language.
-
-
-
Fortran (previously FORTRAN) is a general-purpose, imperative programming language that is especially suited to numeric computation and scientific computing.
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/accordion/tools.html b/vendor/FHC-vendor/easyui/demo/accordion/tools.html
deleted file mode 100644
index 6351a91e9..000000000
--- a/vendor/FHC-vendor/easyui/demo/accordion/tools.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
- Accordion Tools - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Accordion Tools
-
Click the tools on top right of panel to perform actions.
-
-
-
-
Accordion for jQuery
-
Accordion is a part of easyui framework for jQuery. It lets you define your accordion component on web page more easily.
-
-
-
The accordion allows you to provide multiple panels and display one ore more at a time. Each panel has built-in support for expanding and collapsing. Clicking on a panel header to expand or collapse that panel body. The panel content can be loaded via ajax by specifying a 'href' property. Users can define a panel to be selected. If it is not specified, then the first panel is taken by default.
-
-
-
-
-
-
Item ID
-
Product ID
-
List Price
-
Unit Cost
-
Attribute
-
Status
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/calendar/basic.html b/vendor/FHC-vendor/easyui/demo/calendar/basic.html
deleted file mode 100644
index 9d0d67276..000000000
--- a/vendor/FHC-vendor/easyui/demo/calendar/basic.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
- Basic Calendar - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Basic Calendar
-
Click to select date.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/calendar/custom.html b/vendor/FHC-vendor/easyui/demo/calendar/custom.html
deleted file mode 100644
index 7c570e6ce..000000000
--- a/vendor/FHC-vendor/easyui/demo/calendar/custom.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
- Custom Calendar - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Custom Calendar
-
This example shows how to custom the calendar date by using 'formatter' function.
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/calendar/disabledate.html b/vendor/FHC-vendor/easyui/demo/calendar/disabledate.html
deleted file mode 100644
index 0b1edeb5e..000000000
--- a/vendor/FHC-vendor/easyui/demo/calendar/disabledate.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
- Disable Calendar Date - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Disable Calendar Date
-
This example shows how to disable specified dates, only allows the user to select Mondays.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/calendar/firstday.html b/vendor/FHC-vendor/easyui/demo/calendar/firstday.html
deleted file mode 100644
index dd414c5eb..000000000
--- a/vendor/FHC-vendor/easyui/demo/calendar/firstday.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
- First Day of Week - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
First Day of Week
-
Choose the first day of the week.
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/calendar/fluid.html b/vendor/FHC-vendor/easyui/demo/calendar/fluid.html
deleted file mode 100644
index 3ca0fae3b..000000000
--- a/vendor/FHC-vendor/easyui/demo/calendar/fluid.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- Fluid Calendar - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Fluid Calendar
-
This example shows how to set the width of calendar to a percentage of its parent container.
-
-
-
width: 50%, height: 250px
-
-
width: 30%, height: 40%
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/combo/animation.html b/vendor/FHC-vendor/easyui/demo/combo/animation.html
deleted file mode 100644
index 4a958e74e..000000000
--- a/vendor/FHC-vendor/easyui/demo/combo/animation.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
- Combo Animation - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Combo Animation
-
Change the animation type when open & close the drop-down panel.
-
- Animation Type:
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/combo/basic.html b/vendor/FHC-vendor/easyui/demo/combo/basic.html
deleted file mode 100644
index cba9b8dfb..000000000
--- a/vendor/FHC-vendor/easyui/demo/combo/basic.html
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
- Basic Combo - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Basic Combo
-
Click the right arrow button to show drop down panel that can be filled with any content.
-
-
-
-
Select a language
-
- Java
- C#
- Ruby
- Basic
- Fortran
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/combobox/actions.html b/vendor/FHC-vendor/easyui/demo/combobox/actions.html
deleted file mode 100644
index d2605fc3e..000000000
--- a/vendor/FHC-vendor/easyui/demo/combobox/actions.html
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
- ComboBox Actions - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/switchbutton/basic.html b/vendor/FHC-vendor/easyui/demo/switchbutton/basic.html
deleted file mode 100644
index 56a09b586..000000000
--- a/vendor/FHC-vendor/easyui/demo/switchbutton/basic.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
- Basic SwitchButton - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Basic SwitchButton
-
Click the switchbutton to change its state.
-
-
-
-
-
Receive mail:
-
-
-
-
Shared network:
-
-
-
-
Subscribed:
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/autoheight.html b/vendor/FHC-vendor/easyui/demo/tabs/autoheight.html
deleted file mode 100644
index e34a3dc8e..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/autoheight.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
- Auto Height for Tabs - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Auto Height for Tabs
-
The tabs height is auto adjusted according to tab panel content.
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/basic.html b/vendor/FHC-vendor/easyui/demo/tabs/basic.html
deleted file mode 100644
index 293b91ce7..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/basic.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
- Basic Tabs - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Basic Tabs
-
Click tab strip to swap tab panel content.
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/dropdown.html b/vendor/FHC-vendor/easyui/demo/tabs/dropdown.html
deleted file mode 100644
index 7c9daacf3..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/dropdown.html
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
- Tabs with DropDown - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tabs with DropDown
-
This sample shows how to add a dropdown menu over a tab strip.
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
Welcome
-
Help Contents
-
Search
-
Dynamic Help
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/fixedwidth.html b/vendor/FHC-vendor/easyui/demo/tabs/fixedwidth.html
deleted file mode 100644
index 390e9878e..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/fixedwidth.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
- Fixed Tab Width - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Fixed Tab Width
-
The tab strips have fixed width and height.
-
-
-
-
Home Content.
-
-
-
Maps Content.
-
-
-
Journal Content.
-
-
-
History Content.
-
-
-
References Content.
-
-
-
Contact Content.
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/fluid.html b/vendor/FHC-vendor/easyui/demo/tabs/fluid.html
deleted file mode 100644
index d31d6fb65..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/fluid.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- Fluid Tabs - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Fluid Tabs
-
This example shows how to set the width of Tabs to a percentage of its parent container.
-
-
-
-
The tabs has a width of 100%.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/hover.html b/vendor/FHC-vendor/easyui/demo/tabs/hover.html
deleted file mode 100644
index 5b62ac90b..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/hover.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
- Hover Tabs - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Hover Tabs
-
Move mouse over the tab strip to open the tab panel.
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/images/modem.png b/vendor/FHC-vendor/easyui/demo/tabs/images/modem.png
deleted file mode 100644
index be5a2eb2f..000000000
Binary files a/vendor/FHC-vendor/easyui/demo/tabs/images/modem.png and /dev/null differ
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/images/pda.png b/vendor/FHC-vendor/easyui/demo/tabs/images/pda.png
deleted file mode 100644
index 1458d9bfa..000000000
Binary files a/vendor/FHC-vendor/easyui/demo/tabs/images/pda.png and /dev/null differ
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/images/scanner.png b/vendor/FHC-vendor/easyui/demo/tabs/images/scanner.png
deleted file mode 100644
index 974635d94..000000000
Binary files a/vendor/FHC-vendor/easyui/demo/tabs/images/scanner.png and /dev/null differ
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/images/tablet.png b/vendor/FHC-vendor/easyui/demo/tabs/images/tablet.png
deleted file mode 100644
index fa871f540..000000000
Binary files a/vendor/FHC-vendor/easyui/demo/tabs/images/tablet.png and /dev/null differ
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/nestedtabs.html b/vendor/FHC-vendor/easyui/demo/tabs/nestedtabs.html
deleted file mode 100644
index 94c2ac5dd..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/nestedtabs.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
- Nested Tabs - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Nested Tabs
-
The tab panel can contain sub tabs or other components.
-
-
-
-
-
Content 1
-
Content 2
-
Content 3
-
-
-
-
-
-
-
-
-
-
-
Title1
-
Title2
-
Title3
-
-
-
-
-
d11
-
d12
-
d13
-
-
-
d21
-
d22
-
d23
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/striptools.html b/vendor/FHC-vendor/easyui/demo/tabs/striptools.html
deleted file mode 100644
index 83e6ba0d1..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/striptools.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
- Tabs Strip Tools - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tabs Strip Tools
-
Click the mini-buttons on the tab strip to perform actions.
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
- This is the help content.
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/style.html b/vendor/FHC-vendor/easyui/demo/tabs/style.html
deleted file mode 100644
index f27eae1de..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/style.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
- Tabs Style - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tabs Style
-
Click the options below to change the tabs style.
-
- plain
- narrow
- pill
- justified
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/tabimage.html b/vendor/FHC-vendor/easyui/demo/tabs/tabimage.html
deleted file mode 100644
index e9df65de8..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/tabimage.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
- Tabs with Images - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tabs with Images
-
The tab strip can display big images.
-
-
-
-
A modem (modulator-demodulator) is a device that modulates an analog carrier signal to encode digital information, and also demodulates such a carrier signal to decode the transmitted information.
-
-
-
In computing, an image scanner—often abbreviated to just scanner—is a device that optically scans images, printed text, handwriting, or an object, and converts it to a digital image.
-
-
-
A personal digital assistant (PDA), also known as a palmtop computer, or personal data assistant, is a mobile device that functions as a personal information manager. PDAs are largely considered obsolete with the widespread adoption of smartphones.
-
-
-
A tablet computer, or simply tablet, is a one-piece mobile computer. Devices typically have a touchscreen, with finger or stylus gestures replacing the conventional computer mouse.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/tabposition.html b/vendor/FHC-vendor/easyui/demo/tabs/tabposition.html
deleted file mode 100644
index 9f1bc19ea..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/tabposition.html
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
- Tab Position - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tab Position
-
Click the 'position' drop-down list and select an item to change the tab position.
-
- Position:
-
-
-
-
-
jQuery EasyUI framework helps you build your web pages easily.
-
-
easyui is a collection of user-interface plugin based on jQuery.
-
easyui provides essential functionality for building modem, interactive, javascript applications.
-
using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.
-
complete framework for HTML5 web page.
-
easyui save your time and scales while developing your products.
-
easyui is very easy but powerful.
-
-
-
-
-
-
- This is the help content.
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/FHC-vendor/easyui/demo/tabs/tabstools.html b/vendor/FHC-vendor/easyui/demo/tabs/tabstools.html
deleted file mode 100644
index 48bdda83e..000000000
--- a/vendor/FHC-vendor/easyui/demo/tabs/tabstools.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
- Tabs Tools - jQuery EasyUI Demo
-
-
-
-
-
-
-
-
Tabs Tools
-
Click the buttons on the top right of tabs header to add or remove tab panel.