From 652223da644ffb9c9acbc04118a5c174fab10926 Mon Sep 17 00:00:00 2001 From: Stephen Soltesz Date: Fri, 27 Feb 2009 21:05:30 +0000 Subject: [PATCH] Take two: Initial import of www-register-wizard module. Pulled out of plcwww --- application/config/autoload.php | 127 ++ application/config/config.php | 317 +++ application/config/constants.php | 41 + application/config/database.php | 55 + application/config/hooks.php | 16 + application/config/index.html | 10 + application/config/mimes.php | 105 + application/config/routes.php | 48 + application/config/smileys.php | 66 + application/config/user_agents.php | 175 ++ application/controllers/conffile.php | 311 +++ application/controllers/confirm.php | 175 ++ application/controllers/download.php | 291 +++ application/controllers/index.html | 10 + application/controllers/register.php | 716 +++++++ application/controllers/welcome.php | 17 + application/errors/error_404.php | 35 + application/errors/error_db.php | 34 + application/errors/error_general.php | 34 + application/errors/error_php.php | 10 + application/errors/index.html | 10 + application/helpers/index.html | 10 + application/hooks/index.html | 10 + application/index.html | 10 + application/language/english/index.html | 10 + application/libraries/index.html | 10 + application/models/index.html | 10 + application/views/AddConfFile.php | 166 ++ application/views/ListConfFiles.php | 71 + application/views/UpdateConfFile.php | 166 ++ application/views/debug.php | 5 + application/views/footer.php | 15 + application/views/header.php | 20 + application/views/index.html | 10 + application/views/stage0.php | 27 + application/views/stage1_pcu_choose.php | 121 ++ application/views/stage2_pcu_confirm.php | 79 + application/views/stage3_node_choose.php | 170 ++ application/views/stage45_pcuport.php | 68 + application/views/stage4_node_confirm.php | 153 ++ application/views/stage5_bootimage.php | 47 + application/views/stage6_download.php | 63 + application/views/stage7_firstcontact.php | 55 + application/views/stage8_rebootpcu.php | 72 + application/views/stage9_complete.php | 18 + application/views/welcome_message.php | 62 + cache/index.html | 10 + codeigniter/Base4.php | 69 + codeigniter/Base5.php | 56 + codeigniter/CodeIgniter.php | 276 +++ codeigniter/Common.php | 307 +++ codeigniter/Compat.php | 93 + codeigniter/index.html | 10 + database/DB.php | 146 ++ database/DB_active_rec.php | 1770 ++++++++++++++++ database/DB_cache.php | 195 ++ database/DB_driver.php | 1319 ++++++++++++ database/DB_forge.php | 355 ++++ database/DB_result.php | 342 +++ database/DB_utility.php | 389 ++++ database/drivers/index.html | 10 + database/drivers/mssql/index.html | 10 + database/drivers/mssql/mssql_driver.php | 611 ++++++ database/drivers/mssql/mssql_forge.php | 248 +++ database/drivers/mssql/mssql_result.php | 169 ++ database/drivers/mssql/mssql_utility.php | 123 ++ database/drivers/mysql/index.html | 10 + database/drivers/mysql/mysql_driver.php | 623 ++++++ database/drivers/mysql/mysql_forge.php | 254 +++ database/drivers/mysql/mysql_result.php | 169 ++ database/drivers/mysql/mysql_utility.php | 245 +++ database/drivers/mysqli/index.html | 10 + database/drivers/mysqli/mysqli_driver.php | 606 ++++++ database/drivers/mysqli/mysqli_forge.php | 254 +++ database/drivers/mysqli/mysqli_result.php | 169 ++ database/drivers/mysqli/mysqli_utility.php | 123 ++ database/drivers/oci8/index.html | 10 + database/drivers/oci8/oci8_driver.php | 726 +++++++ database/drivers/oci8/oci8_forge.php | 248 +++ database/drivers/oci8/oci8_result.php | 249 +++ database/drivers/oci8/oci8_utility.php | 122 ++ database/drivers/odbc/index.html | 10 + database/drivers/odbc/odbc_driver.php | 583 ++++++ database/drivers/odbc/odbc_forge.php | 266 +++ database/drivers/odbc/odbc_result.php | 228 ++ database/drivers/odbc/odbc_utility.php | 148 ++ database/drivers/postgre/index.html | 10 + database/drivers/postgre/postgre_driver.php | 625 ++++++ database/drivers/postgre/postgre_forge.php | 248 +++ database/drivers/postgre/postgre_result.php | 169 ++ database/drivers/postgre/postgre_utility.php | 124 ++ database/drivers/sqlite/index.html | 10 + database/drivers/sqlite/sqlite_driver.php | 601 ++++++ database/drivers/sqlite/sqlite_forge.php | 265 +++ database/drivers/sqlite/sqlite_result.php | 179 ++ database/drivers/sqlite/sqlite_utility.php | 141 ++ database/index.html | 10 + fonts/index.html | 10 + fonts/texb.ttf | Bin 0 -> 143821 bytes helpers/array_helper.php | 78 + helpers/compatibility_helper.php | 498 +++++ helpers/cookie_helper.php | 136 ++ helpers/date_helper.php | 611 ++++++ helpers/directory_helper.php | 80 + helpers/download_helper.php | 100 + helpers/email_helper.php | 62 + helpers/file_helper.php | 465 +++++ helpers/form_helper.php | 963 +++++++++ helpers/html_helper.php | 376 ++++ helpers/index.html | 10 + helpers/inflector_helper.php | 169 ++ helpers/language_helper.php | 58 + helpers/number_helper.php | 75 + helpers/path_helper.php | 72 + helpers/security_helper.php | 126 ++ helpers/smiley_helper.php | 175 ++ helpers/string_helper.php | 273 +++ helpers/text_helper.php | 443 ++++ helpers/typography_helper.php | 72 + helpers/url_helper.php | 582 ++++++ helpers/xml_helper.php | 62 + index.php | 118 ++ language/english/calendar_lang.php | 51 + language/english/date_lang.php | 60 + language/english/db_lang.php | 28 + language/english/email_lang.php | 23 + language/english/form_validation_lang.php | 24 + language/english/ftp_lang.php | 17 + language/english/imglib_lang.php | 23 + language/english/index.html | 10 + language/english/number_lang.php | 10 + language/english/profiler_lang.php | 19 + language/english/scaffolding_lang.php | 17 + language/english/unit_test_lang.php | 24 + language/english/upload_lang.php | 22 + language/english/validation_lang.php | 24 + language/index.html | 10 + libraries/Benchmark.php | 113 + libraries/Calendar.php | 477 +++++ libraries/Config.php | 244 +++ libraries/Controller.php | 127 ++ libraries/Email.php | 1953 ++++++++++++++++++ libraries/Encrypt.php | 484 +++++ libraries/Exceptions.php | 172 ++ libraries/Form_validation.php | 1280 ++++++++++++ libraries/Ftp.php | 618 ++++++ libraries/Hooks.php | 226 ++ libraries/Image_lib.php | 1546 ++++++++++++++ libraries/Input.php | 1059 ++++++++++ libraries/Language.php | 124 ++ libraries/Loader.php | 1088 ++++++++++ libraries/Log.php | 117 ++ libraries/Model.php | 83 + libraries/Output.php | 478 +++++ libraries/Pagination.php | 244 +++ libraries/Parser.php | 173 ++ libraries/Profiler.php | 392 ++++ libraries/Router.php | 385 ++++ libraries/Session.php | 758 +++++++ libraries/Sha1.php | 251 +++ libraries/Table.php | 440 ++++ libraries/Trackback.php | 550 +++++ libraries/Typography.php | 342 +++ libraries/URI.php | 584 ++++++ libraries/Unit_test.php | 347 ++++ libraries/Upload.php | 931 +++++++++ libraries/User_agent.php | 502 +++++ libraries/Validation.php | 875 ++++++++ libraries/Xmlrpc.php | 1421 +++++++++++++ libraries/Xmlrpcs.php | 536 +++++ libraries/Zip.php | 359 ++++ libraries/index.html | 10 + logs/index.html | 10 + plugins/captcha_pi.php | 356 ++++ plugins/index.html | 10 + plugins/js_calendar_pi.php | 629 ++++++ rounded.css | 59 + rounded.js | 187 ++ scaffolding/Scaffolding.php | 291 +++ scaffolding/images/background.jpg | Bin 0 -> 410 bytes scaffolding/images/index.html | 10 + scaffolding/images/logo.jpg | Bin 0 -> 4518 bytes scaffolding/index.html | 10 + scaffolding/views/add.php | 32 + scaffolding/views/delete.php | 9 + scaffolding/views/edit.php | 33 + scaffolding/views/footer.php | 10 + scaffolding/views/header.php | 29 + scaffolding/views/index.html | 10 + scaffolding/views/no_data.php | 8 + scaffolding/views/stylesheet.css | 143 ++ scaffolding/views/view.php | 27 + www-register-wizard.spec | 64 + 193 files changed, 45186 insertions(+) create mode 100644 application/config/autoload.php create mode 100644 application/config/config.php create mode 100644 application/config/constants.php create mode 100644 application/config/database.php create mode 100644 application/config/hooks.php create mode 100644 application/config/index.html create mode 100644 application/config/mimes.php create mode 100644 application/config/routes.php create mode 100644 application/config/smileys.php create mode 100644 application/config/user_agents.php create mode 100644 application/controllers/conffile.php create mode 100644 application/controllers/confirm.php create mode 100644 application/controllers/download.php create mode 100644 application/controllers/index.html create mode 100644 application/controllers/register.php create mode 100644 application/controllers/welcome.php create mode 100644 application/errors/error_404.php create mode 100644 application/errors/error_db.php create mode 100644 application/errors/error_general.php create mode 100644 application/errors/error_php.php create mode 100644 application/errors/index.html create mode 100644 application/helpers/index.html create mode 100644 application/hooks/index.html create mode 100644 application/index.html create mode 100644 application/language/english/index.html create mode 100644 application/libraries/index.html create mode 100644 application/models/index.html create mode 100644 application/views/AddConfFile.php create mode 100644 application/views/ListConfFiles.php create mode 100644 application/views/UpdateConfFile.php create mode 100644 application/views/debug.php create mode 100644 application/views/footer.php create mode 100644 application/views/header.php create mode 100644 application/views/index.html create mode 100644 application/views/stage0.php create mode 100644 application/views/stage1_pcu_choose.php create mode 100644 application/views/stage2_pcu_confirm.php create mode 100644 application/views/stage3_node_choose.php create mode 100644 application/views/stage45_pcuport.php create mode 100644 application/views/stage4_node_confirm.php create mode 100644 application/views/stage5_bootimage.php create mode 100644 application/views/stage6_download.php create mode 100644 application/views/stage7_firstcontact.php create mode 100644 application/views/stage8_rebootpcu.php create mode 100644 application/views/stage9_complete.php create mode 100644 application/views/welcome_message.php create mode 100644 cache/index.html create mode 100644 codeigniter/Base4.php create mode 100644 codeigniter/Base5.php create mode 100644 codeigniter/CodeIgniter.php create mode 100644 codeigniter/Common.php create mode 100644 codeigniter/Compat.php create mode 100644 codeigniter/index.html create mode 100644 database/DB.php create mode 100644 database/DB_active_rec.php create mode 100644 database/DB_cache.php create mode 100644 database/DB_driver.php create mode 100644 database/DB_forge.php create mode 100644 database/DB_result.php create mode 100644 database/DB_utility.php create mode 100644 database/drivers/index.html create mode 100644 database/drivers/mssql/index.html create mode 100644 database/drivers/mssql/mssql_driver.php create mode 100644 database/drivers/mssql/mssql_forge.php create mode 100644 database/drivers/mssql/mssql_result.php create mode 100644 database/drivers/mssql/mssql_utility.php create mode 100644 database/drivers/mysql/index.html create mode 100644 database/drivers/mysql/mysql_driver.php create mode 100644 database/drivers/mysql/mysql_forge.php create mode 100644 database/drivers/mysql/mysql_result.php create mode 100644 database/drivers/mysql/mysql_utility.php create mode 100644 database/drivers/mysqli/index.html create mode 100644 database/drivers/mysqli/mysqli_driver.php create mode 100644 database/drivers/mysqli/mysqli_forge.php create mode 100644 database/drivers/mysqli/mysqli_result.php create mode 100644 database/drivers/mysqli/mysqli_utility.php create mode 100644 database/drivers/oci8/index.html create mode 100644 database/drivers/oci8/oci8_driver.php create mode 100644 database/drivers/oci8/oci8_forge.php create mode 100644 database/drivers/oci8/oci8_result.php create mode 100644 database/drivers/oci8/oci8_utility.php create mode 100644 database/drivers/odbc/index.html create mode 100644 database/drivers/odbc/odbc_driver.php create mode 100644 database/drivers/odbc/odbc_forge.php create mode 100644 database/drivers/odbc/odbc_result.php create mode 100644 database/drivers/odbc/odbc_utility.php create mode 100644 database/drivers/postgre/index.html create mode 100644 database/drivers/postgre/postgre_driver.php create mode 100644 database/drivers/postgre/postgre_forge.php create mode 100644 database/drivers/postgre/postgre_result.php create mode 100644 database/drivers/postgre/postgre_utility.php create mode 100644 database/drivers/sqlite/index.html create mode 100644 database/drivers/sqlite/sqlite_driver.php create mode 100644 database/drivers/sqlite/sqlite_forge.php create mode 100644 database/drivers/sqlite/sqlite_result.php create mode 100644 database/drivers/sqlite/sqlite_utility.php create mode 100644 database/index.html create mode 100644 fonts/index.html create mode 100644 fonts/texb.ttf create mode 100644 helpers/array_helper.php create mode 100644 helpers/compatibility_helper.php create mode 100644 helpers/cookie_helper.php create mode 100644 helpers/date_helper.php create mode 100644 helpers/directory_helper.php create mode 100644 helpers/download_helper.php create mode 100644 helpers/email_helper.php create mode 100644 helpers/file_helper.php create mode 100644 helpers/form_helper.php create mode 100644 helpers/html_helper.php create mode 100644 helpers/index.html create mode 100644 helpers/inflector_helper.php create mode 100644 helpers/language_helper.php create mode 100644 helpers/number_helper.php create mode 100644 helpers/path_helper.php create mode 100644 helpers/security_helper.php create mode 100644 helpers/smiley_helper.php create mode 100644 helpers/string_helper.php create mode 100644 helpers/text_helper.php create mode 100644 helpers/typography_helper.php create mode 100644 helpers/url_helper.php create mode 100644 helpers/xml_helper.php create mode 100644 index.php create mode 100644 language/english/calendar_lang.php create mode 100644 language/english/date_lang.php create mode 100644 language/english/db_lang.php create mode 100644 language/english/email_lang.php create mode 100644 language/english/form_validation_lang.php create mode 100644 language/english/ftp_lang.php create mode 100644 language/english/imglib_lang.php create mode 100644 language/english/index.html create mode 100644 language/english/number_lang.php create mode 100644 language/english/profiler_lang.php create mode 100644 language/english/scaffolding_lang.php create mode 100644 language/english/unit_test_lang.php create mode 100644 language/english/upload_lang.php create mode 100644 language/english/validation_lang.php create mode 100644 language/index.html create mode 100644 libraries/Benchmark.php create mode 100644 libraries/Calendar.php create mode 100644 libraries/Config.php create mode 100644 libraries/Controller.php create mode 100644 libraries/Email.php create mode 100644 libraries/Encrypt.php create mode 100644 libraries/Exceptions.php create mode 100644 libraries/Form_validation.php create mode 100644 libraries/Ftp.php create mode 100644 libraries/Hooks.php create mode 100644 libraries/Image_lib.php create mode 100644 libraries/Input.php create mode 100644 libraries/Language.php create mode 100644 libraries/Loader.php create mode 100644 libraries/Log.php create mode 100644 libraries/Model.php create mode 100644 libraries/Output.php create mode 100644 libraries/Pagination.php create mode 100644 libraries/Parser.php create mode 100644 libraries/Profiler.php create mode 100644 libraries/Router.php create mode 100644 libraries/Session.php create mode 100644 libraries/Sha1.php create mode 100644 libraries/Table.php create mode 100644 libraries/Trackback.php create mode 100644 libraries/Typography.php create mode 100644 libraries/URI.php create mode 100644 libraries/Unit_test.php create mode 100644 libraries/Upload.php create mode 100644 libraries/User_agent.php create mode 100644 libraries/Validation.php create mode 100644 libraries/Xmlrpc.php create mode 100644 libraries/Xmlrpcs.php create mode 100644 libraries/Zip.php create mode 100644 libraries/index.html create mode 100644 logs/index.html create mode 100644 plugins/captcha_pi.php create mode 100644 plugins/index.html create mode 100644 plugins/js_calendar_pi.php create mode 100644 rounded.css create mode 100644 rounded.js create mode 100644 scaffolding/Scaffolding.php create mode 100644 scaffolding/images/background.jpg create mode 100644 scaffolding/images/index.html create mode 100644 scaffolding/images/logo.jpg create mode 100644 scaffolding/index.html create mode 100644 scaffolding/views/add.php create mode 100644 scaffolding/views/delete.php create mode 100644 scaffolding/views/edit.php create mode 100644 scaffolding/views/footer.php create mode 100644 scaffolding/views/header.php create mode 100644 scaffolding/views/index.html create mode 100644 scaffolding/views/no_data.php create mode 100644 scaffolding/views/stylesheet.css create mode 100644 scaffolding/views/view.php create mode 100755 www-register-wizard.spec diff --git a/application/config/autoload.php b/application/config/autoload.php new file mode 100644 index 0000000..b99ffe4 --- /dev/null +++ b/application/config/autoload.php @@ -0,0 +1,127 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/config/mimes.php b/application/config/mimes.php new file mode 100644 index 0000000..51e444e --- /dev/null +++ b/application/config/mimes.php @@ -0,0 +1,105 @@ + 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'), + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'psd' => 'application/x-photoshop', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => array('application/pdf', 'application/x-download'), + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => array('application/excel', 'application/vnd.ms-excel'), + 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'), + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'php' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'), + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => array('audio/mpeg', 'audio/mpg'), + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => array('image/png', 'image/x-png'), + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => array('text/plain', 'text/x-log'), + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'word' => array('application/msword', 'application/octet-stream'), + 'xl' => 'application/excel', + 'eml' => 'message/rfc822' + ); + + +/* End of file mimes.php */ +/* Location: ./system/application/config/mimes.php */ \ No newline at end of file diff --git a/application/config/routes.php b/application/config/routes.php new file mode 100644 index 0000000..beeff25 --- /dev/null +++ b/application/config/routes.php @@ -0,0 +1,48 @@ + array('grin.gif', '19', '19', 'grin'), + ':lol:' => array('lol.gif', '19', '19', 'LOL'), + ':cheese:' => array('cheese.gif', '19', '19', 'cheese'), + ':)' => array('smile.gif', '19', '19', 'smile'), + ';-)' => array('wink.gif', '19', '19', 'wink'), + ';)' => array('wink.gif', '19', '19', 'wink'), + ':smirk:' => array('smirk.gif', '19', '19', 'smirk'), + ':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'), + ':-S' => array('confused.gif', '19', '19', 'confused'), + ':wow:' => array('surprise.gif', '19', '19', 'surprised'), + ':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'), + ':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'), + '%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'), + ';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'), + ':P' => array('raspberry.gif', '19', '19', 'raspberry'), + ':blank:' => array('blank.gif', '19', '19', 'blank stare'), + ':long:' => array('longface.gif', '19', '19', 'long face'), + ':ohh:' => array('ohh.gif', '19', '19', 'ohh'), + ':grrr:' => array('grrr.gif', '19', '19', 'grrr'), + ':gulp:' => array('gulp.gif', '19', '19', 'gulp'), + '8-/' => array('ohoh.gif', '19', '19', 'oh oh'), + ':down:' => array('downer.gif', '19', '19', 'downer'), + ':red:' => array('embarrassed.gif', '19', '19', 'red face'), + ':sick:' => array('sick.gif', '19', '19', 'sick'), + ':shut:' => array('shuteye.gif', '19', '19', 'shut eye'), + ':-/' => array('hmm.gif', '19', '19', 'hmmm'), + '>:(' => array('mad.gif', '19', '19', 'mad'), + ':mad:' => array('mad.gif', '19', '19', 'mad'), + '>:-(' => array('angry.gif', '19', '19', 'angry'), + ':angry:' => array('angry.gif', '19', '19', 'angry'), + ':zip:' => array('zip.gif', '19', '19', 'zipper'), + ':kiss:' => array('kiss.gif', '19', '19', 'kiss'), + ':ahhh:' => array('shock.gif', '19', '19', 'shock'), + ':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'), + ':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'), + ':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'), + ':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'), + ':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'), + ':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'), + ':vampire:' => array('vampire.gif', '19', '19', 'vampire'), + ':snake:' => array('snake.gif', '19', '19', 'snake'), + ':exclaim:' => array('exclaim.gif', '19', '19', 'excaim'), + ':question:' => array('question.gif', '19', '19', 'question') // no comma after last item + + ); + +/* End of file smileys.php */ +/* Location: ./system/application/config/smileys.php */ \ No newline at end of file diff --git a/application/config/user_agents.php b/application/config/user_agents.php new file mode 100644 index 0000000..38bfc1f --- /dev/null +++ b/application/config/user_agents.php @@ -0,0 +1,175 @@ + 'Windows Longhorn', + 'windows nt 5.2' => 'Windows 2003', + 'windows nt 5.0' => 'Windows 2000', + 'windows nt 5.1' => 'Windows XP', + 'windows nt 4.0' => 'Windows NT 4.0', + 'winnt4.0' => 'Windows NT 4.0', + 'winnt 4.0' => 'Windows NT', + 'winnt' => 'Windows NT', + 'windows 98' => 'Windows 98', + 'win98' => 'Windows 98', + 'windows 95' => 'Windows 95', + 'win95' => 'Windows 95', + 'windows' => 'Unknown Windows OS', + 'os x' => 'Mac OS X', + 'ppc mac' => 'Power PC Mac', + 'freebsd' => 'FreeBSD', + 'ppc' => 'Macintosh', + 'linux' => 'Linux', + 'debian' => 'Debian', + 'sunos' => 'Sun Solaris', + 'beos' => 'BeOS', + 'apachebench' => 'ApacheBench', + 'aix' => 'AIX', + 'irix' => 'Irix', + 'osf' => 'DEC OSF', + 'hp-ux' => 'HP-UX', + 'netbsd' => 'NetBSD', + 'bsdi' => 'BSDi', + 'openbsd' => 'OpenBSD', + 'gnu' => 'GNU/Linux', + 'unix' => 'Unknown Unix OS' + ); + + +// The order of this array should NOT be changed. Many browsers return +// multiple browser types so we want to identify the sub-type first. +$browsers = array( + 'Opera' => 'Opera', + 'MSIE' => 'Internet Explorer', + 'Internet Explorer' => 'Internet Explorer', + 'Shiira' => 'Shiira', + 'Firefox' => 'Firefox', + 'Chimera' => 'Chimera', + 'Phoenix' => 'Phoenix', + 'Firebird' => 'Firebird', + 'Camino' => 'Camino', + 'Netscape' => 'Netscape', + 'OmniWeb' => 'OmniWeb', + 'Mozilla' => 'Mozilla', + 'Safari' => 'Safari', + 'Konqueror' => 'Konqueror', + 'icab' => 'iCab', + 'Lynx' => 'Lynx', + 'Links' => 'Links', + 'hotjava' => 'HotJava', + 'amaya' => 'Amaya', + 'IBrowse' => 'IBrowse' + ); + +$mobiles = array( + // legacy array, old values commented out + 'mobileexplorer' => 'Mobile Explorer', +// 'openwave' => 'Open Wave', +// 'opera mini' => 'Opera Mini', +// 'operamini' => 'Opera Mini', +// 'elaine' => 'Palm', + 'palmsource' => 'Palm', +// 'digital paths' => 'Palm', +// 'avantgo' => 'Avantgo', +// 'xiino' => 'Xiino', + 'palmscape' => 'Palmscape', +// 'nokia' => 'Nokia', +// 'ericsson' => 'Ericsson', +// 'blackberry' => 'BlackBerry', +// 'motorola' => 'Motorola' + + // Phones and Manufacturers + 'motorola' => "Motorola", + 'nokia' => "Nokia", + 'palm' => "Palm", + 'iphone' => "Apple iPhone", + 'ipod' => "Apple iPod Touch", + 'sony' => "Sony Ericsson", + 'ericsson' => "Sony Ericsson", + 'blackberry' => "BlackBerry", + 'cocoon' => "O2 Cocoon", + 'blazer' => "Treo", + 'lg' => "LG", + 'amoi' => "Amoi", + 'xda' => "XDA", + 'mda' => "MDA", + 'vario' => "Vario", + 'htc' => "HTC", + 'samsung' => "Samsung", + 'sharp' => "Sharp", + 'sie-' => "Siemens", + 'alcatel' => "Alcatel", + 'benq' => "BenQ", + 'ipaq' => "HP iPaq", + 'mot-' => "Motorola", + 'playstation portable' => "PlayStation Portable", + 'hiptop' => "Danger Hiptop", + 'nec-' => "NEC", + 'panasonic' => "Panasonic", + 'philips' => "Philips", + 'sagem' => "Sagem", + 'sanyo' => "Sanyo", + 'spv' => "SPV", + 'zte' => "ZTE", + 'sendo' => "Sendo", + + // Operating Systems + 'symbian' => "Symbian", + 'SymbianOS' => "SymbianOS", + 'elaine' => "Palm", + 'palm' => "Palm", + 'series60' => "Symbian S60", + 'windows ce' => "Windows CE", + + // Browsers + 'obigo' => "Obigo", + 'netfront' => "Netfront Browser", + 'openwave' => "Openwave Browser", + 'mobilexplorer' => "Mobile Explorer", + 'operamini' => "Opera Mini", + 'opera mini' => "Opera Mini", + + // Other + 'digital paths' => "Digital Paths", + 'avantgo' => "AvantGo", + 'xiino' => "Xiino", + 'novarra' => "Novarra Transcoder", + 'vodafone' => "Vodafone", + 'docomo' => "NTT DoCoMo", + 'o2' => "O2", + + // Fallback + 'mobile' => "Generic Mobile", + 'wireless' => "Generic Mobile", + 'j2me' => "Generic Mobile", + 'midp' => "Generic Mobile", + 'cldc' => "Generic Mobile", + 'up.link' => "Generic Mobile", + 'up.browser' => "Generic Mobile", + 'smartphone' => "Generic Mobile", + 'cellphone' => "Generic Mobile" + ); + +// There are hundreds of bots but these are the most common. +$robots = array( + 'googlebot' => 'Googlebot', + 'msnbot' => 'MSNBot', + 'slurp' => 'Inktomi Slurp', + 'yahoo' => 'Yahoo', + 'askjeeves' => 'AskJeeves', + 'fastcrawler' => 'FastCrawler', + 'infoseek' => 'InfoSeek Robot 1.0', + 'lycos' => 'Lycos' + ); + +/* End of file user_agents.php */ +/* Location: ./system/application/config/user_agents.php */ \ No newline at end of file diff --git a/application/controllers/conffile.php b/application/controllers/conffile.php new file mode 100644 index 0000000..9f59fc6 --- /dev/null +++ b/application/controllers/conffile.php @@ -0,0 +1,311 @@ +GetConfFiles(); + print $api->error(); + $data = array(); + $data['all_conf_files'] = $all_conf_files; + $data['api'] = $api; + $data['stage'] = 1; + + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('ListConfFile', $data); + $this->load->view('footer', $data); + + } + function get_stage_data(&$data) + { + global $api, $plc; + $data['nodegroups'] = $api->GetNodeGroups(); + $data['nodes'] = $api->GetNodes(NULL, array('hostname', 'node_id')); + return $data; + } + function convert_checked($val) + { + if ( $val == "on" || $val == True ) + { + print "CALLED CHECKED '$val' .
"; + return True; + } else { + return False; + } + } + function update($conf_file_id=-1) + { + global $api, $plc; + $conf_file_id = intval($conf_file_id); + + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + $this->validation->set_error_delimiters('', ''); + $data = array(); + + $data['submit_caption'] = "Update"; + $data['title'] = "Update existing node configuration file"; + $data['conf_file_id'] = $conf_file_id; + + $fields['enabled'] = "Enabled"; + $fields['source'] = "Source"; + $fields['dest'] = "Destination"; + $fields['file_permissions'] = "File Permissions"; + $fields['file_owner'] = "File Owner"; + $fields['file_group'] = "File Group"; + $fields['preinstall_cmd'] = "Preinstall Command"; + $fields['postinstall_cmd'] = "Postinstall Command"; + $fields['error_cmd'] = "Error Command"; + $fields['ignore_cmd_errors'] = "Ignore Command Errors"; + $fields['always_update'] = "Always Update"; + $fields['scope'] = "ConfFile Scope"; + $fields['node_ids'] = ""; + $fields['nodegroup_ids'] = ""; + $this->validation->set_fields($fields); + + if( isset($_REQUEST['submitted']) ) + { + print "SUBMITTED REQUEST"; + $rules['enabled'] = "trim"; + $rules['source'] = "trim|required"; + $rules['dest'] = "trim|required"; + $rules['file_permissions'] = "trim"; + $rules['file_owner'] = "trim"; + $rules['file_group'] = "trim"; + $rules['preinstall_cmd'] = "trim"; + $rules['postinstall_cmd'] = "trim"; + $rules['error_cmd'] = "trim"; + $rules['ignore_cmd_errors'] = "trim"; + $rules['always_update'] = "trim"; + $rules['scope'] = "trim"; + $rules['node_ids'] = "trim"; + $rules['nodegroup_ids'] = "trim"; + $this->validation->set_rules($rules); + + $error_occurred= 0; + if ($this->validation->run() == TRUE) + { + $data['enabled'] = $this->validation->enabled; + $data['source'] = $this->validation->source; + $data['dest'] = $this->validation->dest; + $data['file_permissions'] = $this->validation->file_permissions; + $data['file_owner'] = $this->validation->file_owner; + $data['file_group'] = $this->validation->file_group; + $data['preinstall_cmd'] = $this->validation->preinstall_cmd; + $data['postinstall_cmd'] = $this->validation->postinstall_cmd; + $data['error_cmd'] = $this->validation->error_cmd; + $data['ignore_cmd_errors'] = $this->validation->ignore_cmd_errors; + $data['always_update'] = $this->validation->always_update; + $data['scope'] = $this->validation->scope; + $data['node_ids'] = array($this->validation->node_ids); + $data['nodegroup_ids'] = array($this->validation->nodegroup_ids); + + print "
"; + print "ena: " . $data['enabled'] . "
"; + print "ice: " . $data['ignore_cmd_errors'] . "
"; + print "alu: " . $data['always_update'] . "
"; + $conf_fields = array(); + + $data['enabled'] = $this->convert_checked($data['enabled']); + $data['ignore_cmd_errors'] = $this->convert_checked($data['ignore_cmd_errors']); + $data['always_update'] = $this->convert_checked($data['always_update']); + + # TODO: UpdateConfFile does not honor the values of 'node_ids' + # and 'nodegroup_ids'. These are read-only values. Instead + # there needs to be a check to GetConfFile() followed by: + # AddConfFileToNode(or Group) or, + # DeleteConfFileFromNode(or Group) as appropriate. + # This would be easier to update in the API, but it's not + # clear if there are other semantics that are being honored by + # doing it the way that it currently is. + + $return = $api->UpdateConfFile($conf_file_id, $data); + if ($return != 1 ) { + print $api->error(); + $error_occurred= 1; + } + $return = $api->GetConfFiles(array($conf_file_id)); + print "
";
+				print_r($return );
+				print "
"; + + } else { + print "VALIDATION FAILED
"; + print $this->validation->error_string ; + print "VALIDATION FAILED
"; + $error_occurred= 1; + } + + if( !$error_occurred ) + { + $edit_finalized= 1; + $finalized_message= "Successfully updated."; + } + } else { + + $conf_file = $api->GetConfFiles(array(intval($conf_file_id))); + if (empty($conf_file)) { + print $api->error(); + } + $conf_file = $conf_file[0]; + + $data['enabled'] = $conf_file['enabled']; + $data['source'] = $conf_file['source']; + $data['dest'] = $conf_file['dest']; + $data['file_permissions'] = $conf_file['file_permissions']; + $data['file_owner'] = $conf_file['file_owner']; + $data['file_group'] = $conf_file['file_group']; + $data['preinstall_cmd'] = $conf_file['preinstall_cmd']; + $data['postinstall_cmd'] = $conf_file['postinstall_cmd']; + $data['error_cmd'] = $conf_file['error_cmd']; + $data['ignore_cmd_errors'] = $conf_file['ignore_cmd_errors']; + $data['always_update'] = $conf_file['always_update']; + $data['node_ids'] = $conf_file['node_ids']; + $data['nodegroup_ids'] = $conf_file['nodegroup_ids']; + + if( count($data['nodegroup_ids']) == 0 && count($data['node_ids']) == 0 ) + { + $data['scope'] = "global"; + } else { + if( count($data['nodegroup_ids']) == 0 && is_numeric($data['node_ids'][0]) ) + { + $data['scope'] = "node"; + } elseif( count($data['node_ids']) == 0 && is_numeric($data['nodegroup_ids'][0]) ) + { + $data['scope'] = "group"; + } + } + } + + $data = $this->get_stage_data($data); + + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('UpdateConfFile', $data); + $this->load->view('footer', $data); + } + + function add() + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + $this->validation->set_error_delimiters('', ''); + $data = array(); + + $fields['enabled'] = "Enabled"; + $fields['source'] = "Source"; + $fields['dest'] = "Destination"; + $fields['file_permissions'] = "File Permissions"; + $fields['file_owner'] = "File Owner"; + $fields['file_group'] = "File Group"; + $fields['preinstall_cmd'] = "Preinstall Command"; + $fields['postinstall_cmd'] = "Postinstall Command"; + $fields['error_cmd'] = "Error Command"; + $fields['ignore_cmd_errors'] = "Ignore Command Errors"; + $fields['always_update'] = "Always Update"; + $fields['scope'] = "ConfFile Scope"; + $fields['node_ids'] = ""; + $fields['nodegroup_ids'] = ""; + $this->validation->set_fields($fields); + + if ( isset($_REQUEST['submitted']) ) + { + $action= $_REQUEST["action"]; + $submitted= $_REQUEST["submitted"]; + } else { + $action= 'default'; + $submitted= False; + } + + $data['submit_caption']= "Create"; + $data['title'] = "Create new node configuration file"; + + if( $submitted ) + { + $rules['enabled'] = "trim|required|integer|intval"; + $rules['source'] = "trim|required"; + $rules['dest'] = "trim|required"; + $rules['file_permissions'] = "trim"; + $rules['file_owner'] = "trim"; + $rules['file_group'] = "trim"; + $rules['preinstall_cmd'] = "trim"; + $rules['postinstall_cmd'] = "trim"; + $rules['error_cmd'] = "trim"; + $rules['ignore_cmd_errors'] = "trim|integer|intval"; + $rules['always_update'] = "trim|integer|intval"; + $rules['scope'] = "trim"; + $rules['node_ids'] = "trim"; + $rules['nodegroup_ids'] = "trim"; + $this->validation->set_rules($rules); + + $data['enabled'] = $this->validation->enabled; + $data['source'] = $this->validation->source; + $data['dest'] = $this->validation->dest; + $data['file_permissions'] = $this->validation->file_permissions; + $data['file_owner'] = $this->validation->file_owner; + $data['file_group'] = $this->validation->file_group; + $data['preinstall_cmd'] = $this->validation->preinstall_cmd; + $data['postinstall_cmd'] = $this->validation->postinstall_cmd; + $data['error_cmd'] = $this->validation->error_cmd; + $data['ignore_cmd_errors'] = $this->validation->ignore_cmd_errors; + $data['always_update'] = $this->validation->always_update; + $data['scope'] = $this->validation->scope; + $data['node_ids'] = array($this->validation->node_ids); + $data['nodegroup_ids'] = array($this->validation->nodegroup_ids); + + if ($this->validation->run() == TRUE) + { + $conf_file_id = $api->AddConfFile($data); + if ($conf_file_id == -1 ) { + print $api->error(); + } + } + + if( !$error_occurred ) + { + $edit_finalized= 1; + $finalized_message= "Successfully created."; + } + } else { + // set up default values for a new one + $data['enabled'] = 0; + $data['source'] = ""; + $data['dest'] = ""; + $data['file_permissions'] = "644"; + $data['file_owner'] = "root"; + $data['file_group'] = "root"; + $data['preinstall_cmd'] = ""; + $data['postinstall_cmd'] = ""; + $data['error_cmd'] = ""; + $data['ignore_cmd_errors'] = 0; + $data['always_update'] = 0; + $data['scope'] = "global"; + } + + $data = $this->get_stage_data($data); + + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('AddConfFile', $data); + $this->load->view('footer', $data); + + } +} diff --git a/application/controllers/confirm.php b/application/controllers/confirm.php new file mode 100644 index 0000000..2f399f5 --- /dev/null +++ b/application/controllers/confirm.php @@ -0,0 +1,175 @@ +load->helper(array('form', 'url')); + $this->load->helper('download'); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['node_id'] = intval($node_id); + $data['site_id'] = intval($site_id); + $data['stage'] = 7; + $data = $this->get_stage7_data($person, $data); + /*print "RESULT: ".$result . "
";*/ + /*print $this->validation->error_string . "
";*/ + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage7_firstcontact', $data); + $this->load->view('footer', $data); + } + + function getnode($node_id) + { + global $api, $plc; + $plc_node_list = $api->GetNodes(array('node_id' => intval($node_id) )); + return $plc_node_list[0]; + } + function getsite($site_id) + { + global $api, $plc; + $site_info = $api->GetSites($site_id, array( "name", "site_id", "login_base" ) ); + return $site_info[0]; + } + + function get_stage7_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['node'] = $this->getnode($data['node_id']); + $data['site'] = $this->getsite($data['node']['site_id']); + if( $data['node']['last_contact'] != NULL && $data['node']['last_contact'] != '' ) { + $last_contact = $data['node']['last_contact']; + } else { + $last_contact = NULL; + } + if( $last_contact != NULL ) { + $last_contact_str = timeDiff($last_contact); + } else { + $last_contact_str = "Never"; + } + $data['last_contact_str'] = $last_contact_str; + return $data; + } + + function stage8_rebootpcu($pcu_id, $site_id, $node_id) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->helper('download'); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['node_id'] = intval($node_id); + $data['site_id'] = intval($site_id); + $data['stage'] = 8; + $data = $this->get_stage8_data($person, $data); + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage8_rebootpcu', $data); + $this->load->view('footer', $data); + } + + function reboot($site_id, $pcu_id, $node_id) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->helper('download'); + $this->load->library('validation'); + $person = new Person($plc->person); + + $data = array(); + $data['site_id'] = intval($site_id); + $data['node_id'] = intval($node_id); + $data['pcu_id'] = intval($pcu_id); + $data['stage'] = 8; + $this->reboot_node($data); + $data = $this->get_stage8_data($person, $data); + + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage8_rebootpcu', $data); + $this->load->view('footer', $data); + } + + function complete($site_id, $pcu_id, $node_id) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->helper('download'); + $this->load->library('validation'); + + $data = array(); + $data['site_id'] = $site_id; + $data['stage'] = 9; + + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage9_complete', $data); + $this->load->view('footer', $data); + } + + function get_stage8_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['node'] = $this->getnode($data['node_id']); + $data['site'] = $this->getsite($data['node']['site_id']); + if( $data['node']['last_contact'] != NULL && $data['node']['last_contact'] != '' ) { + $last_contact = $data['node']['last_contact']; + } else { + $last_contact = NULL; + } + if( $last_contact != NULL ) { + $last_contact_str = timeDiff($last_contact); + } else { + $last_contact_str = "Never"; + } + $data['last_contact_str'] = $last_contact_str; + return $data; + } + + function reboot_node(&$data) + { + global $api, $plc; + $hostname = $data['node_id']; + $api->UpdateNode( $hostname, array( "boot_state" => 'rins') ); + $ret = $api->RebootNodeWithPCU( $hostname ); + if ( $ret != 0 ) { + $data['error'] = $api->error(); + } + $data['error'] = $api->error(); + } +} +?> diff --git a/application/controllers/download.php b/application/controllers/download.php new file mode 100644 index 0000000..06e1737 --- /dev/null +++ b/application/controllers/download.php @@ -0,0 +1,291 @@ +load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + + # TODO: if result is false, redirect to beginning. + /*$result = $this->validation->run();*/ + /*print "RESULT: ".$result . "
";*/ + /*print $this->validation->error_string . "
";*/ + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['node_id'] = intval($node_id); + $data['site_id'] = intval($site_id); + $data['stage'] = 5; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage5_bootimage', $data); + $this->load->view('footer', $data); + } + + function stage6_download($pcu_id=0, $site_id=0, $node_id=0) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->helper('download'); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + + $fields['action'] = "Download Action"; + $this->validation->set_fields($fields); + + $rules['action'] = "required"; + $this->validation->set_rules($rules); + + # TODO: if result is false, redirect to beginning. + $result = $this->validation->run(); + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['node_id'] = intval($node_id); + $data['site_id'] = intval($site_id); + $data['action'] = $this->validation->action; + $data['stage'] = 6; + $data['generic_iso_name'] = $this->get_bootcd_version(); + $data = $this->get_stage6_data($person, $data); + print $this->validation->error_string . "
"; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage6_download', $data); + $this->load->view('footer', $data); + } + + function getnode($node_id) + { + global $api, $plc; + $plc_node_list = $api->GetNodes(array('node_id' => intval($node_id) )); + return $plc_node_list[0]; + } + + function get_stage6_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['api'] = $api; + $data['has_primary'] = 1; + $node_detail = $this->getnode($data['node_id']); + $data['node_detail'] = $node_detail; + $node_id = $data['node_id']; + $action = $_REQUEST['action']; + switch ($action) { + + case "download-generic-iso": + case "download-generic-usb": + + if ($action=="download-generic-iso") { + $boot_action="generic-iso"; + $basename=PLC_NAME."-BootCD.iso"; + } else { + $boot_action="generic-usb"; + $basename=PLC_NAME."-BootCD.usb"; + } + + $api->UpdateNode( $hostname, array( "boot_state" => 'disable', + "version" => $this->get_bootcd_version() ) ); + /* exits on success */ + $success = $this->deliver_bootmedium($node_id, $boot_action, $basename); + if (! $success ) { + $error="Unexpected error from deliver_bootmedium - probably wrong directory modes"; + print ("
$error
\n"); + print ("

Back to node \n"); + return ; + } + break; + + case "download-node-floppy-with-iso": + case "download-node-floppy-with-usb": + case "download-node-floppy": + case "download-node-iso": + case "download-node-usb": + case "download-node-usb-partition": + + $return= $api->GetNodes( array( $node_id ) ); + $node_detail= $return[0]; + + // non-admin people need to be affiliated with the right site + if( !in_array( 10, $person->data['role_ids'] ) ) { + $node_site_id = $node_detail['site_id']; + $in_site = in_array ($node_site_id,$person->data['site_ids']); + if( ! $in_site) { + $error= "Insufficient permission. You cannot create configuration files for this node."; + } + } + + $hostname= $node_detail['hostname']; + $return= $api->GetNodeNetworks( array( "node_id" => $node_id ), NULL ); + + $can_gen_config= 1; + $data['has_primary']= 0; + + if( count($return) > 0 ) { + foreach( $return as $node_network_detail ) { + if( $node_network_detail['is_primary'] == true ) { + $data['has_primary']= 1; + break; + } + } + $data['node_network_detail'] = $node_network_detail; + } + + if( !$data['has_primary'] ) { + $can_gen_config= 0; + } else { + if( $node_detail['hostname'] == "" ) { + $can_gen_config= 0; + $node_detail['hostname']= "Missing"; + } + + $fields= array("method","ip"); + foreach( $fields as $field ) { + if( $node_network_detail[$field] == "" ) { + $can_gen_config= 0; + $node_network_detail[$field]= "Missing"; + } + } + + if( $node_network_detail['method'] == "static" ) { + $fields= array("gateway","netmask","network","broadcast","dns1"); + foreach( $fields as $field ) { + if( $node_network_detail[$field] == "" ) { + $can_gen_config= 0; + $node_network_detail[$field]= "Missing"; + } + } + } + + if( $node_network_detail['method'] != "static" && $node_network_detail['method'] != "dhcp" ) { + $can_gen_config= 0; + $node_network_detail['method']= "Unknown method"; + } + } + + if( $can_gen_config && isset($_REQUEST['download']) ) { + $download = $_REQUEST['download']; + if (method_exists($api, "GetBootMedium")) + $file_contents= $api->GetBootMedium($node_id, "node-floppy", ""); + else + $file_contents= $api->GenerateNodeConfFile($node_id, true); + + switch ($action) { + case 'download-node-floppy-with-iso': + case 'download-node-floppy-with-usb': + case 'download-node-floppy': + $boot_action='node-floppy'; + $basename = "plnode.txt"; + break; + case 'download-node-iso': + $boot_action='node-iso'; + $basename = "$hostname.iso"; + break; + case 'download-node-usb': + $boot_action='node-usb'; + $basename = "$hostname.usb"; + break; + case "download-node-usb-partition": + $boot_action='node-usb-partition'; + $basename = "$hostname-partition.usb"; + break; + } + if ($action != 'download-node-floppy') + { + $api->UpdateNode( $hostname, array( "boot_state" => 'disable', + "version" => $this->get_bootcd_version() ) ); + } + /* exits on success */ + $success = $this->deliver_bootmedium($node_id, $boot_action, $basename); + if (! $success ) { + $error="Unexpected error from deliver_bootmedium - probably wrong directory modes"; + print ("

$error
\n"); + print ("

Back to node \n"); + return ; + } + } + $action_labels = array ( + 'download-node-floppy' => 'textual node config (for floppy)' , + 'download-node-floppy-with-iso' => 'textual node config (for floppy)' , + 'download-node-floppy-with-usb' => 'textual node config (for floppy)' , + 'download-node-iso' => 'ISO image', + 'download-node-usb-partition' => 'USB image', + 'download-node-usb' => 'USB image' ); + + $format = $action_labels [ $action ] ; + /*drupal_set_title("Download boot material for $hostname");*/ + break; + + default: + echo "Unknown action $action."; + exit(); + break; + } + $data['format'] = $format; + $data['can_gen_config'] = $can_gen_config; + return $data; + } + function get_bootcd_version() + { + $BOOTCD="/usr/share/bootcd"; + $BOOTCDVERSION="$BOOTCD/build/version.txt"; + $version = trim(file_get_contents($BOOTCDVERSION)); + $bootcd_version = PLC_NAME . "-BootCD-".$version; + return $bootcd_version; + } + + function deliver_bootmedium($node_id, $action, $filename) + { + global $api; + ini_set("memory_limit","300M"); + $options = array(); + switch ($action) { + case "node-usb-partition": + $action = "node-usb"; + $options[] = "partition"; + break; + } + $b64_data = $api->GetBootMedium($node_id,$action,"", $options); + $error= $api->error(); + if ( empty($error) ) + { + if ( $action == "node-floppy" ) + { + # data comes back in plain text for node-floppy. + $data = $b64_data; + } else { + $data = base64_decode($b64_data); + } + $size = strlen($data); + /* exits on success */ + force_download($filename, $data); + } + return False; + } + +} +?> diff --git a/application/controllers/index.html b/application/controllers/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/controllers/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/controllers/register.php b/application/controllers/register.php new file mode 100644 index 0000000..f7f330d --- /dev/null +++ b/application/controllers/register.php @@ -0,0 +1,716 @@ +load->helper(array('form', 'url')); + $data=array(); + $data['stage'] = 0; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage0', $data); + $this->load->view('footer', $data); + } + function stage1_addpcu() + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + + $rules['hostname'] = "trim|required"; + $rules['ip'] = "trim|required|valid_ip"; + $rules['username'] = "trim"; + $rules['password'] = "trim|required"; + $rules['notes'] = "trim"; + $this->validation->set_rules($rules); + + $fields['hostname'] = "Hostname"; + $fields['ip'] = "IP Address"; + $fields['username'] = "Username"; + $fields['password'] = "Password"; + $this->validation->set_fields($fields); + + $person = new Person($plc->person); + $data = array(); + if ($this->validation->run() == TRUE) + { + /* b/c the submit is valid, it doesn't matter if pcu_register is set */ + $this->pcu_id = $this->add_pcu($data); + } + $data = $this->get_stage1_data($person, $data); + $data['stage'] = 1; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage1_pcu_choose', $data); + $this->load->view('footer', $data); + } + + function add_pcu(&$data) + { + global $api, $plc; + /* add pcu, get pcu info */ + $site_id = intval( $_REQUEST['site_id'] ); + $fields= array( 'protocol'=> '', + 'model'=> $_REQUEST['model'], + 'hostname'=> $this->validation->hostname, + 'ip'=> $this->validation->ip, + 'username'=> $this->validation->username, + 'password'=> $this->validation->password, + 'notes'=> $_REQUEST['notes'], ); + $pcu_id= $api->AddPCU( $site_id, $fields ); + + if( $pcu_id == 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + $data['pcu_id'] = $pcu_id; + + return $pcu_id; + } + + function get_stage1_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['default_site_list'] = $person->getSites(); + $data['pcu_types'] = $api->GetPCUTypes(NULL, array('model', 'name')); + $data['pcu_list'] = $this->getpculist($person); + $data['site_list'] = $this->getsitelist($person); + return $data; + } + + function getpculist($person) + { + global $api, $plc; + $plc_pcu_list = $api->GetPCUs(array('site_id' => $person->getSites())); + return PlcObject::constructList('PCU', $plc_pcu_list); + } + + function getsitelist($person) + { + global $api, $plc; + // get sites depending on role and sites associated. + if( $person->isAdmin() ) { + $site_info= $api->GetSites( NULL, array( "name", "site_id", "login_base" ) ); + } else { + $site_info= $api->GetSites( $person->getSites(), array( "name", "site_id", "login_base" ) ); + } + sort_sites( $site_info ); + return $site_info; + } + + var $run_update = True; + var $disp_errors = True; + function stage2_confirmpcu() + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + + if ( isset($_REQUEST['pcu_choose']) ) { + $rules['pcu_id'] = "required"; + $this->validation->set_rules($rules); + $fields['pcu_id'] = "PCU id"; + $this->validation->set_fields($fields); + + $result = $this->validation->run(); + /* I don't know, shouldn't we redirect to the first stage in this case? */ + $this->run_update = False; + $this->disp_errors = False; + print $this->validation->error_string . "
"; + + } else { + # Information update + + $rules['pcu_id'] = "required"; + $rules['hostname'] = "trim|required"; + $rules['ip'] = "trim|required|valid_ip"; + $rules['username'] = "trim"; + $rules['password'] = "trim|required"; + $rules['notes'] = "trim"; + $this->validation->set_rules($rules); + + $fields['pcu_id'] = "PCU id"; + $fields['hostname'] = "Hostname"; + $fields['ip'] = "IP Address"; + $fields['username'] = "Username"; + $fields['password'] = "Password"; + $this->validation->set_fields($fields); + + if ( $this->validation->run() == FALSE ) + { + print $this->validation->error_string . "
"; + $this->run_update = False; + } + } + $data = $this->get_stage2_data($person); + $data['stage'] = 2; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage2_pcu_confirm', $data); + $this->load->view('footer', $data); + + } + + function update_pcu($data) + { + global $api, $plc; + /* add pcu, get pcu info */ + $pcu_id = intval($this->validation->pcu_id); + $fields= array( 'protocol'=> '', + 'model'=> $_REQUEST['model'], + 'hostname'=> $this->validation->hostname, + 'ip'=> $this->validation->ip, + 'username'=> $this->validation->username, + 'password'=> $this->validation->password, + 'notes'=> $_REQUEST['notes'], ); + $ret = $api->UpdatePCU( $pcu_id, $fields ); + + if( $ret != 1 ) { + $data['error'] = $api->error(); + print $data['error']; + } + + return $pcu_id; + } + + function get_stage2_data($person) + { + global $api, $plc; + + $data = array(); + if ( $this->run_update && isset($_REQUEST['pcu_update']) ) + { + $this->update_pcu($data); + } + + if ( isset($_REQUEST['pcu_id']) ) + { + $pcu_id = intval($_REQUEST['pcu_id']); + $pcu_data = $api->GetPCUs(array('pcu_id'=>$pcu_id)); + $pcu = new PCU($pcu_data[0]); + $data['pcu'] = $pcu; + $data['pcu_id'] = $pcu_id; + $data['site_id'] = $pcu_data[0]['site_id']; + } + + $data['default_site_list'] = $person->getSites(); + $data['pcu_types'] = $api->GetPCUTypes(NULL, array('model', 'name')); + $data['pcu_site'] = $api->GetSites( $pcu_data[0]['site_id'], array( "name", "site_id", "login_base" ) ); + return $data; + } + + var $node_id = 0; + function stage3_addnode($pcu_id, $site_id) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['site_id'] = intval($site_id); + + if ( isset($_REQUEST['pcu_proceed']) ) { + $rules['hostname'] = ""; + $rules['model'] = ""; + $rules['method'] = ""; + $rules['ip'] = ""; + $rules['netmask'] = ""; + $rules['network'] = ""; + $rules['gateway'] = ""; + $rules['broadcast'] = ""; + $rules['dns1'] = ""; + $rules['dns2'] = ""; + $this->validation->set_rules($rules); + $fields['hostname'] = "Hostname"; + $fields['model'] = "Model"; + $fields['method'] = "Method"; + $fields['ip'] = "IP Address"; + $fields['netmask'] = "Netmask Address"; + $fields['network'] = "Network Address"; + $fields['gateway'] = "Gateway Address"; + $fields['broadcast'] = "Broadcast Address"; + $fields['dns1'] = "Primary DNS Address"; + $fields['dns2'] = "Secondary DNS Address"; + $this->validation->set_fields($fields); + + $result = $this->validation->run(); + /*print "RESULT: ".$result . "
";*/ + # TODO: if result is false, redirect to beginning. + $this->disp_errors = False; + print $this->validation->error_string . "
"; + } else { + $rules['hostname'] = "trim|required"; + $rules['model'] = "trim|required"; + $rules['method'] = "required"; + $rules['ip'] = "trim|required|valid_ip"; + if ( isset ($_REQUEST['method']) && $_REQUEST['method'] == 'static' ) + { + $rules['netmask'] = "trim|valid_ip"; + $rules['network'] = "trim|valid_ip"; + $rules['gateway'] = "trim|valid_ip"; + $rules['broadcast'] = "trim|valid_ip"; + $rules['dns1'] = "trim|valid_ip"; + $rules['dns2'] = "trim|valid_ip"; + } else { + $rules['netmask'] = ""; + $rules['network'] = ""; + $rules['gateway'] = ""; + $rules['broadcast'] = ""; + $rules['dns1'] = ""; + $rules['dns2'] = ""; + } + $this->validation->set_rules($rules); + + $fields['hostname'] = "Hostname"; + $fields['model'] = "Model"; + $fields['method'] = "Method"; + $fields['ip'] = "IP Address"; + $fields['netmask'] = "Netmask Address"; + $fields['network'] = "Network Address"; + $fields['gateway'] = "Gateway Address"; + $fields['broadcast'] = "Broadcast Address"; + $fields['dns1'] = "Primary DNS Address"; + $fields['dns2'] = "Secondary DNS Address"; + $this->validation->set_fields($fields); + + if ($this->validation->run() == TRUE) + { + /* b/c the submit is valid, all values are minimally consistent. */ + $this->node_id = $this->add_node($data); + } + } + $data = $this->get_stage3_data($person, $data); + $data['stage'] = 3; + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage3_node_choose', $data); + $this->load->view('footer', $data); + } + + function add_node(&$data) + { + global $api, $plc; + print "Adding Node\n
"; + $hostname = trim($_REQUEST['hostname']); + $model= trim($_REQUEST['model']); + $method = trim($_REQUEST['method']); + $ip = trim($_REQUEST['ip']); + if ( $method == 'static' ) + { + $netmask = trim($_REQUEST['netmask']); + $network = trim($_REQUEST['network']); + $gateway = trim($_REQUEST['gateway']); + $broadcast = trim($_REQUEST['broadcast']); + $dns1 = trim($_REQUEST['dns1']); + $dns2 = trim($_REQUEST['dns2']); + } + + // used to generate error strings for static fields only + $static_fields= array(); + $static_fields['netmask']= "Netmask address"; + $static_fields['network']= "Network address"; + $static_fields['gateway']= "Gateway address"; + $static_fields['broadcast']= "Broadcast address"; + $static_fields['dns1']= "Primary DNS address"; + + if( $method == 'static' ) + { + if( !is_valid_network_addr($network,$netmask) ) + { + $errors[] = "The network address does not coorespond to the netmask"; + } + } + + if( !isset($errors) || count($errors) == 0 ) + { + // add new node and its network + $optional_vals= array( "hostname"=>$hostname, "model"=>$model ); + + $site_id= $data['site_id']; + $node_id= $api->AddNode( intval( $site_id ), $optional_vals ); + if( $node_id <= 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + + // now, try to add the network. + $optional_vals= array(); + $optional_vals['is_primary']= true; + $optional_vals['ip']= $ip; + $optional_vals['type']= 'ipv4'; + $optional_vals['method']= $method; + + if( $method == 'static' ) + { + $optional_vals['gateway']= $gateway; + $optional_vals['network']= $network; + $optional_vals['broadcast']= $broadcast; + $optional_vals['netmask']= $netmask; + $optional_vals['dns1']= $dns1; + if (!empty($dns2)) { + $optional_vals['dns2']= $dns2; + } + } + + $nodenetwork_id= $api->AddNodeNetwork( $node_id, $optional_vals); + if( $nodenetwork_id <= 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + + $success = $api->AddNodeToPCU( $node_id, $data['pcu_id'], 1); + if( !isset($success) || $success <= 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + } + + + $data['node_id'] = $node_id; + if ( isset($errors) ) { $data['errors'] = $errors; } + + return $node_id; + } + + function get_stage3_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['default_site_list'] = array($data['site_id']); + $data['node_list'] = $this->getnodelist($data['site_id']); + $data['site'] = $this->getsite($data['site_id']); + return $data; + } + + function getnodelist($site_id) + { + global $api, $plc; + $plc_node_list = $api->GetNodes(array('site_id' => intval($site_id) )); + return PlcObject::constructList('Node', $plc_node_list); + } + + function getsite($site_id) + { + global $api, $plc; + $site_info = $api->GetSites($site_id, array( "name", "site_id", "login_base" ) ); + return $site_info[0]; + } + + function stage45_mappcu($pcu_id=0, $site_id=0, $node_id=0) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + + $fields['port_number'] = "Port Number"; + $this->validation->set_fields($fields); + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['site_id'] = intval($site_id); + $data['node_id'] = intval($node_id); + + if ( isset($_REQUEST['node_confirm']) ) { + /* skip the rules, since we're just displaying the page. */ + $rules['port_number'] = ""; + $this->validation->set_rules($rules); + + } else { + /* setup rules, to validate the form data. */ + $rules['port_number'] = "trim|required|intval"; + $this->validation->set_rules($rules); + + $result = $this->validation->run(); + if ( ! $result ) { + print "ERROR"; + } else { + $port = $this->validation->port_number; + /* if we do not delete the node from the PCU first, a fault is raised */ + $ret = $api->DeleteNodeFromPCU($data['node_id'], $data['pcu_id']); + $ret = $api->AddNodeToPCU($data['node_id'], $data['pcu_id'], $port); + if ( $ret != 1 ) + { + $data['error'] = $api->error(); + print $data['error']; + } + } + $this->disp_errors = False; + print $this->validation->error_string . "
"; + } + + + $data['node'] = $this->getnode($data['node_id']); + $api_pcus = $api->GetPCUs($data['node']->pcu_ids); + $pcu = $api_pcus[0]; + + # NOTE: find index of node id, then pull out that index of + $index = array_search($data['node_id'], $pcu['node_ids']); + $data['pcu_port'] = $pcu['ports'][$index]; + $data['stage'] = 4.5; + #$data = $this->get_stage4_data($person, $data); + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage45_pcuport', $data); + $this->load->view('footer', $data); + } + + function stage4_confirmnode($pcu_id=0, $site_id=0) + { + global $api, $plc; + $this->load->helper(array('form', 'url')); + $this->load->library('validation'); + + $this->validation->set_error_delimiters('', ''); + $person = new Person($plc->person); + + $fields['hostname'] = "Hostname"; + $fields['model'] = "Model"; + $fields['method'] = "Method"; + $fields['ip'] = "IP Address"; + $fields['netmask'] = "Netmask Address"; + $fields['network'] = "Network Address"; + $fields['gateway'] = "Gateway Address"; + $fields['broadcast']= "Broadcast Address"; + $fields['dns1'] = "Primary DNS Address"; + $fields['dns2'] = "Secondary DNS Address"; + $fields['node_id'] = "NODE id"; + $this->validation->set_fields($fields); + + $data = array(); + $data['pcu_id'] = intval($pcu_id); + $data['site_id'] = intval($site_id); + + if ( isset($_REQUEST['node_choose']) ) { + $rules['node_id'] = "required|intval"; + $rules['hostname'] = ""; + $rules['model'] = ""; + $rules['method'] = ""; + $rules['ip'] = ""; + $rules['netmask'] = ""; + $rules['network'] = ""; + $rules['gateway'] = ""; + $rules['broadcast'] = ""; + $rules['dns1'] = ""; + $rules['dns2'] = ""; + $this->validation->set_rules($rules); + + $result = $this->validation->run(); + /*print "RESULT: ".$result . "
";*/ + # TODO: if result is false, redirect to beginning. + $this->disp_errors = False; + print $this->validation->error_string . "
"; + } else { + $rules['hostname'] = "trim|required"; + $rules['model'] = "trim|required"; + $rules['method'] = "required"; + $rules['ip'] = "trim|required|valid_ip"; + if ( isset ($_REQUEST['method']) && $_REQUEST['method'] == 'static' ) + { + $rules['netmask'] = "trim|valid_ip"; + $rules['network'] = "trim|valid_ip"; + $rules['gateway'] = "trim|valid_ip"; + $rules['broadcast'] = "trim|valid_ip"; + $rules['dns1'] = "trim|valid_ip"; + $rules['dns2'] = "trim"; + } else { + # NOTE: There are no conditions that must be met for these fields. + $rules['netmask'] = ""; + $rules['network'] = ""; + $rules['gateway'] = ""; + $rules['broadcast'] = ""; + $rules['dns1'] = ""; + $rules['dns2'] = ""; + } + $rules['node_id'] = "required|intval"; + $this->validation->set_rules($rules); + + if ($this->validation->run() == TRUE) + { + /* b/c the submit is valid, all values are minimally consistent. */ + $this->node_id = $this->update_node($data); + } + } + $data['node_id'] = intval($this->validation->node_id); + $data['stage'] = 4; + if ( $this->checknodeid($data['node_id']) ) + { + $data = $this->get_stage4_data($person, $data); + $this->load->view('header', $data); + $this->load->view('debug', $data); + $this->load->view('stage4_node_confirm', $data); + $this->load->view('footer', $data); + } else { + print "You must select a valid Node before continuing."; + } + } + + function update_node(&$data) + { + # TODO: RECODE To update values instead of adding them... + global $api, $plc; + $hostname = trim($_REQUEST['hostname']); + $model= trim($_REQUEST['model']); + $node_id = intval($this->validation->node_id); + $ret = $api->UpdateNode( $node_id, array('hostname' => $hostname, + 'model' => $model)); + if( $ret <= 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + + $api_node_list = $api->GetNodes($node_id); + if ( count($api_node_list) > 0 ) + { + $node_obj = new Node($api_node_list[0]); + } else { + print "broken!!!"; + exit (1); + } + + + $optional_vals= array(); + + $method = trim($_REQUEST['method']); + if ( $node_obj->method != $method ) { + $optional_vals['method']= $method; + } + + $ip = trim($_REQUEST['ip']); + if ( $node_obj->ip != $ip ) { + $optional_vals['ip']= $ip; + } + + // used to generate error strings for static fields only + $static_fields= array(); + $static_fields['netmask']= "Netmask address"; + $static_fields['network']= "Network address"; + $static_fields['gateway']= "Gateway address"; + $static_fields['broadcast']= "Broadcast address"; + $static_fields['dns1']= "Primary DNS address"; + + if ( $method == 'static' ) + { + $netmask = trim($_REQUEST['netmask']); + $network = trim($_REQUEST['network']); + $gateway = trim($_REQUEST['gateway']); + $broadcast = trim($_REQUEST['broadcast']); + $dns1 = trim($_REQUEST['dns1']); + $dns2 = trim($_REQUEST['dns2']); + + if( !is_valid_network_addr($network,$netmask) ) + { + $errors[] = "The network address does not coorespond to the netmask"; + } + } + + if ( !isset($errors) || count($errors) == 0 ) + { + // now, try to add the network. + if( $method == 'static' ) + { + if ( $node_obj->gateway != $gateway ) { + $optional_vals['gateway']= $gateway; + } + if ( $node_obj->network != $network ) { + $optional_vals['network']= $network; + } + if ( $node_obj->broadcast != $broadcast ) { + $optional_vals['broadcast']= $broadcast; + } + if ( $node_obj->netmask != $netmask ) { + $optional_vals['netmask']= $netmask; + } + if ( $node_obj->dns1 != $dns1 ) { + $optional_vals['dns1']= $dns1; + } + if ( $node_obj->dns2 != $dns2 ) { + $optional_vals['dns2']= $dns2; + } + } + + if ( count($optional_vals) > 0 ) + { + print_r($optional_vals); + $ret = $api->UpdateNodeNetwork( $node_obj->nodenetwork_id, $optional_vals); + if( $ret <= 0 ) { + $data['error'] = $api->error(); + print $data['error']; + } + } + } + + $data['node_id'] = $node_id; + if ( isset($errors) ) { $data['errors'] = $errors; } + + return $node_id; + } + + function get_stage4_data($person, $data=NULL) + { + global $api, $plc; + if ( $data == NULL ){ + $data = array(); + } + $data['node'] = $this->getnode($data['node_id']); + $data['site'] = $this->getsite($data['site_id']); + /*print "SITENAME: " . $data['site']['login_base'] . "
";*/ + return $data; + } + + function checknodeid($node_id) + { + global $api, $plc; + $plc_node_list = $api->GetNodes(array('node_id' => intval($node_id) ), array('node_id')); + if ( count($plc_node_list) > 0 ) { + return True; + } else { + return False; + } + } + + function getnode($node_id) + { + global $api, $plc; + $plc_node_list = $api->GetNodes(array('node_id' => intval($node_id) )); + if ( count($plc_node_list) > 0 ) + { + return new Node($plc_node_list[0]); + } else { + return NULL; + } + } + +} +?> diff --git a/application/controllers/welcome.php b/application/controllers/welcome.php new file mode 100644 index 0000000..ec16000 --- /dev/null +++ b/application/controllers/welcome.php @@ -0,0 +1,17 @@ +load->view('welcome_message'); + } +} + +/* End of file welcome.php */ +/* Location: ./system/application/controllers/welcome.php */ \ No newline at end of file diff --git a/application/errors/error_404.php b/application/errors/error_404.php new file mode 100644 index 0000000..bfe9444 --- /dev/null +++ b/application/errors/error_404.php @@ -0,0 +1,35 @@ + + + +404 Page Not Found + + + +
+

+ +
+ + \ No newline at end of file diff --git a/application/errors/error_db.php b/application/errors/error_db.php new file mode 100644 index 0000000..1ce52df --- /dev/null +++ b/application/errors/error_db.php @@ -0,0 +1,34 @@ + + +Database Error + + + +
+

+ +
+ + \ No newline at end of file diff --git a/application/errors/error_general.php b/application/errors/error_general.php new file mode 100644 index 0000000..d861070 --- /dev/null +++ b/application/errors/error_general.php @@ -0,0 +1,34 @@ + + +Error + + + +
+

+ +
+ + \ No newline at end of file diff --git a/application/errors/error_php.php b/application/errors/error_php.php new file mode 100644 index 0000000..f085c20 --- /dev/null +++ b/application/errors/error_php.php @@ -0,0 +1,10 @@ +
+ +

A PHP Error was encountered

+ +

Severity:

+

Message:

+

Filename:

+

Line Number:

+ +
\ No newline at end of file diff --git a/application/errors/index.html b/application/errors/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/errors/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/helpers/index.html b/application/helpers/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/helpers/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/hooks/index.html b/application/hooks/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/hooks/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/index.html b/application/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/language/english/index.html b/application/language/english/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/language/english/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/libraries/index.html b/application/libraries/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/libraries/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/models/index.html b/application/models/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/models/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/views/AddConfFile.php b/application/views/AddConfFile.php new file mode 100644 index 0000000..729455f --- /dev/null +++ b/application/views/AddConfFile.php @@ -0,0 +1,166 @@ + + + +Return to listing... +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ validation->set_checkbox('enabled', $this->validation->enabled ) ?> + name="enabled">Enabled + validation->enabled_error ?> +
File Scope: +> Global
+> Node : + +
+ +> Group : +
Source:http:/// + +validation->source_error ?> +
Destination: + +validation->dest_error ?> +
Permissions: + +validation->file_permissions_error ?> +
Owner: + +validation->file_owner_error ?> +
Group: + +validation->file_group_error ?> +
Pre-Install Command: + +validation->preinstall_cmd_error ?> +
Post-Install Command: + +validation->postinstall_cmd_error ?> +
Error Command: + +validation->postinstall_cmd_error ?> +
+(run if an error occured, regardless if errors are being ignored)
+ validation->set_checkbox('ignore_cmd_errors', $this->validation->ignore_cmd_errors) ?> + name="ignore_cmd_errors">Ignore pre/post install command errors + validation->ignore_cmd_errors_error?> +
+ validation->set_checkbox('always_update', $this->validation->always_update) ?> + name="always_update">Always update this file, even if same as original + validation->always_update_error?> +
+

+ diff --git a/application/views/ListConfFiles.php b/application/views/ListConfFiles.php new file mode 100644 index 0000000..c690275 --- /dev/null +++ b/application/views/ListConfFiles.php @@ -0,0 +1,71 @@ + +

All current node configuration files

+ +

+ + + + + + + + + +GetNodes( $node_id, array("hostname") ); + + $hostname= $nodes[0]["hostname"]; + $scope= "node: $hostname"; + } elseif(count($node_id) == 0 && is_numeric($nodegroup_id[0]) ) + { + $nodegroups= $api->GetNodeGroups( $nodegroup_id ); + $group_name= $nodegroups[0]["name"]; + $scope= "group: $group_name"; + } + } + + print( "\n\n" ); + print( "\n" ); + print( "\n\n" ); + } +?> + + + +
Destination File [click to view/edit details]ScopeEnabled
" ); + print( "$dest$scope$enabled
+ +

Create new... +
Copy... +
Delete... + +


+

File scope priority

+ +The following priority is applied to files that have the same destination: +
    +
  1. Files that have a one-node scope +
  2. Files that have a group scope +
  3. Files that have a global scope +
diff --git a/application/views/UpdateConfFile.php b/application/views/UpdateConfFile.php new file mode 100644 index 0000000..9f13d16 --- /dev/null +++ b/application/views/UpdateConfFile.php @@ -0,0 +1,166 @@ + + + +Return to listing... +'fm', 'method'=>'post')) ?> + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + name="enabled">Enabled + validation->enabled_error ?> +
File Scope: +> Global
+> Node : + +
+ +> Group : +
Source:http:/// + +validation->source_error ?> +
Destination: + +validation->dest_error ?> +
Permissions: + +validation->file_permissions_error ?> +
Owner: + +validation->file_owner_error ?> +
Group: + +validation->file_group_error ?> +
Pre-Install Command: + +validation->preinstall_cmd_error ?> +
Post-Install Command: + +validation->postinstall_cmd_error ?> +
Error Command: + +validation->postinstall_cmd_error ?> +
+(run if an error occured, regardless if errors are being ignored)
+ validation->set_checkbox('ignore_cmd_errors', $ignore_cmd_errors) ?> + name="ignore_cmd_errors">Ignore pre/post install command errors + validation->ignore_cmd_errors_error?> +
+ validation->set_checkbox('always_update', $always_update) ?> + name="always_update">Always update this file, even if same as original + validation->always_update_error?> +
+ + diff --git a/application/views/debug.php b/application/views/debug.php new file mode 100644 index 0000000..723715c --- /dev/null +++ b/application/views/debug.php @@ -0,0 +1,5 @@ + diff --git a/application/views/footer.php b/application/views/footer.php new file mode 100644 index 0000000..32a5bfb --- /dev/null +++ b/application/views/footer.php @@ -0,0 +1,15 @@ + + + + +

+ + + + + + diff --git a/application/views/header.php b/application/views/header.php new file mode 100644 index 0000000..45ead67 --- /dev/null +++ b/application/views/header.php @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + '; + $out .= $this->newline; + } + + // Is there a table heading to display? + if (count($this->heading) > 0) + { + $out .= $this->template['heading_row_start']; + $out .= $this->newline; + + foreach($this->heading as $heading) + { + $out .= $this->template['heading_cell_start']; + $out .= $heading; + $out .= $this->template['heading_cell_end']; + } + + $out .= $this->template['heading_row_end']; + $out .= $this->newline; + } + + // Build the table rows + if (count($this->rows) > 0) + { + $i = 1; + foreach($this->rows as $row) + { + if ( ! is_array($row)) + { + break; + } + + // We use modulus to alternate the row colors + $name = (fmod($i++, 2)) ? '' : 'alt_'; + + $out .= $this->template['row_'.$name.'start']; + $out .= $this->newline; + + foreach($row as $cell) + { + $out .= $this->template['cell_'.$name.'start']; + + if ($cell === "") + { + $out .= $this->empty_cells; + } + else + { + $out .= $cell; + } + + $out .= $this->template['cell_'.$name.'end']; + } + + $out .= $this->template['row_'.$name.'end']; + $out .= $this->newline; + } + } + + $out .= $this->template['table_close']; + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * Clears the table arrays. Useful if multiple tables are being generated + * + * @access public + * @return void + */ + function clear() + { + $this->rows = array(); + $this->heading = array(); + $this->auto_heading = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set table data from a database result object + * + * @access public + * @param object + * @return void + */ + function _set_from_object($query) + { + if ( ! is_object($query)) + { + return FALSE; + } + + // First generate the headings from the table column names + if (count($this->heading) == 0) + { + if ( ! method_exists($query, 'list_fields')) + { + return FALSE; + } + + $this->heading = $query->list_fields(); + } + + // Next blast through the result array and build out the rows + + if ($query->num_rows() > 0) + { + foreach ($query->result_array() as $row) + { + $this->rows[] = $row; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Set table data from an array + * + * @access public + * @param array + * @return void + */ + function _set_from_array($data, $set_heading = TRUE) + { + if ( ! is_array($data) OR count($data) == 0) + { + return FALSE; + } + + $i = 0; + foreach ($data as $row) + { + if ( ! is_array($row)) + { + $this->rows[] = $data; + break; + } + + // If a heading hasn't already been set we'll use the first row of the array as the heading + if ($i == 0 AND count($data) > 1 AND count($this->heading) == 0 AND $set_heading == TRUE) + { + $this->heading = $row; + } + else + { + $this->rows[] = $row; + } + + $i++; + } + } + + // -------------------------------------------------------------------- + + /** + * Compile Template + * + * @access private + * @return void + */ + function _compile_template() + { + if ($this->template == NULL) + { + $this->template = $this->_default_template(); + return; + } + + $this->temp = $this->_default_template(); + foreach (array('table_open','heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val) + { + if ( ! isset($this->template[$val])) + { + $this->template[$val] = $this->temp[$val]; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Default Template + * + * @access private + * @return void + */ + function _default_template() + { + return array ( + 'table_open' => '
+

Stage of 9

+
diff --git a/application/views/index.html b/application/views/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/application/views/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/application/views/stage0.php b/application/views/stage0.php new file mode 100644 index 0000000..4d37540 --- /dev/null +++ b/application/views/stage0.php @@ -0,0 +1,27 @@ +
+
+ +

What to Expect

+ +This wizard helps walk you through the sequence of steps needed to register, +configure, and verify a node as part of the PlanetLab network. + +There are ten stages, which correspond to the following sections: + +
    +
  1. Register and Confirm PCU Information
  2. +
  3. Register and Confirm Node Information
  4. +
  5. Map PCU to Node
  6. +
  7. Select and Download your Node's BootImage
  8. +
  9. Verify that Node Boots and Contacts PLC
  10. +
  11. Verify that the PCU can Control your Node
  12. +
  13. Complete!
  14. +
+ +
+ +
+ + +
+
diff --git a/application/views/stage1_pcu_choose.php b/application/views/stage1_pcu_choose.php new file mode 100644 index 0000000..ff4845c --- /dev/null +++ b/application/views/stage1_pcu_choose.php @@ -0,0 +1,121 @@ +
+ + + + + + + + + + + + +

Choose an Existing PCU

PCU Name: + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Or, Register a New PCU

Site: + + +
Model: + +
Hostname: + + validation->hostname_error ?> +
IP Address: + + validation->ip_error ?> +
Username: + + validation->username_error ?> +
Password: + + validation->password_error ?> +
Notes:
+ +
+ +
+ + + + + + + + +

PCU Added Successfully!!!!

+ +

+ Back to Home Site diff --git a/application/views/stage2_pcu_confirm.php b/application/views/stage2_pcu_confirm.php new file mode 100644 index 0000000..886096a --- /dev/null +++ b/application/views/stage2_pcu_confirm.php @@ -0,0 +1,79 @@ +

Confirm PCU Information

+
+ +
+If the information below is correct, then Proceed, otherwise Update the values +below. +
+ 'display: inline; margin: 0px')) ?> + +
+ +
+ +
+
+
+
+ 'display: inline; margin: 0px')) ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Site:
Model: + +
Hostname: + '> + disp_errors ? $this->validation->hostname_error : "" ) ?> +
IP Address: '> + disp_errors ? $this->validation->ip_error : "" ) ?> +
Username: '> + disp_errors ? $this->validation->username_error : "" ) ?> +
Password: + '> + disp_errors ? $this->validation->password_error : "" ) ?> +
Notes:
+ + + + +
+
+

+ Back to Home Site diff --git a/application/views/stage3_node_choose.php b/application/views/stage3_node_choose.php new file mode 100644 index 0000000..0330f39 --- /dev/null +++ b/application/views/stage3_node_choose.php @@ -0,0 +1,170 @@ + 0 ) +{ + print( "

The following errors occured:" ); + print( "

    \n" ); + foreach( $errors as $err ) + { + print( "
  • $err\n" ); + } + print( "
\n" ); +} +?> + +
+ + + + + + + + + + + + + +

Choose a Node to Associate with PCU

Node Name: + + +
+ +
+ +
+
+
+ + + +'fm', 'method'=>'post')) ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Or, Add a Node

Site:
Hostname: + + disp_errors ? $this->validation->hostname_error : "") ?> +
Model: + + + disp_errors ? $this->validation->model_error : "") ?> +
Network Settings
Addressing Method + validation->method == 'dhcp' ? "checked" : "" ) ?>>DHCP + validation->method == 'static' ? "checked" : "" ) ?>>Static +
IP Address + + disp_errors ? $this->validation->ip_error : "") ?> +
Netmask + + disp_errors ? $this->validation->netmask_error : "") ?> +
Network address + + disp_errors ? $this->validation->network_error : "") ?> +
Gateway Address + + disp_errors ? $this->validation->gateway_error : "") ?> +
Broadcast address + + disp_errors ? $this->validation->broadcast_error : "") ?> +
Primary DNS + + disp_errors ? $this->validation->dns1_error : "") ?> +
Secondary DNS + + disp_errors ? $this->validation->dns2_error : "") ?> + (optional) +
+ + + +
+ + + + + + + + + + +

Node Added Successfully!!!!

+ +
+

+ Back to Home Site + + + diff --git a/application/views/stage45_pcuport.php b/application/views/stage45_pcuport.php new file mode 100644 index 0000000..e076a59 --- /dev/null +++ b/application/views/stage45_pcuport.php @@ -0,0 +1,68 @@ + 0 ) +{ + print( "

The following errors occured:" ); + print( "

    \n" ); + foreach( $errors as $err ) + { + print( "
  • $err\n" ); + } + print( "
\n" ); +} +?> +
+

Confirm PCU Port to Node Mapping

+Please confirm that the node is associated with the correct port on the PCU. +Most newer models have this function built-in, where as older models allow for +multiple 'ports'. Port 1 is appropriate for built-in models. +'post')) ?> +
+ + + + +
+ +
+
+
+'fm', 'method'=>'post')) ?> + + + + + + + + + + + + + +
+
Hostname : PCUPort + hostname ?> :
PCU Port: + + +
+ + + + +
+
+

+ Back to Home Site + + diff --git a/application/views/stage4_node_confirm.php b/application/views/stage4_node_confirm.php new file mode 100644 index 0000000..dbb4a78 --- /dev/null +++ b/application/views/stage4_node_confirm.php @@ -0,0 +1,153 @@ + 0 ) +{ + print( "

The following errors occured:" ); + print( "

    \n" ); + foreach( $errors as $err ) + { + print( "
  • $err\n" ); + } + print( "
\n" ); +} +?> + +
+

Confirm Node Information

+'post')) ?> + + + + + + + + +
+Please review the information below, and if it is correct, the Proceed to then +next stage. Otherwise, please Update the information as appropriate. +
+
+ + + + +
+
+ +
+
+
+'fm', 'method'=>'post')) ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Site:
Hostname: + + disp_errors ? $this->validation->hostname_error : "") ?> +
Model: + + disp_errors ? $this->validation->model_error : "") ?> +
Addressing Method + method == 'dhcp' ? "checked" : "" ) ?>>DHCP + method == 'static' ? "checked" : "" ) ?>>Static +
IP Address + disp_errors ? $this->validation->ip_error : "") ?> +
Netmask + disp_errors ? $this->validation->netmask_error : "") ?> +
Network address + disp_errors ? $this->validation->network_error : "") ?> +
Gateway Address + disp_errors ? $this->validation->gateway_error : "") ?> +
Broadcast address + disp_errors ? $this->validation->broadcast_error : "") ?> +
Primary DNS + disp_errors ? $this->validation->dns1_error : "") ?> +
Secondary DNS + disp_errors ? $this->validation->dns2_error : "") ?> + (optional) +
+ + + + +
+
+

+ Back to Home Site + + + diff --git a/application/views/stage5_bootimage.php b/application/views/stage5_bootimage.php new file mode 100644 index 0000000..919e458 --- /dev/null +++ b/application/views/stage5_bootimage.php @@ -0,0 +1,47 @@ + +

Choose a Boot Image

+
+
+ 'fm', 'style' => 'display: inline; margin: 0px')) ?> + +
+

Pick one of the following options. Then, download the files needed for that option.

+ + + + + + + + + + + + + + + + + + + + + + +
All-In-One ISO image. + Includes plnode.txt. No additional formatting required.
All-In-One USB image. Includes plnode.txt, and filesystem only. Requires additional USB stick formatting.**
All-In-One partitioned, USB image. Includes, plnode.txt, MBR, partition table, and filesystem. No additional formatting required.**
Generic CD, and plnode.txt on Floppy
Generic USB, and plnode.txt on Floppy
+ +

Additional directions are provided on the next page.

+

NOTE: ** USB images are not guaranteed to work on all systems.

+
+
+ +
+ +
diff --git a/application/views/stage6_download.php b/application/views/stage6_download.php new file mode 100644 index 0000000..f11c0aa --- /dev/null +++ b/application/views/stage6_download.php @@ -0,0 +1,63 @@ + + +
+
+ +

Download BootImage and Follow Steps Below

+ +GetBootMedium($node_id,"node-preview",""); ?> +

Current node configuration contents

+
+
+ endif; +*/ +?> + +
+
Step 1
+ +
Download the Generic ISO image. Then, + use the 'Download' button below to get the configuration file for your + floppy disk.
+ +
Download the Generic USB image. Then, + use the 'Download' button below to get the configuration file for your + floppy disk.
+ +
Download your BootImage using 'Download' button below.
+ + +
Step 2
Burn or copy the BootImage to the appropriate read-only media
+
Step 3
Install the BootImage in your machine. Turn the machine on and allow it to boot.
+
Step 4
Continue to the next Stage.
+
+ + +

WARNING: By using the download button below, we will generate +a new node key for your machine. Therefore, all previous configuration files or All-in-one BootImages that you have downloaded will be expired. Only continue if you will complete the installation process.

+ +

+ 'display: inline; margin: 0px')) ?> + + + + + 'fm', 'style' => 'display: inline; margin: 0px')) ?> + + +

+
+Configuration file cannot be created. + diff --git a/application/views/stage7_firstcontact.php b/application/views/stage7_firstcontact.php new file mode 100644 index 0000000..540faf6 --- /dev/null +++ b/application/views/stage7_firstcontact.php @@ -0,0 +1,55 @@ +
+
+

Confirm Node Boot and PLC Contact

+ + + + + + + + + + + + + + + + +
Site:
Hostname:
Boot State:
Model:
Version:
Last Contact: + + Once your machine has booted and contacted PLC, + this field will update and you will be able to 'Continue' to the next + stage. + +
+ 'Reload' to refresh this page. + + Success!! Your machine has booted and contacted PLC.
+ 'Continue' and test the remote reset function. + +
 
+ + 'display: inline; margin: 0px')) ?> + + + 'display: inline; margin: 0px')) ?> + + + + + + + +

+
+
+

+

Trouble Shooting:

+
    +
  • If you have booted the machine, but the Last Contact field has +not updated, then there is very likely a local, network configuration problem.
    +We recommend verifying that the machine's network settings are correct and operational. You can achieve this using the site_admin account on the BootCD to manually diagnose the problem.
  • +
+

diff --git a/application/views/stage8_rebootpcu.php b/application/views/stage8_rebootpcu.php new file mode 100644 index 0000000..6fafd21 --- /dev/null +++ b/application/views/stage8_rebootpcu.php @@ -0,0 +1,72 @@ +
+
+

Confirm Reboot Node With PCU

+ + + + + + + + + + + + + + + + +
Site:
Hostname:
Boot State: + + Begin by trying the 'Test Reboot' button. + This will set the boot state to 'rins' and attempt to reboot your machine + using the registered PCU. + + If successful, your machine will reboot, install the bootstrap image, and + contacted PLC. Finally, the boot state will be updated to 'boot'. + +
+ 'Test Reboot' to try again.
+ 'Reload' to Reload this page. + + Success!! Your machine has booted and contacted PLC. + 'Continue' to the final stage. + +
Model:
Version:
Last Contact:
+ +
+ + 'display: inline; margin: 0px')) ?> + + + 'display: inline; margin: 0px')) ?> + + + 'display: inline; margin: 0px')) ?> + + + + + + + +

+
+
+ +

Tips:

+
    +
  • + If you do not see an error, this does not mean that none have + occurred. We need your help in verifying that the PCU function is + configured correctly. +
  • +
  • + Please be patient. Installations may take between 10 to 30 + minutes depending on the speed of your hardware and network. Checking the + console is the fastest way to verify that the installation is proceeding. +
  • +
diff --git a/application/views/stage9_complete.php b/application/views/stage9_complete.php new file mode 100644 index 0000000..e0f82a0 --- /dev/null +++ b/application/views/stage9_complete.php @@ -0,0 +1,18 @@ +
+
+

Congratulations!!! You are done.

+

Your machine is configured correctly, online, and fully running.
+

We sincerely thank you for the effort you have invested in setting up your +machines. With your contribution PlanetLab will have a more reliable and growing +infrastructure to build upon. +

Thank you. +
+

If you have any questions, or suggestions for how to improve +this process, please send us a hint at PlanetLab Suggestions +
+ +
+

+
+Back to Home Site diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php new file mode 100644 index 0000000..8313059 --- /dev/null +++ b/application/views/welcome_message.php @@ -0,0 +1,62 @@ + + +Welcome to CodeIgniter + + + + + +

Welcome to CodeIgniter!

+ +

The page you are looking at is being generated dynamically by CodeIgniter.

+ +

If you would like to edit this page you'll find it located at:

+system/application/views/welcome_message.php + +

The corresponding controller for this page is found at:

+system/application/controllers/welcome.php + +

If you are exploring CodeIgniter for the very first time, you should start by reading the User Guide.

+ + +


Page rendered in {elapsed_time} seconds

+ + + \ No newline at end of file diff --git a/cache/index.html b/cache/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/cache/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/codeigniter/Base4.php b/codeigniter/Base4.php new file mode 100644 index 0000000..b242c00 --- /dev/null +++ b/codeigniter/Base4.php @@ -0,0 +1,69 @@ +load->library('email') to instantiate + * classes that can then be used within controllers as $this->email->send() + * + * PHP 4 also has trouble referencing the CI super object within application + * constructors since objects do not exist until the class is fully + * instantiated. Basically PHP 4 sucks... + * + * Since PHP 5 doesn't suffer from this problem so we load one of + * two files based on the version of PHP being run. + * + * @package CodeIgniter + * @subpackage codeigniter + * @category front-controller + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/ + */ + class CI_Base extends CI_Loader { + + function CI_Base() + { + // This allows syntax like $this->load->foo() to work + parent::CI_Loader(); + $this->load =& $this; + + // This allows resources used within controller constructors to work + global $OBJ; + $OBJ = $this->load; // Do NOT use a reference. + } +} + +function &get_instance() +{ + global $CI, $OBJ; + + if (is_object($CI)) + { + return $CI; + } + + return $OBJ->load; +} + + +/* End of file Base4.php */ +/* Location: ./system/codeigniter/Base4.php */ \ No newline at end of file diff --git a/codeigniter/Base5.php b/codeigniter/Base5.php new file mode 100644 index 0000000..8dd3ad0 --- /dev/null +++ b/codeigniter/Base5.php @@ -0,0 +1,56 @@ +mark('total_execution_time_start'); +$BM->mark('loading_time_base_classes_start'); + +/* + * ------------------------------------------------------ + * Instantiate the hooks class + * ------------------------------------------------------ + */ + +$EXT =& load_class('Hooks'); + +/* + * ------------------------------------------------------ + * Is there a "pre_system" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('pre_system'); + +/* + * ------------------------------------------------------ + * Instantiate the base classes + * ------------------------------------------------------ + */ + +$CFG =& load_class('Config'); +$URI =& load_class('URI'); +$RTR =& load_class('Router'); +$OUT =& load_class('Output'); + +/* + * ------------------------------------------------------ + * Is there a valid cache file? If so, we're done... + * ------------------------------------------------------ + */ + +if ($EXT->_call_hook('cache_override') === FALSE) +{ + if ($OUT->_display_cache($CFG, $URI) == TRUE) + { + exit; + } +} + +/* + * ------------------------------------------------------ + * Load the remaining base classes + * ------------------------------------------------------ + */ + +$IN =& load_class('Input'); +$LANG =& load_class('Language'); + +/* + * ------------------------------------------------------ + * Load the app controller and local controller + * ------------------------------------------------------ + * + * Note: Due to the poor object handling in PHP 4 we'll + * conditionally load different versions of the base + * class. Retaining PHP 4 compatibility requires a bit of a hack. + * + * Note: The Loader class needs to be included first + * + */ +if (floor(phpversion()) < 5) +{ + load_class('Loader', FALSE); + require(BASEPATH.'codeigniter/Base4'.EXT); +} +else +{ + require(BASEPATH.'codeigniter/Base5'.EXT); +} + +// Load the base controller class +load_class('Controller', FALSE); + +// Load the local application controller +// Note: The Router class automatically validates the controller path. If this include fails it +// means that the default controller in the Routes.php file is not resolving to something valid. +if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT)) +{ + show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); +} + +include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT); + +// Set a mark point for benchmarking +$BM->mark('loading_time_base_classes_end'); + + +/* + * ------------------------------------------------------ + * Security check + * ------------------------------------------------------ + * + * None of the functions in the app controller or the + * loader class can be called via the URI, nor can + * controller functions that begin with an underscore + */ +$class = $RTR->fetch_class(); +$method = $RTR->fetch_method(); + +if ( ! class_exists($class) + OR $method == 'controller' + OR strncmp($method, '_', 1) == 0 + OR in_array(strtolower($method), array_map('strtolower', get_class_methods('Controller'))) + ) +{ + show_404("{$class}/{$method}"); +} + +/* + * ------------------------------------------------------ + * Is there a "pre_controller" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('pre_controller'); + +/* + * ------------------------------------------------------ + * Instantiate the controller and call requested method + * ------------------------------------------------------ + */ + +// Mark a start point so we can benchmark the controller +$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); + +$CI = new $class(); + +// Is this a scaffolding request? +if ($RTR->scaffolding_request === TRUE) +{ + if ($EXT->_call_hook('scaffolding_override') === FALSE) + { + $CI->_ci_scaffolding(); + } +} +else +{ + /* + * ------------------------------------------------------ + * Is there a "post_controller_constructor" hook? + * ------------------------------------------------------ + */ + $EXT->_call_hook('post_controller_constructor'); + + // Is there a "remap" function? + if (method_exists($CI, '_remap')) + { + $CI->_remap($method); + } + else + { + // is_callable() returns TRUE on some versions of PHP 5 for private and protected + // methods, so we'll use this workaround for consistent behavior + if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) + { + show_404("{$class}/{$method}"); + } + + // Call the requested method. + // Any URI segments present (besides the class/function) will be passed to the method for convenience + call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); + } +} + +// Mark a benchmark end point +$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); + +/* + * ------------------------------------------------------ + * Is there a "post_controller" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('post_controller'); + +/* + * ------------------------------------------------------ + * Send the final rendered output to the browser + * ------------------------------------------------------ + */ + +if ($EXT->_call_hook('display_override') === FALSE) +{ + $OUT->_display(); +} + +/* + * ------------------------------------------------------ + * Is there a "post_system" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('post_system'); + +/* + * ------------------------------------------------------ + * Close the DB connection if one exists + * ------------------------------------------------------ + */ +if (class_exists('CI_DB') AND isset($CI->db)) +{ + $CI->db->close(); +} + + +/* End of file CodeIgniter.php */ +/* Location: ./system/codeigniter/CodeIgniter.php */ \ No newline at end of file diff --git a/codeigniter/Common.php b/codeigniter/Common.php new file mode 100644 index 0000000..9212f07 --- /dev/null +++ b/codeigniter/Common.php @@ -0,0 +1,307 @@ +show_error('An Error Was Encountered', $message); + exit; +} + + +/** +* 404 Page Handler +* +* This function is similar to the show_error() function above +* However, instead of the standard error template it displays +* 404 errors. +* +* @access public +* @return void +*/ +function show_404($page = '') +{ + $error =& load_class('Exceptions'); + $error->show_404($page); + exit; +} + + +/** +* Error Logging Interface +* +* We use this as a simple mechanism to access the logging +* class and send messages to be logged. +* +* @access public +* @return void +*/ +function log_message($level = 'error', $message, $php_error = FALSE) +{ + static $LOG; + + $config =& get_config(); + if ($config['log_threshold'] == 0) + { + return; + } + + $LOG =& load_class('Log'); + $LOG->write_log($level, $message, $php_error); +} + +/** +* Exception Handler +* +* This is the custom exception handler that is declaired at the top +* of Codeigniter.php. The main reason we use this is permit +* PHP errors to be logged in our own log files since we may +* not have access to server logs. Since this function +* effectively intercepts PHP errors, however, we also need +* to display errors based on the current error_reporting level. +* We do that with the use of a PHP error template. +* +* @access private +* @return void +*/ +function _exception_handler($severity, $message, $filepath, $line) +{ + // We don't bother with "strict" notices since they will fill up + // the log file with information that isn't normally very + // helpful. For example, if you are running PHP 5 and you + // use version 4 style class functions (without prefixes + // like "public", "private", etc.) you'll get notices telling + // you that these have been deprecated. + + if ($severity == E_STRICT) + { + return; + } + + $error =& load_class('Exceptions'); + + // Should we display the error? + // We'll get the current error_reporting level and add its bits + // with the severity bits to find out. + + if (($severity & error_reporting()) == $severity) + { + $error->show_php_error($severity, $message, $filepath, $line); + } + + // Should we log the error? No? We're done... + $config =& get_config(); + if ($config['log_threshold'] == 0) + { + return; + } + + $error->log_exception($severity, $message, $filepath, $line); +} + + + +/* End of file Common.php */ +/* Location: ./system/codeigniter/Common.php */ \ No newline at end of file diff --git a/codeigniter/Compat.php b/codeigniter/Compat.php new file mode 100644 index 0000000..291f01d --- /dev/null +++ b/codeigniter/Compat.php @@ -0,0 +1,93 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/DB.php b/database/DB.php new file mode 100644 index 0000000..2b3ff3d --- /dev/null +++ b/database/DB.php @@ -0,0 +1,146 @@ + $dns['scheme'], + 'hostname' => (isset($dns['host'])) ? rawurldecode($dns['host']) : '', + 'username' => (isset($dns['user'])) ? rawurldecode($dns['user']) : '', + 'password' => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '', + 'database' => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : '' + ); + + // were additional config items set? + if (isset($dns['query'])) + { + parse_str($dns['query'], $extra); + + foreach($extra as $key => $val) + { + // booleans please + if (strtoupper($val) == "TRUE") + { + $val = TRUE; + } + elseif (strtoupper($val) == "FALSE") + { + $val = FALSE; + } + + $params[$key] = $val; + } + } + } + + // No DB specified yet? Beat them senseless... + if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '') + { + show_error('You have not selected a database type to connect to.'); + } + + // Load the DB classes. Note: Since the active record class is optional + // we need to dynamically create a class that extends proper parent class + // based on whether we're using the active record class or not. + // Kudos to Paul for discovering this clever use of eval() + + if ($active_record_override == TRUE) + { + $active_record = TRUE; + } + + require_once(BASEPATH.'database/DB_driver'.EXT); + + if ( ! isset($active_record) OR $active_record == TRUE) + { + require_once(BASEPATH.'database/DB_active_rec'.EXT); + + if ( ! class_exists('CI_DB')) + { + eval('class CI_DB extends CI_DB_active_record { }'); + } + } + else + { + if ( ! class_exists('CI_DB')) + { + eval('class CI_DB extends CI_DB_driver { }'); + } + } + + require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver'.EXT); + + // Instantiate the DB adapter + $driver = 'CI_DB_'.$params['dbdriver'].'_driver'; + $DB =& new $driver($params); + + if ($DB->autoinit == TRUE) + { + $DB->initialize(); + } + + return $DB; +} + + + +/* End of file DB.php */ +/* Location: ./system/database/DB.php */ \ No newline at end of file diff --git a/database/DB_active_rec.php b/database/DB_active_rec.php new file mode 100644 index 0000000..e379825 --- /dev/null +++ b/database/DB_active_rec.php @@ -0,0 +1,1770 @@ +_protect_identifiers = $escape; + } + + if (is_string($select)) + { + $select = explode(',', $select); + } + + foreach ($select as $val) + { + $val = trim($val); + + if ($val != '') + { + $this->ar_select[] = $val; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_select[] = $val; + $this->ar_cache_exists[] = 'select'; + } + } + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Select Max + * + * Generates a SELECT MAX(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_max($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MAX'); + } + + // -------------------------------------------------------------------- + + /** + * Select Min + * + * Generates a SELECT MIN(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_min($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MIN'); + } + + // -------------------------------------------------------------------- + + /** + * Select Average + * + * Generates a SELECT AVG(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_avg($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'AVG'); + } + + // -------------------------------------------------------------------- + + /** + * Select Sum + * + * Generates a SELECT SUM(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_sum($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'SUM'); + } + + // -------------------------------------------------------------------- + + /** + * Processing Function for the four functions above: + * + * select_max() + * select_min() + * select_avg() + * select_sum() + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') + { + if ( ! is_string($select) OR $select == '') + { + $this->display_error('db_invalid_query'); + } + + $type = strtoupper($type); + + if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM'))) + { + show_error('Invalid function type: '.$type); + } + + if ($alias == '') + { + $alias = $this->_create_alias_from_table(trim($select)); + } + + $sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$alias; + + $this->ar_select[] = $sql; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_select[] = $sql; + $this->ar_cache_exists[] = 'select'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Determines the alias name based on the table + * + * @access private + * @param string + * @return string + */ + function _create_alias_from_table($item) + { + if (strpos($item, '.') !== FALSE) + { + return end(explode('.', $item)); + } + + return $item; + } + + // -------------------------------------------------------------------- + + /** + * DISTINCT + * + * Sets a flag which tells the query string compiler to add DISTINCT + * + * @access public + * @param bool + * @return object + */ + function distinct($val = TRUE) + { + $this->ar_distinct = (is_bool($val)) ? $val : TRUE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * From + * + * Generates the FROM portion of the query + * + * @access public + * @param mixed can be a string or array + * @return object + */ + function from($from) + { + foreach ((array)$from as $val) + { + // Extract any aliases that might exist. We use this information + // in the _protect_identifiers to know whether to add a table prefix + $this->_track_aliases($val); + + $this->ar_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); + $this->ar_cache_exists[] = 'from'; + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Join + * + * Generates the JOIN portion of the query + * + * @access public + * @param string + * @param string the join condition + * @param string the type of join + * @return object + */ + function join($table, $cond, $type = '') + { + if ($type != '') + { + $type = strtoupper(trim($type)); + + if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'))) + { + $type = ''; + } + else + { + $type .= ' '; + } + } + + // Extract any aliases that might exist. We use this information + // in the _protect_identifiers to know whether to add a table prefix + $this->_track_aliases($table); + + // Strip apart the condition and protect the identifiers + if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match)) + { + $match[1] = $this->_protect_identifiers($match[1]); + $match[3] = $this->_protect_identifiers($match[3]); + + $cond = $match[1].$match[2].$match[3]; + } + + // Assemble the JOIN statement + $join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond; + + $this->ar_join[] = $join; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_join[] = $join; + $this->ar_cache_exists[] = 'join'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Where + * + * Generates the WHERE portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function where($key, $value = NULL, $escape = TRUE) + { + return $this->_where($key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR Where + * + * Generates the WHERE portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_where($key, $value = NULL, $escape = TRUE) + { + return $this->_where($key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * orwhere() is an alias of or_where() + * this function is here for backwards compatibility, as + * orwhere() has been deprecated + */ + function orwhere($key, $value = NULL, $escape = TRUE) + { + return $this->or_where($key, $value, $escape); + } + + // -------------------------------------------------------------------- + + /** + * Where + * + * Called by where() or orwhere() + * + * @access private + * @param mixed + * @param mixed + * @param string + * @return object + */ + function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) + { + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + // If the escape value was not set will will base it on the global setting + if ( ! is_bool($escape)) + { + $escape = $this->_protect_identifiers; + } + + foreach ($key as $k => $v) + { + $prefix = (count($this->ar_where) == 0 AND count($this->ar_cache_where) == 0) ? '' : $type; + + if (is_null($v) && ! $this->_has_operator($k)) + { + // value appears not to have been set, assign the test to IS NULL + $k .= ' IS NULL'; + } + + if ( ! is_null($v)) + { + if ($escape === TRUE) + { + $k = $this->_protect_identifiers($k, FALSE, $escape); + + $v = ' '.$this->escape($v); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' ='; + } + } + else + { + $k = $this->_protect_identifiers($k, FALSE, $escape); + } + + $this->ar_where[] = $prefix.$k.$v; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_where[] = $prefix.$k.$v; + $this->ar_cache_exists[] = 'where'; + } + + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Where_in + * + * Generates a WHERE field IN ('item', 'item') SQL query joined with + * AND if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function where_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values); + } + + // -------------------------------------------------------------------- + + /** + * Where_in_or + * + * Generates a WHERE field IN ('item', 'item') SQL query joined with + * OR if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function or_where_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, FALSE, 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Where_not_in + * + * Generates a WHERE field NOT IN ('item', 'item') SQL query joined + * with AND if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function where_not_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Where_not_in_or + * + * Generates a WHERE field NOT IN ('item', 'item') SQL query joined + * with OR if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function or_where_not_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, TRUE, 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Where_in + * + * Called by where_in, where_in_or, where_not_in, where_not_in_or + * + * @access public + * @param string The field to search + * @param array The values searched on + * @param boolean If the statement would be IN or NOT IN + * @param string + * @return object + */ + function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') + { + if ($key === NULL OR $values === NULL) + { + return; + } + + if ( ! is_array($values)) + { + $values = array($values); + } + + $not = ($not) ? ' NOT' : ''; + + foreach ($values as $value) + { + $this->ar_wherein[] = $this->escape($value); + } + + $prefix = (count($this->ar_where) == 0) ? '' : $type; + + $where_in = $prefix . $this->_protect_identifiers($key) . $not . " IN (" . implode(", ", $this->ar_wherein) . ") "; + + $this->ar_where[] = $where_in; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_where[] = $where_in; + $this->ar_cache_exists[] = 'where'; + } + + // reset the array for multiple calls + $this->ar_wherein = array(); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Like + * + * Generates a %LIKE% portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'AND ', $side); + } + + // -------------------------------------------------------------------- + + /** + * Not Like + * + * Generates a NOT LIKE portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function not_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'AND ', $side, 'NOT'); + } + + // -------------------------------------------------------------------- + + /** + * OR Like + * + * Generates a %LIKE% portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'OR ', $side); + } + + // -------------------------------------------------------------------- + + /** + * OR Not Like + * + * Generates a NOT LIKE portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_not_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'OR ', $side, 'NOT'); + } + + // -------------------------------------------------------------------- + + /** + * orlike() is an alias of or_like() + * this function is here for backwards compatibility, as + * orlike() has been deprecated + */ + function orlike($field, $match = '', $side = 'both') + { + return $this->or_like($field, $match, $side); + } + + // -------------------------------------------------------------------- + + /** + * Like + * + * Called by like() or orlike() + * + * @access private + * @param mixed + * @param mixed + * @param string + * @return object + */ + function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') + { + if ( ! is_array($field)) + { + $field = array($field => $match); + } + + foreach ($field as $k => $v) + { + $k = $this->_protect_identifiers($k); + + $prefix = (count($this->ar_like) == 0) ? '' : $type; + + $v = $this->escape_str($v); + + if ($side == 'before') + { + $like_statement = $prefix." $k $not LIKE '%{$v}'"; + } + elseif ($side == 'after') + { + $like_statement = $prefix." $k $not LIKE '{$v}%'"; + } + else + { + $like_statement = $prefix." $k $not LIKE '%{$v}%'"; + } + + $this->ar_like[] = $like_statement; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_like[] = $like_statement; + $this->ar_cache_exists[] = 'like'; + } + + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * GROUP BY + * + * @access public + * @param string + * @return object + */ + function group_by($by) + { + if (is_string($by)) + { + $by = explode(',', $by); + } + + foreach ($by as $val) + { + $val = trim($val); + + if ($val != '') + { + $this->ar_groupby[] = $this->_protect_identifiers($val); + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_groupby[] = $this->_protect_identifiers($val); + $this->ar_cache_exists[] = 'groupby'; + } + } + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * groupby() is an alias of group_by() + * this function is here for backwards compatibility, as + * groupby() has been deprecated + */ + function groupby($by) + { + return $this->group_by($by); + } + + // -------------------------------------------------------------------- + + /** + * Sets the HAVING value + * + * Separates multiple calls with AND + * + * @access public + * @param string + * @param string + * @return object + */ + function having($key, $value = '', $escape = TRUE) + { + return $this->_having($key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * orhaving() is an alias of or_having() + * this function is here for backwards compatibility, as + * orhaving() has been deprecated + */ + + function orhaving($key, $value = '', $escape = TRUE) + { + return $this->or_having($key, $value, $escape); + } + // -------------------------------------------------------------------- + + /** + * Sets the OR HAVING value + * + * Separates multiple calls with OR + * + * @access public + * @param string + * @param string + * @return object + */ + function or_having($key, $value = '', $escape = TRUE) + { + return $this->_having($key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * Sets the HAVING values + * + * Called by having() or or_having() + * + * @access private + * @param string + * @param string + * @return object + */ + function _having($key, $value = '', $type = 'AND ', $escape = TRUE) + { + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + foreach ($key as $k => $v) + { + $prefix = (count($this->ar_having) == 0) ? '' : $type; + + if ($escape === TRUE) + { + $k = $this->_protect_identifiers($k); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' = '; + } + + if ($v != '') + { + $v = ' '.$this->escape_str($v); + } + + $this->ar_having[] = $prefix.$k.$v; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_having[] = $prefix.$k.$v; + $this->ar_cache_exists[] = 'having'; + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Sets the ORDER BY value + * + * @access public + * @param string + * @param string direction: asc or desc + * @return object + */ + function order_by($orderby, $direction = '') + { + if (strtolower($direction) == 'random') + { + $orderby = ''; // Random results want or don't need a field name + $direction = $this->_random_keyword; + } + elseif (trim($direction) != '') + { + $direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' '.$direction : ' ASC'; + } + + $orderby_statement = $this->_protect_identifiers($orderby).$direction; + + $this->ar_orderby[] = $orderby_statement; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_orderby[] = $orderby_statement; + $this->ar_cache_exists[] = 'orderby'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * orderby() is an alias of order_by() + * this function is here for backwards compatibility, as + * orderby() has been deprecated + */ + function orderby($orderby, $direction = '') + { + return $this->order_by($orderby, $direction); + } + + // -------------------------------------------------------------------- + + /** + * Sets the LIMIT value + * + * @access public + * @param integer the limit value + * @param integer the offset value + * @return object + */ + function limit($value, $offset = '') + { + $this->ar_limit = $value; + + if ($offset != '') + { + $this->ar_offset = $offset; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Sets the OFFSET value + * + * @access public + * @param integer the offset value + * @return object + */ + function offset($offset) + { + $this->ar_offset = $offset; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * The "set" function. Allows key/value pairs to be set for inserting or updating + * + * @access public + * @param mixed + * @param string + * @param boolean + * @return object + */ + function set($key, $value = '', $escape = TRUE) + { + $key = $this->_object_to_array($key); + + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + foreach ($key as $k => $v) + { + if ($escape === FALSE) + { + $this->ar_set[$this->_protect_identifiers($k)] = $v; + } + else + { + $this->ar_set[$this->_protect_identifiers($k)] = $this->escape($v); + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get + * + * Compiles the select statement based on the other functions called + * and runs the query + * + * @access public + * @param string the table + * @param string the limit clause + * @param string the offset clause + * @return object + */ + function get($table = '', $limit = null, $offset = null) + { + if ($table != '') + { + $this->_track_aliases($table); + $this->from($table); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->_compile_select(); + + $result = $this->query($sql); + $this->_reset_select(); + return $result; + } + + /** + * "Count All Results" query + * + * Generates a platform-specific query string that counts all records + * returned by an Active Record query. + * + * @access public + * @param string + * @return string + */ + function count_all_results($table = '') + { + if ($table != '') + { + $this->_track_aliases($table); + $this->from($table); + } + + $sql = $this->_compile_select($this->_count_string . $this->_protect_identifiers('numrows')); + + $query = $this->query($sql); + $this->_reset_select(); + + if ($query->num_rows() == 0) + { + return '0'; + } + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Get_Where + * + * Allows the where clause, limit and offset to be added directly + * + * @access public + * @param string the where clause + * @param string the limit clause + * @param string the offset clause + * @return object + */ + function get_where($table = '', $where = null, $limit = null, $offset = null) + { + if ($table != '') + { + $this->from($table); + } + + if ( ! is_null($where)) + { + $this->where($where); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->_compile_select(); + + $result = $this->query($sql); + $this->_reset_select(); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * getwhere() is an alias of get_where() + * this function is here for backwards compatibility, as + * getwhere() has been deprecated + */ + function getwhere($table = '', $where = null, $limit = null, $offset = null) + { + return $this->get_where($table, $where, $limit, $offset); + } + + // -------------------------------------------------------------------- + + /** + * Insert + * + * Compiles an insert string and runs the query + * + * @access public + * @param string the table to retrieve the results from + * @param array an associative array of insert values + * @return object + */ + function insert($table = '', $set = NULL) + { + if ( ! is_null($set)) + { + $this->set($set); + } + + if (count($this->ar_set) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_must_use_set'); + } + return FALSE; + } + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + + $sql = $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set)); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Update + * + * Compiles an update string and runs the query + * + * @access public + * @param string the table to retrieve the results from + * @param array an associative array of update values + * @param mixed the where clause + * @return object + */ + function update($table = '', $set = NULL, $where = NULL, $limit = NULL) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ( ! is_null($set)) + { + $this->set($set); + } + + if (count($this->ar_set) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_must_use_set'); + } + return FALSE; + } + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + + if ($where != NULL) + { + $this->where($where); + } + + if ($limit != NULL) + { + $this->limit($limit); + } + + $sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set, $this->ar_where, $this->ar_orderby, $this->ar_limit); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Empty Table + * + * Compiles a delete string and runs "DELETE FROM table" + * + * @access public + * @param string the table to empty + * @return object + */ + function empty_table($table = '') + { + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_delete($table); + + $this->_reset_write(); + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Truncate + * + * Compiles a truncate string and runs the query + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table to truncate + * @return object + */ + function truncate($table = '') + { + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_truncate($table); + + $this->_reset_write(); + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Delete + * + * Compiles a delete string and runs the query + * + * @access public + * @param mixed the table(s) to delete from. String or array + * @param mixed the where clause + * @param mixed the limit clause + * @param boolean + * @return object + */ + function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + elseif (is_array($table)) + { + foreach($table as $single_table) + { + $this->delete($single_table, $where, $limit, FALSE); + } + + $this->_reset_write(); + return; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + if ($where != '') + { + $this->where($where); + } + + if ($limit != NULL) + { + $this->limit($limit); + } + + if (count($this->ar_where) == 0 && count($this->ar_like) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_del_must_use_where'); + } + + return FALSE; + } + + $sql = $this->_delete($table, $this->ar_where, $this->ar_like, $this->ar_limit); + + if ($reset_data) + { + $this->_reset_write(); + } + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * DB Prefix + * + * Prepends a database prefix if one exists in configuration + * + * @access public + * @param string the table + * @return string + */ + function dbprefix($table = '') + { + if ($table == '') + { + $this->display_error('db_table_name_required'); + } + + return $this->dbprefix.$table; + } + + // -------------------------------------------------------------------- + + /** + * Track Aliases + * + * Used to track SQL statements written with aliased tables. + * + * @access private + * @param string The table to inspect + * @return string + */ + function _track_aliases($table) + { + if (is_array($table)) + { + foreach ($table as $t) + { + $this->_track_aliases($t); + } + return; + } + + // Does the string contain a comma? If so, we need to separate + // the string into discreet statements + if (strpos($table, ',') !== FALSE) + { + return $this->_track_aliases(explode(',', $table)); + } + + // if a table alias is used we can recognize it by a space + if (strpos($table, " ") !== FALSE) + { + // if the alias is written with the AS keyword, remove it + $table = preg_replace('/ AS /i', ' ', $table); + + // Grab the alias + $table = trim(strrchr($table, " ")); + + // Store the alias, if it doesn't already exist + if ( ! in_array($table, $this->ar_aliased_tables)) + { + $this->ar_aliased_tables[] = $table; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Compile the SELECT statement + * + * Generates a query string based on which functions were used. + * Should not be called directly. The get() function calls it. + * + * @access private + * @return string + */ + function _compile_select($select_override = FALSE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + // ---------------------------------------------------------------- + + // Write the "select" portion of the query + + if ($select_override !== FALSE) + { + $sql = $select_override; + } + else + { + $sql = ( ! $this->ar_distinct) ? 'SELECT ' : 'SELECT DISTINCT '; + + if (count($this->ar_select) == 0) + { + $sql .= '*'; + } + else + { + // Cycle through the "select" portion of the query and prep each column name. + // The reason we protect identifiers here rather then in the select() function + // is because until the user calls the from() function we don't know if there are aliases + foreach ($this->ar_select as $key => $val) + { + $this->ar_select[$key] = $this->_protect_identifiers($val); + } + + $sql .= implode(', ', $this->ar_select); + } + } + + // ---------------------------------------------------------------- + + // Write the "FROM" portion of the query + + if (count($this->ar_from) > 0) + { + $sql .= "\nFROM "; + + $sql .= $this->_from_tables($this->ar_from); + } + + // ---------------------------------------------------------------- + + // Write the "JOIN" portion of the query + + if (count($this->ar_join) > 0) + { + $sql .= "\n"; + + $sql .= implode("\n", $this->ar_join); + } + + // ---------------------------------------------------------------- + + // Write the "WHERE" portion of the query + + if (count($this->ar_where) > 0 OR count($this->ar_like) > 0) + { + $sql .= "\n"; + + $sql .= "WHERE "; + } + + $sql .= implode("\n", $this->ar_where); + + // ---------------------------------------------------------------- + + // Write the "LIKE" portion of the query + + if (count($this->ar_like) > 0) + { + if (count($this->ar_where) > 0) + { + $sql .= "\nAND "; + } + + $sql .= implode("\n", $this->ar_like); + } + + // ---------------------------------------------------------------- + + // Write the "GROUP BY" portion of the query + + if (count($this->ar_groupby) > 0) + { + $sql .= "\nGROUP BY "; + + $sql .= implode(', ', $this->ar_groupby); + } + + // ---------------------------------------------------------------- + + // Write the "HAVING" portion of the query + + if (count($this->ar_having) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $this->ar_having); + } + + // ---------------------------------------------------------------- + + // Write the "ORDER BY" portion of the query + + if (count($this->ar_orderby) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $this->ar_orderby); + + if ($this->ar_order !== FALSE) + { + $sql .= ($this->ar_order == 'desc') ? ' DESC' : ' ASC'; + } + } + + // ---------------------------------------------------------------- + + // Write the "LIMIT" portion of the query + + if (is_numeric($this->ar_limit)) + { + $sql .= "\n"; + $sql = $this->_limit($sql, $this->ar_limit, $this->ar_offset); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @access public + * @param object + * @return array + */ + function _object_to_array($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = array(); + foreach (get_object_vars($object) as $key => $val) + { + // There are some built in keys we need to ignore for this conversion + if ( ! is_object($val) && ! is_array($val) && $key != '_parent_name' && $key != '_ci_scaffolding' && $key != '_ci_scaff_table') + { + $array[$key] = $val; + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Start Cache + * + * Starts AR caching + * + * @access public + * @return void + */ + function start_cache() + { + $this->ar_caching = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Stop Cache + * + * Stops AR caching + * + * @access public + * @return void + */ + function stop_cache() + { + $this->ar_caching = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Flush Cache + * + * Empties the AR cache + * + * @access public + * @return void + */ + function flush_cache() + { + $this->_reset_run( + array( + 'ar_cache_select' => array(), + 'ar_cache_from' => array(), + 'ar_cache_join' => array(), + 'ar_cache_where' => array(), + 'ar_cache_like' => array(), + 'ar_cache_groupby' => array(), + 'ar_cache_having' => array(), + 'ar_cache_orderby' => array(), + 'ar_cache_set' => array(), + 'ar_cache_exists' => array() + ) + ); + } + + // -------------------------------------------------------------------- + + /** + * Merge Cache + * + * When called, this function merges any cached AR arrays with + * locally called ones. + * + * @access private + * @return void + */ + function _merge_cache() + { + if (count($this->ar_cache_exists) == 0) + { + return; + } + + foreach ($this->ar_cache_exists as $val) + { + $ar_variable = 'ar_'.$val; + $ar_cache_var = 'ar_cache_'.$val; + + if (count($this->$ar_cache_var) == 0) + { + continue; + } + + $this->$ar_variable = array_unique(array_merge($this->$ar_cache_var, $this->$ar_variable)); + } + + // If we are "protecting identifiers" we need to examine the "from" + // portion of the query to determine if there are any aliases + if ($this->_protect_identifiers === TRUE AND count($this->ar_cache_from) > 0) + { + $this->_track_aliases($this->ar_from); + } + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record values. Called by the get() function + * + * @access private + * @param array An array of fields to reset + * @return void + */ + function _reset_run($ar_reset_items) + { + foreach ($ar_reset_items as $item => $default_value) + { + if ( ! in_array($item, $this->ar_store_array)) + { + $this->$item = $default_value; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record values. Called by the get() function + * + * @access private + * @return void + */ + function _reset_select() + { + $ar_reset_items = array( + 'ar_select' => array(), + 'ar_from' => array(), + 'ar_join' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_groupby' => array(), + 'ar_having' => array(), + 'ar_orderby' => array(), + 'ar_wherein' => array(), + 'ar_aliased_tables' => array(), + 'ar_distinct' => FALSE, + 'ar_limit' => FALSE, + 'ar_offset' => FALSE, + 'ar_order' => FALSE, + ); + + $this->_reset_run($ar_reset_items); + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record "write" values. + * + * Called by the insert() update() and delete() functions + * + * @access private + * @return void + */ + function _reset_write() + { + $ar_reset_items = array( + 'ar_set' => array(), + 'ar_from' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_orderby' => array(), + 'ar_limit' => FALSE, + 'ar_order' => FALSE + ); + + $this->_reset_run($ar_reset_items); + } + +} + +/* End of file DB_active_rec.php */ +/* Location: ./system/database/DB_active_rec.php */ \ No newline at end of file diff --git a/database/DB_cache.php b/database/DB_cache.php new file mode 100644 index 0000000..9bb1b16 --- /dev/null +++ b/database/DB_cache.php @@ -0,0 +1,195 @@ +CI + // and load the file helper since we use it a lot + $this->CI =& get_instance(); + $this->db =& $db; + $this->CI->load->helper('file'); + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Directory Path + * + * @access public + * @param string the path to the cache directory + * @return bool + */ + function check_path($path = '') + { + if ($path == '') + { + if ($this->db->cachedir == '') + { + return $this->db->cache_off(); + } + + $path = $this->db->cachedir; + } + + // Add a trailing slash to the path if needed + $path = preg_replace("/(.+?)\/*$/", "\\1/", $path); + + if ( ! is_dir($path) OR ! is_really_writable($path)) + { + // If the path is wrong we'll turn off caching + return $this->db->cache_off(); + } + + $this->db->cachedir = $path; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Retrieve a cached query + * + * The URI being requested will become the name of the cache sub-folder. + * An MD5 hash of the SQL statement will become the cache file name + * + * @access public + * @return string + */ + function read($sql) + { + if ( ! $this->check_path()) + { + return $this->db->cache_off(); + } + + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + + $filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql); + + if (FALSE === ($cachedata = read_file($filepath))) + { + return FALSE; + } + + return unserialize($cachedata); + } + + // -------------------------------------------------------------------- + + /** + * Write a query to a cache file + * + * @access public + * @return bool + */ + function write($sql, $object) + { + if ( ! $this->check_path()) + { + return $this->db->cache_off(); + } + + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + + $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; + + $filename = md5($sql); + + if ( ! @is_dir($dir_path)) + { + if ( ! @mkdir($dir_path, DIR_WRITE_MODE)) + { + return FALSE; + } + + @chmod($dir_path, DIR_WRITE_MODE); + } + + if (write_file($dir_path.$filename, serialize($object)) === FALSE) + { + return FALSE; + } + + @chmod($dir_path.$filename, DIR_WRITE_MODE); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Delete cache files within a particular directory + * + * @access public + * @return bool + */ + function delete($segment_one = '', $segment_two = '') + { + if ($segment_one == '') + { + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + } + + if ($segment_two == '') + { + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + } + + $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; + + delete_files($dir_path, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Delete all existing cache files + * + * @access public + * @return bool + */ + function delete_all() + { + delete_files($this->db->cachedir, TRUE); + } + +} + + +/* End of file DB_cache.php */ +/* Location: ./system/database/DB_cache.php */ \ No newline at end of file diff --git a/database/DB_driver.php b/database/DB_driver.php new file mode 100644 index 0000000..07c19c5 --- /dev/null +++ b/database/DB_driver.php @@ -0,0 +1,1319 @@ + $val) + { + $this->$key = $val; + } + } + + log_message('debug', 'Database Driver Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Initialize Database Settings + * + * @access private Called by the constructor + * @param mixed + * @return void + */ + function initialize() + { + // If an existing connection resource is available + // there is no need to connect and select the database + if (is_resource($this->conn_id) OR is_object($this->conn_id)) + { + return TRUE; + } + + // ---------------------------------------------------------------- + + // Connect to the database and set the connection ID + $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect(); + + // No connection resource? Throw an error + if ( ! $this->conn_id) + { + log_message('error', 'Unable to connect to the database'); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_connect'); + } + return FALSE; + } + + // ---------------------------------------------------------------- + + // Select the DB... assuming a database name is specified in the config file + if ($this->database != '') + { + if ( ! $this->db_select()) + { + log_message('error', 'Unable to select database: '.$this->database); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_select', $this->database); + } + return FALSE; + } + else + { + // We've selected the DB. Now we set the character set + if ( ! $this->db_set_charset($this->char_set, $this->dbcollat)) + { + return FALSE; + } + + return TRUE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat)) + { + log_message('error', 'Unable to set database connection charset: '.$this->char_set); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_set_charset', $this->char_set); + } + + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * The name of the platform in use (mysql, mssql, etc...) + * + * @access public + * @return string + */ + function platform() + { + return $this->dbdriver; + } + + // -------------------------------------------------------------------- + + /** + * Database Version Number. Returns a string containing the + * version of the database being used + * + * @access public + * @return string + */ + function version() + { + if (FALSE === ($sql = $this->_version())) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + if ($this->dbdriver == 'oci8') + { + return $sql; + } + + $query = $this->query($sql); + return $query->row('ver'); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * Accepts an SQL string as input and returns a result object upon + * successful execution of a "read" type query. Returns boolean TRUE + * upon successful execution of a "write" type query. Returns boolean + * FALSE upon failure, and if the $db_debug variable is set to TRUE + * will raise an error. + * + * @access public + * @param string An SQL query string + * @param array An array of binding data + * @return mixed + */ + function query($sql, $binds = FALSE, $return_object = TRUE) + { + if ($sql == '') + { + if ($this->db_debug) + { + log_message('error', 'Invalid query: '.$sql); + return $this->display_error('db_invalid_query'); + } + return FALSE; + } + + // Verify table prefix and replace if necessary + if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) ) + { + $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql); + } + + // Is query caching enabled? If the query is a "read type" + // we will load the caching class and return the previously + // cached query if it exists + if ($this->cache_on == TRUE AND stristr($sql, 'SELECT')) + { + if ($this->_cache_init()) + { + $this->load_rdriver(); + if (FALSE !== ($cache = $this->CACHE->read($sql))) + { + return $cache; + } + } + } + + // Compile binds if needed + if ($binds !== FALSE) + { + $sql = $this->compile_binds($sql, $binds); + } + + // Save the query for debugging + if ($this->save_queries == TRUE) + { + $this->queries[] = $sql; + } + + // Start the Query Timer + $time_start = list($sm, $ss) = explode(' ', microtime()); + + // Run the Query + if (FALSE === ($this->result_id = $this->simple_query($sql))) + { + if ($this->save_queries == TRUE) + { + $this->query_times[] = 0; + } + + // This will trigger a rollback if transactions are being used + $this->_trans_status = FALSE; + + if ($this->db_debug) + { + // grab the error number and message now, as we might run some + // additional queries before displaying the error + $error_no = $this->_error_number(); + $error_msg = $this->_error_message(); + + // We call this function in order to roll-back queries + // if transactions are enabled. If we don't call this here + // the error message will trigger an exit, causing the + // transactions to remain in limbo. + $this->trans_complete(); + + // Log and display errors + log_message('error', 'Query error: '.$error_msg); + return $this->display_error( + array( + 'Error Number: '.$error_no, + $error_msg, + $sql + ) + ); + } + + return FALSE; + } + + // Stop and aggregate the query time results + $time_end = list($em, $es) = explode(' ', microtime()); + $this->benchmark += ($em + $es) - ($sm + $ss); + + if ($this->save_queries == TRUE) + { + $this->query_times[] = ($em + $es) - ($sm + $ss); + } + + // Increment the query counter + $this->query_count++; + + // Was the query a "write" type? + // If so we'll simply return true + if ($this->is_write_type($sql) === TRUE) + { + // If caching is enabled we'll auto-cleanup any + // existing files related to this particular URI + if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init()) + { + $this->CACHE->delete(); + } + + return TRUE; + } + + // Return TRUE if we don't need to create a result object + // Currently only the Oracle driver uses this when stored + // procedures are used + if ($return_object !== TRUE) + { + return TRUE; + } + + // Load and instantiate the result driver + + $driver = $this->load_rdriver(); + $RES = new $driver(); + $RES->conn_id = $this->conn_id; + $RES->result_id = $this->result_id; + + if ($this->dbdriver == 'oci8') + { + $RES->stmt_id = $this->stmt_id; + $RES->curs_id = NULL; + $RES->limit_used = $this->limit_used; + $this->stmt_id = FALSE; + } + + // oci8 vars must be set before calling this + $RES->num_rows = $RES->num_rows(); + + // Is query caching enabled? If so, we'll serialize the + // result object and save it to a cache file. + if ($this->cache_on == TRUE AND $this->_cache_init()) + { + // We'll create a new instance of the result object + // only without the platform specific driver since + // we can't use it with cached data (the query result + // resource ID won't be any good once we've cached the + // result object, so we'll have to compile the data + // and save it) + $CR = new CI_DB_result(); + $CR->num_rows = $RES->num_rows(); + $CR->result_object = $RES->result_object(); + $CR->result_array = $RES->result_array(); + + // Reset these since cached objects can not utilize resource IDs. + $CR->conn_id = NULL; + $CR->result_id = NULL; + + $this->CACHE->write($sql, $CR); + } + + return $RES; + } + + // -------------------------------------------------------------------- + + /** + * Load the result drivers + * + * @access public + * @return string the name of the result class + */ + function load_rdriver() + { + $driver = 'CI_DB_'.$this->dbdriver.'_result'; + + if ( ! class_exists($driver)) + { + include_once(BASEPATH.'database/DB_result'.EXT); + include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result'.EXT); + } + + return $driver; + } + + // -------------------------------------------------------------------- + + /** + * Simple Query + * This is a simplified version of the query() function. Internally + * we only use it when running transaction commands since they do + * not require all the features of the main query() function. + * + * @access public + * @param string the sql query + * @return mixed + */ + function simple_query($sql) + { + if ( ! $this->conn_id) + { + $this->initialize(); + } + + return $this->_execute($sql); + } + + // -------------------------------------------------------------------- + + /** + * Disable Transactions + * This permits transactions to be disabled at run-time. + * + * @access public + * @return void + */ + function trans_off() + { + $this->trans_enabled = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Enable/disable Transaction Strict Mode + * When strict mode is enabled, if you are running multiple groups of + * transactions, if one group fails all groups will be rolled back. + * If strict mode is disabled, each group is treated autonomously, meaning + * a failure of one group will not affect any others + * + * @access public + * @return void + */ + function trans_strict($mode = TRUE) + { + $this->trans_strict = is_bool($mode) ? $mode : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Start Transaction + * + * @access public + * @return void + */ + function trans_start($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + $this->_trans_depth += 1; + return; + } + + $this->trans_begin($test_mode); + } + + // -------------------------------------------------------------------- + + /** + * Complete Transaction + * + * @access public + * @return bool + */ + function trans_complete() + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 1) + { + $this->_trans_depth -= 1; + return TRUE; + } + + // The query() function will set this flag to FALSE in the event that a query failed + if ($this->_trans_status === FALSE) + { + $this->trans_rollback(); + + // If we are NOT running in strict mode, we will reset + // the _trans_status flag so that subsequent groups of transactions + // will be permitted. + if ($this->trans_strict === FALSE) + { + $this->_trans_status = TRUE; + } + + log_message('debug', 'DB Transaction Failure'); + return FALSE; + } + + $this->trans_commit(); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Lets you retrieve the transaction flag to determine if it has failed + * + * @access public + * @return bool + */ + function trans_status() + { + return $this->_trans_status; + } + + // -------------------------------------------------------------------- + + /** + * Compile Bindings + * + * @access public + * @param string the sql statement + * @param array an array of bind data + * @return string + */ + function compile_binds($sql, $binds) + { + if (strpos($sql, $this->bind_marker) === FALSE) + { + return $sql; + } + + if ( ! is_array($binds)) + { + $binds = array($binds); + } + + // Get the sql segments around the bind markers + $segments = explode($this->bind_marker, $sql); + + // The count of bind should be 1 less then the count of segments + // If there are more bind arguments trim it down + if (count($binds) >= count($segments)) { + $binds = array_slice($binds, 0, count($segments)-1); + } + + // Construct the binded query + $result = $segments[0]; + $i = 0; + foreach ($binds as $bind) + { + $result .= $this->escape($bind); + $result .= $segments[++$i]; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Determines if a query is a "write" type. + * + * @access public + * @param string An SQL query string + * @return boolean + */ + function is_write_type($sql) + { + if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql)) + { + return FALSE; + } + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Calculate the aggregate query elapsed time + * + * @access public + * @param integer The number of decimal places + * @return integer + */ + function elapsed_time($decimals = 6) + { + return number_format($this->benchmark, $decimals); + } + + // -------------------------------------------------------------------- + + /** + * Returns the total number of queries + * + * @access public + * @return integer + */ + function total_queries() + { + return $this->query_count; + } + + // -------------------------------------------------------------------- + + /** + * Returns the last query that was executed + * + * @access public + * @return void + */ + function last_query() + { + return end($this->queries); + } + + // -------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type + * Sets boolean and null types + * + * @access public + * @param string + * @return integer + */ + function escape($str) + { + switch (gettype($str)) + { + case 'string' : $str = "'".$this->escape_str($str)."'"; + break; + case 'boolean' : $str = ($str === FALSE) ? 0 : 1; + break; + default : $str = ($str === NULL) ? 'NULL' : $str; + break; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Primary + * + * Retrieves the primary key. It assumes that the row in the first + * position is the primary key + * + * @access public + * @param string the table name + * @return string + */ + function primary($table = '') + { + $fields = $this->list_fields($table); + + if ( ! is_array($fields)) + { + return FALSE; + } + + return current($fields); + } + + // -------------------------------------------------------------------- + + /** + * Returns an array of table names + * + * @access public + * @return array + */ + function list_tables($constrain_by_prefix = FALSE) + { + // Is there a cached result? + if (isset($this->data_cache['table_names'])) + { + return $this->data_cache['table_names']; + } + + if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix))) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + $retval = array(); + $query = $this->query($sql); + + if ($query->num_rows() > 0) + { + foreach($query->result_array() as $row) + { + if (isset($row['TABLE_NAME'])) + { + $retval[] = $row['TABLE_NAME']; + } + else + { + $retval[] = array_shift($row); + } + } + } + + $this->data_cache['table_names'] = $retval; + return $this->data_cache['table_names']; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular table exists + * @access public + * @return boolean + */ + function table_exists($table_name) + { + return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Fetch MySQL Field Names + * + * @access public + * @param string the table name + * @return array + */ + function list_fields($table = '') + { + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if ($table == '') + { + if ($this->db_debug) + { + return $this->display_error('db_field_param_missing'); + } + return FALSE; + } + + if (FALSE === ($sql = $this->_list_columns($this->_protect_identifiers($table, TRUE, NULL, FALSE)))) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + $query = $this->query($sql); + + $retval = array(); + foreach($query->result_array() as $row) + { + if (isset($row['COLUMN_NAME'])) + { + $retval[] = $row['COLUMN_NAME']; + } + else + { + $retval[] = current($row); + } + } + + $this->data_cache['field_names'][$table] = $retval; + return $this->data_cache['field_names'][$table]; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular field exists + * @access public + * @param string + * @param string + * @return boolean + */ + function field_exists($field_name, $table_name) + { + return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @access public + * @param string the table name + * @return object + */ + function field_data($table = '') + { + if ($table == '') + { + if ($this->db_debug) + { + return $this->display_error('db_field_param_missing'); + } + return FALSE; + } + + $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE))); + + return $query->field_data(); + } + + // -------------------------------------------------------------------- + + /** + * Generate an insert string + * + * @access public + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @return string + */ + function insert_string($table, $data) + { + $fields = array(); + $values = array(); + + foreach($data as $key => $val) + { + $fields[] = $this->_escape_identifiers($key); + $values[] = $this->escape($val); + } + + return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values); + } + + // -------------------------------------------------------------------- + + /** + * Generate an update string + * + * @access public + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @param mixed the "where" statement + * @return string + */ + function update_string($table, $data, $where) + { + if ($where == '') + { + return false; + } + + $fields = array(); + foreach($data as $key => $val) + { + $fields[$this->_protect_identifiers($key)] = $this->escape($val); + } + + if ( ! is_array($where)) + { + $dest = array($where); + } + else + { + $dest = array(); + foreach ($where as $key => $val) + { + $prefix = (count($dest) == 0) ? '' : ' AND '; + + if ($val !== '') + { + if ( ! $this->_has_operator($key)) + { + $key .= ' ='; + } + + $val = ' '.$this->escape($val); + } + + $dest[] = $prefix.$key.$val; + } + } + + return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest); + } + + // -------------------------------------------------------------------- + + /** + * Tests whether the string has an SQL operator + * + * @access private + * @param string + * @return bool + */ + function _has_operator($str) + { + $str = trim($str); + if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str)) + { + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Enables a native PHP function to be run, using a platform agnostic wrapper. + * + * @access public + * @param string the function name + * @param mixed any parameters needed by the function + * @return mixed + */ + function call_function($function) + { + $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_'; + + if (FALSE === strpos($driver, $function)) + { + $function = $driver.$function; + } + + if ( ! function_exists($function)) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + else + { + $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null; + + return call_user_func_array($function, $args); + } + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Directory Path + * + * @access public + * @param string the path to the cache directory + * @return void + */ + function cache_set_path($path = '') + { + $this->cachedir = $path; + } + + // -------------------------------------------------------------------- + + /** + * Enable Query Caching + * + * @access public + * @return void + */ + function cache_on() + { + $this->cache_on = TRUE; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Disable Query Caching + * + * @access public + * @return void + */ + function cache_off() + { + $this->cache_on = FALSE; + return FALSE; + } + + + // -------------------------------------------------------------------- + + /** + * Delete the cache files associated with a particular URI + * + * @access public + * @return void + */ + function cache_delete($segment_one = '', $segment_two = '') + { + if ( ! $this->_cache_init()) + { + return FALSE; + } + return $this->CACHE->delete($segment_one, $segment_two); + } + + // -------------------------------------------------------------------- + + /** + * Delete All cache files + * + * @access public + * @return void + */ + function cache_delete_all() + { + if ( ! $this->_cache_init()) + { + return FALSE; + } + + return $this->CACHE->delete_all(); + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Cache Class + * + * @access private + * @return void + */ + function _cache_init() + { + if (is_object($this->CACHE) AND class_exists('CI_DB_Cache')) + { + return TRUE; + } + + if ( ! @include(BASEPATH.'database/DB_cache'.EXT)) + { + return $this->cache_off(); + } + + $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @return void + */ + function close() + { + if (is_resource($this->conn_id) OR is_object($this->conn_id)) + { + $this->_close($this->conn_id); + } + $this->conn_id = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Display an error message + * + * @access public + * @param string the error message + * @param string any "swap" values + * @param boolean whether to localize the message + * @return string sends the application/error_db.php template + */ + function display_error($error = '', $swap = '', $native = FALSE) + { + $LANG =& load_class('Language'); + $LANG->load('db'); + + $heading = $LANG->line('db_error_heading'); + + if ($native == TRUE) + { + $message = $error; + } + else + { + $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error; + } + + $error =& load_class('Exceptions'); + echo $error->show_error($heading, $message, 'error_db'); + exit; + } + + // -------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function adds backticks if appropriate based on db type + * + * @access private + * @param mixed the item to escape + * @return mixed the item with backticks + */ + function protect_identifiers($item, $prefix_single = FALSE) + { + return $this->_protect_identifiers($item, $prefix_single); + } + + // -------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function is used extensively by the Active Record class, and by + * a couple functions in this class. + * It takes a column or table name (optionally with an alias) and inserts + * the table prefix onto it. Some logic is necessary in order to deal with + * column names that include the path. Consider a query like this: + * + * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table + * + * Or a query with aliasing: + * + * SELECT m.member_id, m.member_name FROM members AS m + * + * Since the column name can include up to four segments (host, DB, table, column) + * or also have an alias prefix, we need to do a bit of work to figure this out and + * insert the table prefix (if it exists) in the proper position, and escape only + * the correct identifiers. + * + * @access private + * @param string + * @param bool + * @param mixed + * @param bool + * @return string + */ + function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE) + { + if ( ! is_bool($protect_identifiers)) + { + $protect_identifiers = $this->_protect_identifiers; + } + + // Convert tabs or multiple spaces into single spaces + $item = preg_replace('/[\t| ]+/', ' ', $item); + + // If the item has an alias declaration we remove it and set it aside. + // Basically we remove everything to the right of the first space + $alias = ''; + if (strpos($item, ' ') !== FALSE) + { + $alias = strstr($item, " "); + $item = substr($item, 0, - strlen($alias)); + } + + // Break the string apart if it contains periods, then insert the table prefix + // in the correct location, assuming the period doesn't indicate that we're dealing + // with an alias. While we're at it, we will escape the components + if (strpos($item, '.') !== FALSE) + { + $parts = explode('.', $item); + + // Does the first segment of the exploded item match + // one of the aliases previously identified? If so, + // we have nothing more to do other then escape the item + if (in_array($parts[0], $this->ar_aliased_tables)) + { + if ($protect_identifiers === TRUE) + { + foreach ($parts as $key => $val) + { + if ( ! in_array($val, $this->_reserved_identifiers)) + { + $parts[$key] = $this->_escape_identifiers($val); + } + } + + $item = implode('.', $parts); + } + return $item.$alias; + } + + // Is there a table prefix defined in the config file? If not, no need to do anything + if ($this->dbprefix != '') + { + // We now add the table prefix based on some logic. + // Do we have 4 segments (hostname.database.table.column)? + // If so, we add the table prefix to the column name in the 3rd segment. + if (isset($parts[3])) + { + $i = 2; + } + // Do we have 3 segments (database.table.column)? + // If so, we add the table prefix to the column name in 2nd position + elseif (isset($parts[2])) + { + $i = 1; + } + // Do we have 2 segments (table.column)? + // If so, we add the table prefix to the column name in 1st segment + else + { + $i = 0; + } + + // This flag is set when the supplied $item does not contain a field name. + // This can happen when this function is being called from a JOIN. + if ($field_exists == FALSE) + { + $i++; + } + + // We only add the table prefix if it does not already exist + if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix) + { + $parts[$i] = $this->dbprefix.$parts[$i]; + } + + // Put the parts back together + $item = implode('.', $parts); + } + + if ($protect_identifiers === TRUE) + { + $item = $this->_escape_identifiers($item); + } + + return $item.$alias; + } + + // This is basically a bug fix for queries that use MAX, MIN, etc. + // If a parenthesis is found we know that we do not need to + // escape the data or add a prefix. There's probably a more graceful + // way to deal with this, but I'm not thinking of it -- Rick + if (strpos($item, '(') !== FALSE) + { + return $item.$alias; + } + + // Is there a table prefix? If not, no need to insert it + if ($this->dbprefix != '') + { + // Do we prefix an item with no segments? + if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix) + { + $item = $this->dbprefix.$item; + } + } + + if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers)) + { + $item = $this->_escape_identifiers($item); + } + + return $item.$alias; + } + + +} + + +/* End of file DB_driver.php */ +/* Location: ./system/database/DB_driver.php */ \ No newline at end of file diff --git a/database/DB_forge.php b/database/DB_forge.php new file mode 100644 index 0000000..20f0a30 --- /dev/null +++ b/database/DB_forge.php @@ -0,0 +1,355 @@ +db + $CI =& get_instance(); + $this->db =& $CI->db; + log_message('debug', "Database Forge Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function create_database($db_name) + { + $sql = $this->_create_database($db_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access public + * @param string the database name + * @return bool + */ + function drop_database($db_name) + { + $sql = $this->_drop_database($db_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Add Key + * + * @access public + * @param string key + * @param string type + * @return void + */ + function add_key($key = '', $primary = FALSE) + { + if (is_array($key)) + { + foreach($key as $one) + { + $this->add_key($one, $primary); + } + + return; + } + + if ($key == '') + { + show_error('Key information is required for that operation.'); + } + + if ($primary === TRUE) + { + $this->primary_keys[] = $key; + } + else + { + $this->keys[] = $key; + } + } + + // -------------------------------------------------------------------- + + /** + * Add Field + * + * @access public + * @param string collation + * @return void + */ + function add_field($field = '') + { + if ($field == '') + { + show_error('Field information is required.'); + } + + if (is_string($field)) + { + if ($field == 'id') + { + $this->add_field(array( + 'id' => array( + 'type' => 'INT', + 'constraint' => 9, + 'auto_increment' => TRUE + ) + )); + $this->add_key('id', TRUE); + } + else + { + if (strpos($field, ' ') === FALSE) + { + show_error('Field information is required for that operation.'); + } + + $this->fields[] = $field; + } + } + + if (is_array($field)) + { + $this->fields = array_merge($this->fields, $field); + } + + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access public + * @param string the table name + * @return bool + */ + function create_table($table = '', $if_not_exists = FALSE) + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_create_table($this->db->dbprefix.$table, $this->fields, $this->primary_keys, $this->keys, $if_not_exists); + + $this->_reset(); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access public + * @param string the table name + * @return bool + */ + function drop_table($table_name) + { + $sql = $this->_drop_table($this->db->dbprefix.$table_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Rename Table + * + * @access public + * @param string the old table name + * @param string the new table name + * @return bool + */ + function rename_table($table_name, $new_table_name) + { + if ($table_name == '' OR $new_table_name == '') + { + show_error('A table name is required for that operation.'); + } + + $sql = $this->_rename_table($table_name, $new_table_name); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Add + * + * @access public + * @param string the table name + * @param string the column name + * @param string the column definition + * @return bool + */ + function add_column($table = '', $field = array(), $after_field = '') + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + // add field info into field array, but we can only do one at a time + // so only grab the first field in the event there are more then one + $this->add_field(array_slice($field, 0, 1)); + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->fields, $after_field); + + $this->_reset(); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Drop + * + * @access public + * @param string the table name + * @param string the column name + * @return bool + */ + function drop_column($table = '', $column_name = '') + { + + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + if ($column_name == '') + { + show_error('A column name is required for that operation.'); + } + + $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name); + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Modify + * + * @access public + * @param string the table name + * @param string the column name + * @param string the column definition + * @return bool + */ + function modify_column($table = '', $field = array()) + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + // add field info into field array, but we can only do one at a time + // so only grab the first field in the event there are more then one + $this->add_field(array_slice($field, 0, 1)); + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->fields); + + $this->_reset(); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Reset + * + * Resets table creation vars + * + * @access private + * @return void + */ + function _reset() + { + $this->fields = array(); + $this->keys = array(); + $this->primary_keys = array(); + } + +} + +/* End of file DB_forge.php */ +/* Location: ./system/database/DB_forge.php */ \ No newline at end of file diff --git a/database/DB_result.php b/database/DB_result.php new file mode 100644 index 0000000..8f55f67 --- /dev/null +++ b/database/DB_result.php @@ -0,0 +1,342 @@ +result_object() : $this->result_array(); + } + + // -------------------------------------------------------------------- + + /** + * Query result. "object" version. + * + * @access public + * @return object + */ + function result_object() + { + if (count($this->result_object) > 0) + { + return $this->result_object; + } + + // In the event that query caching is on the result_id variable + // will return FALSE since there isn't a valid SQL resource so + // we'll simply return an empty array. + if ($this->result_id === FALSE OR $this->num_rows() == 0) + { + return array(); + } + + $this->_data_seek(0); + while ($row = $this->_fetch_object()) + { + $this->result_object[] = $row; + } + + return $this->result_object; + } + + // -------------------------------------------------------------------- + + /** + * Query result. "array" version. + * + * @access public + * @return array + */ + function result_array() + { + if (count($this->result_array) > 0) + { + return $this->result_array; + } + + // In the event that query caching is on the result_id variable + // will return FALSE since there isn't a valid SQL resource so + // we'll simply return an empty array. + if ($this->result_id === FALSE OR $this->num_rows() == 0) + { + return array(); + } + + $this->_data_seek(0); + while ($row = $this->_fetch_assoc()) + { + $this->result_array[] = $row; + } + + return $this->result_array; + } + + // -------------------------------------------------------------------- + + /** + * Query result. Acts as a wrapper function for the following functions. + * + * @access public + * @param string + * @param string can be "object" or "array" + * @return mixed either a result object or array + */ + function row($n = 0, $type = 'object') + { + if ( ! is_numeric($n)) + { + // We cache the row data for subsequent uses + if ( ! is_array($this->row_data)) + { + $this->row_data = $this->row_array(0); + } + + // array_key_exists() instead of isset() to allow for MySQL NULL values + if (array_key_exists($n, $this->row_data)) + { + return $this->row_data[$n]; + } + // reset the $n variable if the result was not achieved + $n = 0; + } + + return ($type == 'object') ? $this->row_object($n) : $this->row_array($n); + } + + // -------------------------------------------------------------------- + + /** + * Assigns an item into a particular column slot + * + * @access public + * @return object + */ + function set_row($key, $value = NULL) + { + // We cache the row data for subsequent uses + if ( ! is_array($this->row_data)) + { + $this->row_data = $this->row_array(0); + } + + if (is_array($key)) + { + foreach ($key as $k => $v) + { + $this->row_data[$k] = $v; + } + + return; + } + + if ($key != '' AND ! is_null($value)) + { + $this->row_data[$key] = $value; + } + } + + // -------------------------------------------------------------------- + + /** + * Returns a single result row - object version + * + * @access public + * @return object + */ + function row_object($n = 0) + { + $result = $this->result_object(); + + if (count($result) == 0) + { + return $result; + } + + if ($n != $this->current_row AND isset($result[$n])) + { + $this->current_row = $n; + } + + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * Returns a single result row - array version + * + * @access public + * @return array + */ + function row_array($n = 0) + { + $result = $this->result_array(); + + if (count($result) == 0) + { + return $result; + } + + if ($n != $this->current_row AND isset($result[$n])) + { + $this->current_row = $n; + } + + return $result[$this->current_row]; + } + + + // -------------------------------------------------------------------- + + /** + * Returns the "first" row + * + * @access public + * @return object + */ + function first_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + return $result[0]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "last" row + * + * @access public + * @return object + */ + function last_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + return $result[count($result) -1]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "next" row + * + * @access public + * @return object + */ + function next_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + + if (isset($result[$this->current_row + 1])) + { + ++$this->current_row; + } + + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "previous" row + * + * @access public + * @return object + */ + function previous_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + + if (isset($result[$this->current_row - 1])) + { + --$this->current_row; + } + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * The following functions are normally overloaded by the identically named + * methods in the platform-specific driver -- except when query caching + * is used. When caching is enabled we do not load the other driver. + * These functions are primarily here to prevent undefined function errors + * when a cached result object is in use. They are not otherwise fully + * operational due to the unavailability of the database resource IDs with + * cached results. + */ + function num_rows() { return $this->num_rows; } + function num_fields() { return 0; } + function list_fields() { return array(); } + function field_data() { return array(); } + function free_result() { return TRUE; } + function _data_seek() { return TRUE; } + function _fetch_assoc() { return array(); } + function _fetch_object() { return array(); } + +} +// END DB_result class + +/* End of file DB_result.php */ +/* Location: ./system/database/DB_result.php */ \ No newline at end of file diff --git a/database/DB_utility.php b/database/DB_utility.php new file mode 100644 index 0000000..a37522d --- /dev/null +++ b/database/DB_utility.php @@ -0,0 +1,389 @@ +db + $CI =& get_instance(); + $this->db =& $CI->db; + + log_message('debug', "Database Utility Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * List databases + * + * @access public + * @return bool + */ + function list_databases() + { + // Is there a cached result? + if (isset($this->data_cache['db_names'])) + { + return $this->data_cache['db_names']; + } + + $query = $this->db->query($this->_list_databases()); + $dbs = array(); + if ($query->num_rows() > 0) + { + foreach ($query->result_array() as $row) + { + $dbs[] = current($row); + } + } + + $this->data_cache['db_names'] = $dbs; + return $this->data_cache['db_names']; + } + + // -------------------------------------------------------------------- + + /** + * Optimize Table + * + * @access public + * @param string the table name + * @return bool + */ + function optimize_table($table_name) + { + $sql = $this->_optimize_table($table_name); + + if (is_bool($sql)) + { + show_error('db_must_use_set'); + } + + $query = $this->db->query($sql); + $res = $query->result_array(); + + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + return current($res); + } + + // -------------------------------------------------------------------- + + /** + * Optimize Database + * + * @access public + * @return array + */ + function optimize_database() + { + $result = array(); + foreach ($this->db->list_tables() as $table_name) + { + $sql = $this->_optimize_table($table_name); + + if (is_bool($sql)) + { + return $sql; + } + + $query = $this->db->query($sql); + + // Build the result array... + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + $res = $query->result_array(); + $res = current($res); + $key = str_replace($this->db->database.'.', '', current($res)); + $keys = array_keys($res); + unset($res[$keys[0]]); + + $result[$key] = $res; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Repair Table + * + * @access public + * @param string the table name + * @return bool + */ + function repair_table($table_name) + { + $sql = $this->_repair_table($table_name); + + if (is_bool($sql)) + { + return $sql; + } + + $query = $this->db->query($sql); + + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + $res = $query->result_array(); + return current($res); + } + + // -------------------------------------------------------------------- + + /** + * Generate CSV from a query result object + * + * @access public + * @param object The query result object + * @param string The delimiter - comma by default + * @param string The newline character - \n by default + * @param string The enclosure - double quote by default + * @return string + */ + function csv_from_result($query, $delim = ",", $newline = "\n", $enclosure = '"') + { + if ( ! is_object($query) OR ! method_exists($query, 'field_names')) + { + show_error('You must submit a valid result object'); + } + + $out = ''; + + // First generate the headings from the table column names + foreach ($query->list_fields() as $name) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; + } + + $out = rtrim($out); + $out .= $newline; + + // Next blast through the result array and build out the rows + foreach ($query->result_array() as $row) + { + foreach ($row as $item) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim; + } + $out = rtrim($out); + $out .= $newline; + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * Generate XML data from a query result object + * + * @access public + * @param object The query result object + * @param array Any preferences + * @return string + */ + function xml_from_result($query, $params = array()) + { + if ( ! is_object($query) OR ! method_exists($query, 'field_names')) + { + show_error('You must submit a valid result object'); + } + + // Set our default values + foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val) + { + if ( ! isset($params[$key])) + { + $params[$key] = $val; + } + } + + // Create variables for convenience + extract($params); + + // Load the xml helper + $CI =& get_instance(); + $CI->load->helper('xml'); + + // Generate the result + $xml = "<{$root}>".$newline; + foreach ($query->result_array() as $row) + { + $xml .= $tab."<{$element}>".$newline; + + foreach ($row as $key => $val) + { + $xml .= $tab.$tab."<{$key}>".xml_convert($val)."".$newline; + } + $xml .= $tab."".$newline; + } + $xml .= "".$newline; + + return $xml; + } + + // -------------------------------------------------------------------- + + /** + * Database Backup + * + * @access public + * @return void + */ + function backup($params = array()) + { + // If the parameters have not been submitted as an + // array then we know that it is simply the table + // name, which is a valid short cut. + if (is_string($params)) + { + $params = array('tables' => $params); + } + + // ------------------------------------------------------ + + // Set up our default preferences + $prefs = array( + 'tables' => array(), + 'ignore' => array(), + 'filename' => '', + 'format' => 'gzip', // gzip, zip, txt + 'add_drop' => TRUE, + 'add_insert' => TRUE, + 'newline' => "\n" + ); + + // Did the user submit any preferences? If so set them.... + if (count($params) > 0) + { + foreach ($prefs as $key => $val) + { + if (isset($params[$key])) + { + $prefs[$key] = $params[$key]; + } + } + } + + // ------------------------------------------------------ + + // Are we backing up a complete database or individual tables? + // If no table names were submitted we'll fetch the entire table list + if (count($prefs['tables']) == 0) + { + $prefs['tables'] = $this->db->list_tables(); + } + + // ------------------------------------------------------ + + // Validate the format + if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE)) + { + $prefs['format'] = 'txt'; + } + + // ------------------------------------------------------ + + // Is the encoder supported? If not, we'll either issue an + // error or use plain text depending on the debug settings + if (($prefs['format'] == 'gzip' AND ! @function_exists('gzencode')) + OR ($prefs['format'] == 'zip' AND ! @function_exists('gzcompress'))) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_compression'); + } + + $prefs['format'] = 'txt'; + } + + // ------------------------------------------------------ + + // Set the filename if not provided - Only needed with Zip files + if ($prefs['filename'] == '' AND $prefs['format'] == 'zip') + { + $prefs['filename'] = (count($prefs['tables']) == 1) ? $prefs['tables'] : $this->db->database; + $prefs['filename'] .= '_'.date('Y-m-d_H-i', time()); + } + + // ------------------------------------------------------ + + // Was a Gzip file requested? + if ($prefs['format'] == 'gzip') + { + return gzencode($this->_backup($prefs)); + } + + // ------------------------------------------------------ + + // Was a text file requested? + if ($prefs['format'] == 'txt') + { + return $this->_backup($prefs); + } + + // ------------------------------------------------------ + + // Was a Zip file requested? + if ($prefs['format'] == 'zip') + { + // If they included the .zip file extension we'll remove it + if (preg_match("|.+?\.zip$|", $prefs['filename'])) + { + $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); + } + + // Tack on the ".sql" file extension if needed + if ( ! preg_match("|.+?\.sql$|", $prefs['filename'])) + { + $prefs['filename'] .= '.sql'; + } + + // Load the Zip class and output it + + $CI =& get_instance(); + $CI->load->library('zip'); + $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); + return $CI->zip->get_zip(); + } + + } + +} + + +/* End of file DB_utility.php */ +/* Location: ./system/database/DB_utility.php */ \ No newline at end of file diff --git a/database/drivers/index.html b/database/drivers/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/mssql/index.html b/database/drivers/mssql/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/mssql/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/mssql/mssql_driver.php b/database/drivers/mssql/mssql_driver.php new file mode 100644 index 0000000..72dc263 --- /dev/null +++ b/database/drivers/mssql/mssql_driver.php @@ -0,0 +1,611 @@ +port != '') + { + $this->hostname .= ','.$this->port; + } + + return @mssql_connect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ($this->port != '') + { + $this->hostname .= ','.$this->port; + } + + return @mssql_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Note: The brackets are required in the event that the DB name + // contains reserved characters + return @mssql_select_db('['.$this->database.']', $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @mssql_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('BEGIN TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + // Access the CI object + $CI =& get_instance(); + + // Escape single quotes + return str_replace("'", "''", $CI->input->_remove_invisible_characters($str)); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mssql_rows_affected($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * Returns the last id created in the Identity column. + * + * @access public + * @return integer + */ + function insert_id() + { + $ver = self::_parse_major_version($this->version()); + $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id"); + $query = $this->query($sql); + $row = $query->row(); + return $row->last_id; + } + + // -------------------------------------------------------------------- + + /** + * Parse major version + * + * Grabs the major version number from the + * database server version string passed in. + * + * @access private + * @param string $version + * @return int16 major version number + */ + function _parse_major_version($version) + { + preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info); + return $ver_info[1]; // return the major version b/c that's all we're interested in. + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT @@VERSION AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; + + // for future compatibility + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + //$sql .= " LIKE '".$this->dbprefix."%'"; + return FALSE; // not currently supported + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access private + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$table."'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 * FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + // Are errros even supported in MS SQL? + return ''; + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + // Are error numbers supported? + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $i = $limit + $offset; + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mssql_close($conn_id); + } + +} + + + +/* End of file mssql_driver.php */ +/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file diff --git a/database/drivers/mssql/mssql_forge.php b/database/drivers/mssql/mssql_forge.php new file mode 100644 index 0000000..8665dc0 --- /dev/null +++ b/database/drivers/mssql/mssql_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + // I think this syntax will work, but can find little documentation on renaming tables in MSSQL + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mssql_forge.php */ +/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file diff --git a/database/drivers/mssql/mssql_result.php b/database/drivers/mssql/mssql_result.php new file mode 100644 index 0000000..33fdda9 --- /dev/null +++ b/database/drivers/mssql/mssql_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mssql_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mssql_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mssql_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->max_length = $field->max_length; + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + mssql_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mssql_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mssql_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mssql_fetch_object($this->result_id); + } + +} + + +/* End of file mssql_result.php */ +/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file diff --git a/database/drivers/mssql/mssql_utility.php b/database/drivers/mssql/mssql_utility.php new file mode 100644 index 0000000..1736fba --- /dev/null +++ b/database/drivers/mssql/mssql_utility.php @@ -0,0 +1,123 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + + +/* End of file mssql_utility.php */ +/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file diff --git a/database/drivers/mysql/index.html b/database/drivers/mysql/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/mysql/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/mysql/mysql_driver.php b/database/drivers/mysql/mysql_driver.php new file mode 100644 index 0000000..e0a1cee --- /dev/null +++ b/database/drivers/mysql/mysql_driver.php @@ -0,0 +1,623 @@ +port != '') + { + $this->hostname .= ':'.$this->port; + } + + return @mysql_connect($this->hostname, $this->username, $this->password, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ($this->port != '') + { + $this->hostname .= ':'.$this->port; + } + + return @mysql_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return @mysql_select_db($this->database, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @mysql_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + // "DELETE FROM TABLE" returns 0 affected rows This hack modifies + // the query so that it returns the number of affected rows + if ($this->delete_hack === TRUE) + { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('SET AUTOCOMMIT=0'); + $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val); + } + + return $str; + } + + if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id)) + { + return mysql_real_escape_string($str, $this->conn_id); + } + elseif (function_exists('mysql_escape_string')) + { + return mysql_escape_string($str); + } + else + { + return addslashes($str); + } + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mysql_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @mysql_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return (int)$row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " LIKE '".$this->dbprefix."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return mysql_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return mysql_errno($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mysql_close($conn_id); + } + +} + + +/* End of file mysql_driver.php */ +/* Location: ./system/database/drivers/mysql/mysql_driver.php */ \ No newline at end of file diff --git a/database/drivers/mysql/mysql_forge.php b/database/drivers/mysql/mysql_forge.php new file mode 100644 index 0000000..28a77b3 --- /dev/null +++ b/database/drivers/mysql/mysql_forge.php @@ -0,0 +1,254 @@ +$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + if (array_key_exists('NAME', $attributes)) + { + $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; + } + + if (array_key_exists('TYPE', $attributes)) + { + $sql .= ' '.$attributes['TYPE']; + } + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes)) + { + $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param mixed the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + + $sql .= $this->_process_fields($fields); + + if (count($primary_keys) > 0) + { + $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key_name = $this->db->_protect_identifiers(implode('_', $key)); + $key = $this->db->_protect_identifiers($key); + } + else + { + $key_name = $this->db->_protect_identifiers($key); + $key = array($key_name); + } + + $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return string + */ + function _drop_table($table) + { + return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param array fields + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $fields, $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql.$this->db->_protect_identifiers($fields); + } + + $sql .= $this->_process_fields($fields); + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mysql_forge.php */ +/* Location: ./system/database/drivers/mysql/mysql_forge.php */ \ No newline at end of file diff --git a/database/drivers/mysql/mysql_result.php b/database/drivers/mysql/mysql_result.php new file mode 100644 index 0000000..a28a198 --- /dev/null +++ b/database/drivers/mysql/mysql_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mysql_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mysql_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mysql_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->default = $field->def; + $F->max_length = $field->max_length; + $F->primary_key = $field->primary_key; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + mysql_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mysql_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mysql_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mysql_fetch_object($this->result_id); + } + +} + + +/* End of file mysql_result.php */ +/* Location: ./system/database/drivers/mysql/mysql_result.php */ \ No newline at end of file diff --git a/database/drivers/mysql/mysql_utility.php b/database/drivers/mysql/mysql_utility.php new file mode 100644 index 0000000..d2c10db --- /dev/null +++ b/database/drivers/mysql/mysql_utility.php @@ -0,0 +1,245 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return "REPAIR TABLE ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + /** + * MySQL Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + if (count($params) == 0) + { + return FALSE; + } + + // Extract the prefs for simplicity + extract($params); + + // Build the output + $output = ''; + foreach ((array)$tables as $table) + { + // Is the table in the "ignore" list? + if (in_array($table, (array)$ignore, TRUE)) + { + continue; + } + + // Get the table schema + $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.'.$table); + + // No result means the table name was invalid + if ($query === FALSE) + { + continue; + } + + // Write out the table schema + $output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline; + + if ($add_drop == TRUE) + { + $output .= 'DROP TABLE IF EXISTS '.$table.';'.$newline.$newline; + } + + $i = 0; + $result = $query->result_array(); + foreach ($result[0] as $val) + { + if ($i++ % 2) + { + $output .= $val.';'.$newline.$newline; + } + } + + // If inserts are not needed we're done... + if ($add_insert == FALSE) + { + continue; + } + + // Grab all the data from the current table + $query = $this->db->query("SELECT * FROM $table"); + + if ($query->num_rows() == 0) + { + continue; + } + + // Fetch the field names and determine if the field is an + // integer type. We use this info to decide whether to + // surround the data with quotes or not + + $i = 0; + $field_str = ''; + $is_int = array(); + while ($field = mysql_fetch_field($query->result_id)) + { + // Most versions of MySQL store timestamp as a string + $is_int[$i] = (in_array( + strtolower(mysql_field_type($query->result_id, $i)), + array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'), + TRUE) + ) ? TRUE : FALSE; + + // Create a string of field names + $field_str .= '`'.$field->name.'`, '; + $i++; + } + + // Trim off the end comma + $field_str = preg_replace( "/, $/" , "" , $field_str); + + + // Build the insert string + foreach ($query->result_array() as $row) + { + $val_str = ''; + + $i = 0; + foreach ($row as $v) + { + // Is the value NULL? + if ($v === NULL) + { + $val_str .= 'NULL'; + } + else + { + // Escape the data if it's not an integer + if ($is_int[$i] == FALSE) + { + $val_str .= $this->db->escape($v); + } + else + { + $val_str .= $v; + } + } + + // Append a comma + $val_str .= ', '; + $i++; + } + + // Remove the comma at the end of the string + $val_str = preg_replace( "/, $/" , "" , $val_str); + + // Build the INSERT string + $output .= 'INSERT INTO '.$table.' ('.$field_str.') VALUES ('.$val_str.');'.$newline; + } + + $output .= $newline.$newline; + } + + return $output; + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + +/* End of file mysql_utility.php */ +/* Location: ./system/database/drivers/mysql/mysql_utility.php */ \ No newline at end of file diff --git a/database/drivers/mysqli/index.html b/database/drivers/mysqli/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/mysqli/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/mysqli/mysqli_driver.php b/database/drivers/mysqli/mysqli_driver.php new file mode 100644 index 0000000..4bbe5eb --- /dev/null +++ b/database/drivers/mysqli/mysqli_driver.php @@ -0,0 +1,606 @@ +hostname, $this->username, $this->password, $this->database, $this->port); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return $this->db_connect(); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return @mysqli_select_db($this->conn_id, $this->database); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access private + * @param string + * @param string + * @return resource + */ + function _db_set_charset($charset, $collation) + { + return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + $result = @mysqli_query($this->conn_id, $sql); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + // "DELETE FROM TABLE" returns 0 affected rows This hack modifies + // the query so that it returns the number of affected rows + if ($this->delete_hack === TRUE) + { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('SET AUTOCOMMIT=0'); + $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + if (function_exists('mysqli_real_escape_string') AND is_object($this->conn_id)) + { + return mysqli_real_escape_string($this->conn_id, $str); + } + elseif (function_exists('mysql_escape_string')) + { + return mysql_escape_string($str); + } + else + { + return addslashes($str); + } + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mysqli_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @mysqli_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " LIKE '".$this->dbprefix."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return mysqli_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return mysqli_errno($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $sql .= "LIMIT ".$limit; + + if ($offset > 0) + { + $sql .= " OFFSET ".$offset; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mysqli_close($conn_id); + } + + +} + + +/* End of file mysqli_driver.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_driver.php */ \ No newline at end of file diff --git a/database/drivers/mysqli/mysqli_forge.php b/database/drivers/mysqli/mysqli_forge.php new file mode 100644 index 0000000..c7889bf --- /dev/null +++ b/database/drivers/mysqli/mysqli_forge.php @@ -0,0 +1,254 @@ +$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + if (array_key_exists('NAME', $attributes)) + { + $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; + } + + if (array_key_exists('TYPE', $attributes)) + { + $sql .= ' '.$attributes['TYPE']; + } + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes)) + { + $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param mixed the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + + $sql .= $this->_process_fields($fields); + + if (count($primary_keys) > 0) + { + $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key_name = $this->db->_protect_identifiers(implode('_', $key)); + $key = $this->db->_protect_identifiers($key); + } + else + { + $key_name = $this->db->_protect_identifiers($key); + $key = array($key_name); + } + + $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return string + */ + function _drop_table($table) + { + return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param array fields + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $fields, $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql.$this->db->_protect_identifiers($fields); + } + + $sql .= $this->_process_fields($fields); + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mysqli_forge.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_forge.php */ \ No newline at end of file diff --git a/database/drivers/mysqli/mysqli_result.php b/database/drivers/mysqli/mysqli_result.php new file mode 100644 index 0000000..b690914 --- /dev/null +++ b/database/drivers/mysqli/mysqli_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mysqli_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mysqli_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mysqli_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->default = $field->def; + $F->max_length = $field->max_length; + $F->primary_key = ($field->flags & MYSQLI_PRI_KEY_FLAG) ? 1 : 0; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_object($this->result_id)) + { + mysqli_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mysqli_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mysqli_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mysqli_fetch_object($this->result_id); + } + +} + + +/* End of file mysqli_result.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_result.php */ \ No newline at end of file diff --git a/database/drivers/mysqli/mysqli_utility.php b/database/drivers/mysqli/mysqli_utility.php new file mode 100644 index 0000000..d19b643 --- /dev/null +++ b/database/drivers/mysqli/mysqli_utility.php @@ -0,0 +1,123 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return "REPAIR TABLE ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * MySQLi Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + +/* End of file mysqli_utility.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_utility.php */ \ No newline at end of file diff --git a/database/drivers/oci8/index.html b/database/drivers/oci8/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/oci8/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/oci8/oci8_driver.php b/database/drivers/oci8/oci8_driver.php new file mode 100644 index 0000000..365c9e7 --- /dev/null +++ b/database/drivers/oci8/oci8_driver.php @@ -0,0 +1,726 @@ +username, $this->password, $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @ociplogon($this->username, $this->password, $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return ociserverversion($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + // oracle must parse the query before it is run. All of the actions with + // the query are based on the statement id returned by ociparse + $this->stmt_id = FALSE; + $this->_set_stmt_id($sql); + ocisetprefetch($this->stmt_id, 1000); + return @ociexecute($this->stmt_id, $this->_commit); + } + + /** + * Generate a statement ID + * + * @access private + * @param string an SQL query + * @return none + */ + function _set_stmt_id($sql) + { + if ( ! is_resource($this->stmt_id)) + { + $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql)); + } + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * getCursor. Returns a cursor from the datbase + * + * @access public + * @return cursor id + */ + function get_cursor() + { + $this->curs_id = ocinewcursor($this->conn_id); + return $this->curs_id; + } + + // -------------------------------------------------------------------- + + /** + * Stored Procedure. Executes a stored procedure + * + * @access public + * @param package package stored procedure is in + * @param procedure stored procedure to execute + * @param params array of parameters + * @return array + * + * params array keys + * + * KEY OPTIONAL NOTES + * name no the name of the parameter should be in : format + * value no the value of the parameter. If this is an OUT or IN OUT parameter, + * this should be a reference to a variable + * type yes the type of the parameter + * length yes the max size of the parameter + */ + function stored_procedure($package, $procedure, $params) + { + if ($package == '' OR $procedure == '' OR ! is_array($params)) + { + if ($this->db_debug) + { + log_message('error', 'Invalid query: '.$package.'.'.$procedure); + return $this->display_error('db_invalid_query'); + } + return FALSE; + } + + // build the query string + $sql = "begin $package.$procedure("; + + $have_cursor = FALSE; + foreach($params as $param) + { + $sql .= $param['name'] . ","; + + if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR)) + { + $have_cursor = TRUE; + } + } + $sql = trim($sql, ",") . "); end;"; + + $this->stmt_id = FALSE; + $this->_set_stmt_id($sql); + $this->_bind_params($params); + $this->query($sql, FALSE, $have_cursor); + } + + // -------------------------------------------------------------------- + + /** + * Bind parameters + * + * @access private + * @return none + */ + function _bind_params($params) + { + if ( ! is_array($params) OR ! is_resource($this->stmt_id)) + { + return; + } + + foreach ($params as $param) + { + foreach (array('name', 'value', 'type', 'length') as $val) + { + if ( ! isset($param[$val])) + { + $param[$val] = ''; + } + } + + ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); + } + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->_commit = OCI_DEFAULT; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = OCIcommit($this->conn_id); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = OCIrollback($this->conn_id); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + // Access the CI object + $CI =& get_instance(); + + return $CI->_remove_invisible_characters($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @ocirowcount($this->stmt_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + // not supported in oracle + return $this->display_error('db_unsupported_function'); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query == FALSE) + { + return 0; + } + + $row = $query->row(); + return $row->NUMROWS; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT TABLE_NAME FROM ALL_TABLES"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " WHERE TABLE_NAME LIKE '".$this->dbprefix."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." where rownum = 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + $error = ocierror($this->conn_id); + return $error['message']; + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + $error = ocierror($this->conn_id); + return $error['code']; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE TABLE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $limit = $offset + $limit; + $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)"; + + if ($offset != 0) + { + $newsql .= " WHERE rnum >= $offset"; + } + + // remember that we used limits + $this->limit_used = TRUE; + + return $newsql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @ocilogoff($conn_id); + } + + +} + + + +/* End of file oci8_driver.php */ +/* Location: ./system/database/drivers/oci8/oci8_driver.php */ \ No newline at end of file diff --git a/database/drivers/oci8/oci8_forge.php b/database/drivers/oci8/oci8_forge.php new file mode 100644 index 0000000..4b073d0 --- /dev/null +++ b/database/drivers/oci8/oci8_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tUNIQUE COLUMNS (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file oci8_forge.php */ +/* Location: ./system/database/drivers/oci8/oci8_forge.php */ \ No newline at end of file diff --git a/database/drivers/oci8/oci8_result.php b/database/drivers/oci8/oci8_result.php new file mode 100644 index 0000000..4cfbfa4 --- /dev/null +++ b/database/drivers/oci8/oci8_result.php @@ -0,0 +1,249 @@ +result_array()); + @ociexecute($this->stmt_id); + + if ($this->curs_id) + { + @ociexecute($this->curs_id); + } + + return $rowcount; + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + $count = @ocinumcols($this->stmt_id); + + // if we used a limit we subtract it + if ($this->limit_used) + { + $count = $count - 1; + } + + return $count; + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + $fieldCount = $this->num_fields(); + for ($c = 1; $c <= $fieldCount; $c++) + { + $field_names[] = ocicolumnname($this->stmt_id, $c); + } + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + $fieldCount = $this->num_fields(); + for ($c = 1; $c <= $fieldCount; $c++) + { + $F = new stdClass(); + $F->name = ocicolumnname($this->stmt_id, $c); + $F->type = ocicolumntype($this->stmt_id, $c); + $F->max_length = ocicolumnsize($this->stmt_id, $c); + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + ocifreestatement($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc(&$row) + { + $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; + + return ocifetchinto($id, $row, OCI_ASSOC + OCI_RETURN_NULLS); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + $result = array(); + + // If PHP 5 is being used we can fetch an result object + if (function_exists('oci_fetch_object')) + { + $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; + + return @oci_fetch_object($id); + } + + // If PHP 4 is being used we have to build our own result + foreach ($this->result_array() as $key => $val) + { + $obj = new stdClass(); + if (is_array($val)) + { + foreach ($val as $k => $v) + { + $obj->$k = $v; + } + } + else + { + $obj->$key = $val; + } + + $result[] = $obj; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Query result. "array" version. + * + * @access public + * @return array + */ + function result_array() + { + if (count($this->result_array) > 0) + { + return $this->result_array; + } + + // oracle's fetch functions do not return arrays. + // The information is returned in reference parameters + $row = NULL; + while ($this->_fetch_assoc($row)) + { + $this->result_array[] = $row; + } + + return $this->result_array; + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return FALSE; // Not needed + } + +} + + +/* End of file oci8_result.php */ +/* Location: ./system/database/drivers/oci8/oci8_result.php */ \ No newline at end of file diff --git a/database/drivers/oci8/oci8_utility.php b/database/drivers/oci8/oci8_utility.php new file mode 100644 index 0000000..a3b6d81 --- /dev/null +++ b/database/drivers/oci8/oci8_utility.php @@ -0,0 +1,122 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return FALSE; + } + +} + +/* End of file oci8_utility.php */ +/* Location: ./system/database/drivers/oci8/oci8_utility.php */ \ No newline at end of file diff --git a/database/drivers/odbc/index.html b/database/drivers/odbc/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/odbc/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/odbc/odbc_driver.php b/database/drivers/odbc/odbc_driver.php new file mode 100644 index 0000000..1f03771 --- /dev/null +++ b/database/drivers/odbc/odbc_driver.php @@ -0,0 +1,583 @@ +_random_keyword = ' RND('.time().')'; // database specific random keyword + } + + /** + * Non-persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_connect() + { + return @odbc_connect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @odbc_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Not needed for ODBC + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @odbc_exec($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + return odbc_autocommit($this->conn_id, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = odbc_commit($this->conn_id); + odbc_autocommit($this->conn_id, TRUE); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = odbc_rollback($this->conn_id); + odbc_autocommit($this->conn_id, TRUE); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + // Access the CI object + $CI =& get_instance(); + + // ODBC doesn't require escaping + return $CI->_remove_invisible_characters($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @odbc_num_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @odbc_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM `".$this->database."`"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + //$sql .= " LIKE '".$this->dbprefix."%'"; + return FALSE; // not currently supported + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return odbc_errormsg($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return odbc_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return $this->_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + // Does ODBC doesn't use the LIMIT clause? + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @odbc_close($conn_id); + } + + +} + + + +/* End of file odbc_driver.php */ +/* Location: ./system/database/drivers/odbc/odbc_driver.php */ \ No newline at end of file diff --git a/database/drivers/odbc/odbc_forge.php b/database/drivers/odbc/odbc_forge.php new file mode 100644 index 0000000..099925c --- /dev/null +++ b/database/drivers/odbc/odbc_forge.php @@ -0,0 +1,266 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + // ODBC has no "drop database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file odbc_forge.php */ +/* Location: ./system/database/drivers/odbc/odbc_forge.php */ \ No newline at end of file diff --git a/database/drivers/odbc/odbc_result.php b/database/drivers/odbc/odbc_result.php new file mode 100644 index 0000000..6d6542e --- /dev/null +++ b/database/drivers/odbc/odbc_result.php @@ -0,0 +1,228 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @odbc_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = odbc_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = odbc_field_name($this->result_id, $i); + $F->type = odbc_field_type($this->result_id, $i); + $F->max_length = odbc_field_len($this->result_id, $i); + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + odbc_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + if (function_exists('odbc_fetch_object')) + { + return odbc_fetch_array($this->result_id); + } + else + { + return $this->_odbc_fetch_array($this->result_id); + } + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + if (function_exists('odbc_fetch_object')) + { + return odbc_fetch_object($this->result_id); + } + else + { + return $this->_odbc_fetch_object($this->result_id); + } + } + + + /** + * Result - object + * + * subsititutes the odbc_fetch_object function when + * not available (odbc_fetch_object requires unixODBC) + * + * @access private + * @return object + */ + function _odbc_fetch_object(& $odbc_result) { + $rs = array(); + $rs_obj = false; + if (odbc_fetch_into($odbc_result, $rs)) { + foreach ($rs as $k=>$v) { + $field_name= odbc_field_name($odbc_result, $k+1); + $rs_obj->$field_name = $v; + } + } + return $rs_obj; + } + + + /** + * Result - array + * + * subsititutes the odbc_fetch_array function when + * not available (odbc_fetch_array requires unixODBC) + * + * @access private + * @return array + */ + function _odbc_fetch_array(& $odbc_result) { + $rs = array(); + $rs_assoc = false; + if (odbc_fetch_into($odbc_result, $rs)) { + $rs_assoc=array(); + foreach ($rs as $k=>$v) { + $field_name= odbc_field_name($odbc_result, $k+1); + $rs_assoc[$field_name] = $v; + } + } + return $rs_assoc; + } + +} + + +/* End of file odbc_result.php */ +/* Location: ./system/database/drivers/odbc/odbc_result.php */ \ No newline at end of file diff --git a/database/drivers/odbc/odbc_utility.php b/database/drivers/odbc/odbc_utility.php new file mode 100644 index 0000000..6f4376e --- /dev/null +++ b/database/drivers/odbc/odbc_utility.php @@ -0,0 +1,148 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Generates a platform-specific query so that a table can be optimized + * + * @access private + * @param string the table name + * @return object + */ + function _optimize_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * ODBC Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database() + { + // ODBC has no "create database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + // ODBC has no "drop database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } +} + +/* End of file odbc_utility.php */ +/* Location: ./system/database/drivers/odbc/odbc_utility.php */ \ No newline at end of file diff --git a/database/drivers/postgre/index.html b/database/drivers/postgre/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/postgre/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/postgre/postgre_driver.php b/database/drivers/postgre/postgre_driver.php new file mode 100644 index 0000000..d94cce1 --- /dev/null +++ b/database/drivers/postgre/postgre_driver.php @@ -0,0 +1,625 @@ + 'host', + 'port' => 'port', + 'database' => 'dbname', + 'username' => 'user', + 'password' => 'password' + ); + + $connect_string = ""; + foreach ($components as $key => $val) + { + if (isset($this->$key) && $this->$key != '') + { + $connect_string .= " $val=".$this->$key; + } + } + return trim($connect_string); + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_connect() + { + return @pg_connect($this->_connect_string()); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @pg_pconnect($this->_connect_string()); + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Not needed for Postgre so we'll return TRUE + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @pg_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + return @pg_exec($this->conn_id, "begin"); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return @pg_exec($this->conn_id, "commit"); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return @pg_exec($this->conn_id, "rollback"); + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + return pg_escape_string($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @pg_affected_rows($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + $v = $this->_version(); + $v = $v['server']; + + $table = func_num_args() > 0 ? func_get_arg(0) : null; + $column = func_num_args() > 1 ? func_get_arg(1) : null; + + if ($table == null && $v >= '8.1') + { + $sql='SELECT LASTVAL() as ins_id'; + } + elseif ($table != null && $column != null && $v >= '8.0') + { + $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column); + $query = $this->query($sql); + $row = $query->row(); + $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq); + } + elseif ($table != null) + { + // seq_name passed in table parameter + $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table); + } + else + { + return pg_last_oid($this->result_id); + } + $query = $this->query($sql); + $row = $query->row(); + return $row->ins_id; + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " AND table_name LIKE '".$this->dbprefix."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT column_name FROM information_schema.columns WHERE table_name ='".$table."'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return pg_last_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $sql .= "LIMIT ".$limit; + + if ($offset > 0) + { + $sql .= " OFFSET ".$offset; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @pg_close($conn_id); + } + + +} + + +/* End of file postgre_driver.php */ +/* Location: ./system/database/drivers/postgre/postgre_driver.php */ \ No newline at end of file diff --git a/database/drivers/postgre/postgre_forge.php b/database/drivers/postgre/postgre_forge.php new file mode 100644 index 0000000..5b4bcc2 --- /dev/null +++ b/database/drivers/postgre/postgre_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n);"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + return "DROP TABLE ".$this->db->_escape_identifiers($table)." CASCADE"; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file postgre_forge.php */ +/* Location: ./system/database/drivers/postgre/postgre_forge.php */ \ No newline at end of file diff --git a/database/drivers/postgre/postgre_result.php b/database/drivers/postgre/postgre_result.php new file mode 100644 index 0000000..78b9a60 --- /dev/null +++ b/database/drivers/postgre/postgre_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @pg_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = pg_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = pg_field_name($this->result_id, $i); + $F->type = pg_field_type($this->result_id, $i); + $F->max_length = pg_field_size($this->result_id, $i); + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + pg_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return pg_result_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return pg_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return pg_fetch_object($this->result_id); + } + +} + + +/* End of file postgre_result.php */ +/* Location: ./system/database/drivers/postgre/postgre_result.php */ \ No newline at end of file diff --git a/database/drivers/postgre/postgre_utility.php b/database/drivers/postgre/postgre_utility.php new file mode 100644 index 0000000..f9b0f22 --- /dev/null +++ b/database/drivers/postgre/postgre_utility.php @@ -0,0 +1,124 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + + +} + + +/* End of file postgre_utility.php */ +/* Location: ./system/database/drivers/postgre/postgre_utility.php */ \ No newline at end of file diff --git a/database/drivers/sqlite/index.html b/database/drivers/sqlite/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/drivers/sqlite/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/database/drivers/sqlite/sqlite_driver.php b/database/drivers/sqlite/sqlite_driver.php new file mode 100644 index 0000000..0ba483f --- /dev/null +++ b/database/drivers/sqlite/sqlite_driver.php @@ -0,0 +1,601 @@ +database, FILE_WRITE_MODE, $error)) + { + log_message('error', $error); + + if ($this->db_debug) + { + $this->display_error($error, '', TRUE); + } + + return FALSE; + } + + return $conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ( ! $conn_id = @sqlite_popen($this->database, FILE_WRITE_MODE, $error)) + { + log_message('error', $error); + + if ($this->db_debug) + { + $this->display_error($error, '', TRUE); + } + + return FALSE; + } + + return $conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return sqlite_libversion(); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @sqlite_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('BEGIN TRANSACTION'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @return string + */ + function escape_str($str) + { + return sqlite_escape_string($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return sqlite_changes($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @sqlite_last_insert_rowid($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT name from sqlite_master WHERE type='table'"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " AND 'name' LIKE '".$this->dbprefix."%'"; + } + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + // Not supported + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return sqlite_error_string(sqlite_last_error($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return sqlite_last_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return $this->_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @sqlite_close($conn_id); + } + + +} + + +/* End of file sqlite_driver.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_driver.php */ \ No newline at end of file diff --git a/database/drivers/sqlite/sqlite_forge.php b/database/drivers/sqlite/sqlite_forge.php new file mode 100644 index 0000000..2039525 --- /dev/null +++ b/database/drivers/sqlite/sqlite_forge.php @@ -0,0 +1,265 @@ +db->database) OR ! @unlink($this->db->database)) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unable_to_drop'); + } + return FALSE; + } + return TRUE; + } + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + // IF NOT EXISTS added to SQLite in 3.3.0 + if ($if_not_exists === TRUE && version_compare($this->_version(), '3.3.0', '>=') === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)."("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * Unsupported feature in SQLite + * + * @access private + * @return bool + */ + function _drop_table($table) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return array(); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + // SQLite does not support dropping columns + // http://www.sqlite.org/omitted.html + // http://www.sqlite.org/faq.html#q11 + return FALSE; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } +} + +/* End of file sqlite_forge.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_forge.php */ \ No newline at end of file diff --git a/database/drivers/sqlite/sqlite_result.php b/database/drivers/sqlite/sqlite_result.php new file mode 100644 index 0000000..735a073 --- /dev/null +++ b/database/drivers/sqlite/sqlite_result.php @@ -0,0 +1,179 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @sqlite_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = sqlite_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = sqlite_field_name($this->result_id, $i); + $F->type = 'varchar'; + $F->max_length = 0; + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + // Not implemented in SQLite + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return sqlite_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return sqlite_fetch_array($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + if (function_exists('sqlite_fetch_object')) + { + return sqlite_fetch_object($this->result_id); + } + else + { + $arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC); + if (is_array($arr)) + { + $obj = (object) $arr; + return $obj; + } else { + return NULL; + } + } + } + +} + + +/* End of file sqlite_result.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_result.php */ \ No newline at end of file diff --git a/database/drivers/sqlite/sqlite_utility.php b/database/drivers/sqlite/sqlite_utility.php new file mode 100644 index 0000000..3527b5b --- /dev/null +++ b/database/drivers/sqlite/sqlite_utility.php @@ -0,0 +1,141 @@ +db_debug) + { + return $this->display_error('db_unsuported_feature'); + } + return array(); + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Is optimization even supported in SQLite? + * + * @access private + * @param string the table name + * @return object + */ + function _optimize_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Are table repairs even supported in SQLite? + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * SQLite Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function _create_database() + { + // In SQLite, a database is created when you connect to the database. + // We'll return TRUE so that an error isn't generated + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + if ( ! @file_exists($this->db->database) OR ! @unlink($this->db->database)) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unable_to_drop'); + } + return FALSE; + } + return TRUE; + } + +} + +/* End of file sqlite_utility.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_utility.php */ \ No newline at end of file diff --git a/database/index.html b/database/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/database/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/fonts/index.html b/fonts/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/fonts/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/fonts/texb.ttf b/fonts/texb.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a7aa6469ac968cadb32438bf01e364c300d4ba6b GIT binary patch literal 143821 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 literal 0 HcmV?d00001 diff --git a/helpers/array_helper.php b/helpers/array_helper.php new file mode 100644 index 0000000..4c8323b --- /dev/null +++ b/helpers/array_helper.php @@ -0,0 +1,78 @@ + 0) // 8 = FILE_APPEND flag + { + $mode = FOPEN_WRITE_CREATE; + } + else + { + $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE; + } + + // Check if we're using the include path + if (($flags & 1) > 0) // 1 = FILE_USE_INCLUDE_PATH flag + { + $use_include_path = TRUE; + } + else + { + $use_include_path = FALSE; + } + + $fp = @fopen($filename, $mode, $use_include_path); + + if ($fp === FALSE) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') failed to open stream', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if (($flags & LOCK_EX) > 0) + { + if ( ! flock($fp, LOCK_EX)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') unable to acquire an exclusive lock on file', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + } + + // write it + if (($written = @fwrite($fp, $data)) === FALSE) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') failed to write to '.htmlentities($filename), $backtrace[0]['file'], $backtrace[0]['line']); + } + + // Close the handle + @fclose($fp); + + // Return length + return $written; + } +} + +// ------------------------------------------------------------------------ + +/** + * fputcsv() + * + * Format line as CSV and write to file pointer + * http://us.php.net/manual/en/function.fputcsv.php + * + * @access public + * @param resource file pointer + * @param array data to be written + * @param string delimiter + * @param string enclosure + * @return int length of written string + */ +if ( ! function_exists('fputcsv')) +{ + function fputcsv($handle, $fields, $delimiter = ',', $enclosure = '"') + { + // Checking for a handle resource + if ( ! is_resource($handle)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 1 to be stream resource, '.gettype($handle).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // OK, it is a resource, but is it a stream? + if (get_resource_type($handle) !== 'stream') + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 1 to be stream resource, '.get_resource_type($handle).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // Checking for an array of fields + if ( ! is_array($fields)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 2 to be array, '.gettype($fields).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // validate delimiter + if (strlen($delimiter) > 1) + { + $delimiter = substr($delimiter, 0, 1); + $backtrace = debug_backtrace(); + _exception_handler(E_NOTICE, 'fputcsv() delimiter must be one character long, "'.htmlentities($delimiter).'" used', $backtrace[0]['file'], $backtrace[0]['line']); + } + + // validate enclosure + if (strlen($enclosure) > 1) + { + $enclosure = substr($enclosure, 0, 1); + $backtrace = debug_backtrace(); + _exception_handler(E_NOTICE, 'fputcsv() enclosure must be one character long, "'.htmlentities($enclosure).'" used', $backtrace[0]['file'], $backtrace[0]['line']); + + } + + $out = ''; + + foreach ($fields as $cell) + { + $cell = str_replace($enclosure, $enclosure.$enclosure, $cell); + + if (strpos($cell, $delimiter) !== FALSE OR strpos($cell, $enclosure) !== FALSE OR strpos($cell, "\n") !== FALSE) + { + $out .= $enclosure.$cell.$enclosure.$delimiter; + } + else + { + $out .= $cell.$delimiter; + } + } + + $length = @fwrite($handle, substr($out, 0, -1)."\n"); + + return $length; + } +} + +// ------------------------------------------------------------------------ + +/** + * stripos() + * + * Find position of first occurrence of a case-insensitive string + * http://us.php.net/manual/en/function.stripos.php + * + * @access public + * @param string haystack + * @param string needle + * @param int offset + * @return int numeric position of the first occurrence of needle in the haystack + */ +if ( ! function_exists('stripos')) +{ + function stripos($haystack, $needle, $offset = NULL) + { + // Cast non string scalar values + if (is_scalar($haystack)) + { + settype($haystack, 'STRING'); + } + + if ( ! is_string($haystack)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() expects parameter 1 to be string, '.gettype($haystack).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if ( ! is_scalar($needle)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() needle is not a string or an integer in '.$backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if (is_float($offset)) + { + $offset = (int)$offset; + } + + if ( ! is_int($offset) && ! is_bool($offset) && ! is_null($offset)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() expects parameter 3 to be long, '.gettype($offset).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return NULL; + } + + return strpos(strtolower($haystack), strtolower($needle), $offset); + } +} + +// ------------------------------------------------------------------------ + +/** + * str_ireplace() + * + * Find position of first occurrence of a case-insensitive string + * http://us.php.net/manual/en/function.str-ireplace.php + * (parameter 4, $count, is not supported as to do so in PHP 4 would make + * it a required parameter) + * + * @access public + * @param mixed search + * @param mixed replace + * @param mixed subject + * @return int numeric position of the first occurrence of needle in the haystack + */ +if ( ! function_exists('str_ireplace')) +{ + function str_ireplace($search, $replace, $subject) + { + // Nothing to do here + if ($search === NULL OR $subject === NULL) + { + return $subject; + } + + // Crazy arguments + if (is_scalar($search) && is_array($replace)) + { + $backtrace = debug_backtrace(); + + if (is_object($replace)) + { + show_error('Object of class '.get_class($replace).' could not be converted to string in '.$backtrace[0]['file'].' on line '.$backtrace[0]['line']); + } + else + { + _exception_handler(E_USER_NOTICE, 'Array to string conversion in '.$backtrace[0]['file'], $backtrace[0]['line']); + } + } + + // Searching for an array + if (is_array($search)) + { + // Replacing with an array + if (is_array($replace)) + { + $search = array_values($search); + $replace = array_values($replace); + + if (count($search) >= count($replace)) + { + $replace = array_pad($replace, count($search), ''); + } + else + { + $replace = array_slice($replace, 0, count($search)); + } + } + else + { + // Replacing with a string all positions + $replace = array_fill(0, count($search), $replace); + } + } + else + { + //Searching for a string and replacing with a string. + $search = array((string)$search); + $replace = array((string)$replace); + } + + // Prepare the search array + foreach ($search as $search_key => $search_value) + { + $search[$search_key] = '/'.preg_quote($search_value, '/').'/i'; + } + + // Prepare the replace array (escape backreferences) + foreach ($replace as $k => $v) + { + $replace[$k] = str_replace(array(chr(92), '$'), array(chr(92).chr(92), '\$'), $v); + } + + // do the replacement + $result = preg_replace($search, $replace, (array)$subject); + + // Check if subject was initially a string and return it as a string + if ( ! is_array($subject)) + { + return current($result); + } + + // Otherwise, just return the array + return $result; + } +} + +// ------------------------------------------------------------------------ + +/** + * http_build_query() + * + * Generate URL-encoded query string + * http://us.php.net/manual/en/function.http-build-query.php + * + * @access public + * @param array form data + * @param string numeric prefix + * @param string argument separator + * @return string URL-encoded string + */ +if ( ! function_exists('http_build_query')) +{ + function http_build_query($formdata, $numeric_prefix = NULL, $separator = NULL) + { + // Check the data + if ( ! is_array($formdata) && ! is_object($formdata)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'http_build_query() Parameter 1 expected to be Array or Object. Incorrect value given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // Cast it as array + if (is_object($formdata)) + { + $formdata = get_object_vars($formdata); + } + + // If the array is empty, return NULL + if (empty($formdata)) + { + return NULL; + } + + // Argument separator + if ($separator === NULL) + { + $separator = ini_get('arg_separator.output'); + + if (strlen($separator) == 0) + { + $separator = '&'; + } + } + + // Start building the query + $tmp = array(); + + foreach ($formdata as $key => $val) + { + if ($val === NULL) + { + continue; + } + + if (is_integer($key) && $numeric_prefix != NULL) + { + $key = $numeric_prefix.$key; + } + + if (is_resource($val)) + { + return NULL; + } + + // hand it off to a recursive parser + $tmp[] = _http_build_query_helper($key, $val, $separator); + } + + return implode($separator, $tmp); + } + + + // Helper helper. Remind anyone of college? + // Required to handle recursion in nested arrays. + // + // You could shave fractions of fractions of a second by moving where + // the urlencoding takes place, but it's much less intuitive, and if + // your application has 10,000 form fields, well, you have other problems ;) + function _http_build_query_helper($key, $val, $separator = '&') + { + if (is_scalar($val)) + { + return urlencode($key).'='.urlencode($val); + } + else + { + // arrays please + if (is_object($val)) + { + $val = get_object_vars($val); + } + + foreach ($val as $k => $v) + { + $tmp[] = _http_build_query_helper($key.'['.$k.']', $v, $separator); + } + } + + return implode($separator, $tmp); + } +} + + +/* End of file compatibility_helper.php */ +/* Location: ./system/helpers/compatibility_helper.php */ \ No newline at end of file diff --git a/helpers/cookie_helper.php b/helpers/cookie_helper.php new file mode 100644 index 0000000..61561b7 --- /dev/null +++ b/helpers/cookie_helper.php @@ -0,0 +1,136 @@ +config->item('cookie_prefix') != '') + { + $prefix = $CI->config->item('cookie_prefix'); + } + if ($domain == '' AND $CI->config->item('cookie_domain') != '') + { + $domain = $CI->config->item('cookie_domain'); + } + if ($path == '/' AND $CI->config->item('cookie_path') != '/') + { + $path = $CI->config->item('cookie_path'); + } + + if ( ! is_numeric($expire)) + { + $expire = time() - 86500; + } + else + { + if ($expire > 0) + { + $expire = time() + $expire; + } + else + { + $expire = 0; + } + } + + setcookie($prefix.$name, $value, $expire, $path, $domain, 0); + } +} + +// -------------------------------------------------------------------- + +/** + * Fetch an item from the COOKIE array + * + * @access public + * @param string + * @param bool + * @return mixed + */ +if ( ! function_exists('get_cookie')) +{ + function get_cookie($index = '', $xss_clean = FALSE) + { + $CI =& get_instance(); + return $CI->input->cookie($index, $xss_clean); + } +} + +// -------------------------------------------------------------------- + +/** + * Delete a COOKIE + * + * @param mixed + * @param string the cookie domain. Usually: .yourdomain.com + * @param string the cookie path + * @param string the cookie prefix + * @return void + */ +if ( ! function_exists('delete_cookie')) +{ + function delete_cookie($name = '', $domain = '', $path = '/', $prefix = '') + { + set_cookie($name, '', '', $domain, $path, $prefix); + } +} + + +/* End of file cookie_helper.php */ +/* Location: ./system/helpers/cookie_helper.php */ \ No newline at end of file diff --git a/helpers/date_helper.php b/helpers/date_helper.php new file mode 100644 index 0000000..c9c8968 --- /dev/null +++ b/helpers/date_helper.php @@ -0,0 +1,611 @@ +config->item('time_reference')) == 'gmt') + { + $now = time(); + $system_time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now)); + + if (strlen($system_time) < 10) + { + $system_time = time(); + log_message('error', 'The Date class could not set a proper GMT timestamp so the local time() value was used.'); + } + + return $system_time; + } + else + { + return time(); + } + } +} + +// ------------------------------------------------------------------------ + +/** + * Convert MySQL Style Datecodes + * + * This function is identical to PHPs date() function, + * except that it allows date codes to be formatted using + * the MySQL style, where each code letter is preceded + * with a percent sign: %Y %m %d etc... + * + * The benefit of doing dates this way is that you don't + * have to worry about escaping your text letters that + * match the date codes. + * + * @access public + * @param string + * @param integer + * @return integer + */ +if ( ! function_exists('mdate')) +{ + function mdate($datestr = '', $time = '') + { + if ($datestr == '') + return ''; + + if ($time == '') + $time = now(); + + $datestr = str_replace('%\\', '', preg_replace("/([a-z]+?){1}/i", "\\\\\\1", $datestr)); + return date($datestr, $time); + } +} + +// ------------------------------------------------------------------------ + +/** + * Standard Date + * + * Returns a date formatted according to the submitted standard. + * + * @access public + * @param string the chosen format + * @param integer Unix timestamp + * @return string + */ +if ( ! function_exists('standard_date')) +{ + function standard_date($fmt = 'DATE_RFC822', $time = '') + { + $formats = array( + 'DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q', + 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC', + 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%O', + 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O', + 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC', + 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O', + 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O', + 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O', + 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q' + ); + + if ( ! isset($formats[$fmt])) + { + return FALSE; + } + + return mdate($formats[$fmt], $time); + } +} + +// ------------------------------------------------------------------------ + +/** + * Timespan + * + * Returns a span of seconds in this format: + * 10 days 14 hours 36 minutes 47 seconds + * + * @access public + * @param integer a number of seconds + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('timespan')) +{ + function timespan($seconds = 1, $time = '') + { + $CI =& get_instance(); + $CI->lang->load('date'); + + if ( ! is_numeric($seconds)) + { + $seconds = 1; + } + + if ( ! is_numeric($time)) + { + $time = time(); + } + + if ($time <= $seconds) + { + $seconds = 1; + } + else + { + $seconds = $time - $seconds; + } + + $str = ''; + $years = floor($seconds / 31536000); + + if ($years > 0) + { + $str .= $years.' '.$CI->lang->line((($years > 1) ? 'date_years' : 'date_year')).', '; + } + + $seconds -= $years * 31536000; + $months = floor($seconds / 2628000); + + if ($years > 0 OR $months > 0) + { + if ($months > 0) + { + $str .= $months.' '.$CI->lang->line((($months > 1) ? 'date_months' : 'date_month')).', '; + } + + $seconds -= $months * 2628000; + } + + $weeks = floor($seconds / 604800); + + if ($years > 0 OR $months > 0 OR $weeks > 0) + { + if ($weeks > 0) + { + $str .= $weeks.' '.$CI->lang->line((($weeks > 1) ? 'date_weeks' : 'date_week')).', '; + } + + $seconds -= $weeks * 604800; + } + + $days = floor($seconds / 86400); + + if ($months > 0 OR $weeks > 0 OR $days > 0) + { + if ($days > 0) + { + $str .= $days.' '.$CI->lang->line((($days > 1) ? 'date_days' : 'date_day')).', '; + } + + $seconds -= $days * 86400; + } + + $hours = floor($seconds / 3600); + + if ($days > 0 OR $hours > 0) + { + if ($hours > 0) + { + $str .= $hours.' '.$CI->lang->line((($hours > 1) ? 'date_hours' : 'date_hour')).', '; + } + + $seconds -= $hours * 3600; + } + + $minutes = floor($seconds / 60); + + if ($days > 0 OR $hours > 0 OR $minutes > 0) + { + if ($minutes > 0) + { + $str .= $minutes.' '.$CI->lang->line((($minutes > 1) ? 'date_minutes' : 'date_minute')).', '; + } + + $seconds -= $minutes * 60; + } + + if ($str == '') + { + $str .= $seconds.' '.$CI->lang->line((($seconds > 1) ? 'date_seconds' : 'date_second')).', '; + } + + return substr(trim($str), 0, -1); + } +} + +// ------------------------------------------------------------------------ + +/** + * Number of days in a month + * + * Takes a month/year as input and returns the number of days + * for the given month/year. Takes leap years into consideration. + * + * @access public + * @param integer a numeric month + * @param integer a numeric year + * @return integer + */ +if ( ! function_exists('days_in_month')) +{ + function days_in_month($month = 0, $year = '') + { + if ($month < 1 OR $month > 12) + { + return 0; + } + + if ( ! is_numeric($year) OR strlen($year) != 4) + { + $year = date('Y'); + } + + if ($month == 2) + { + if ($year % 400 == 0 OR ($year % 4 == 0 AND $year % 100 != 0)) + { + return 29; + } + } + + $days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); + return $days_in_month[$month - 1]; + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts a local Unix timestamp to GMT + * + * @access public + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('local_to_gmt')) +{ + function local_to_gmt($time = '') + { + if ($time == '') + $time = time(); + + return mktime( gmdate("H", $time), gmdate("i", $time), gmdate("s", $time), gmdate("m", $time), gmdate("d", $time), gmdate("Y", $time)); + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts GMT time to a localized value + * + * Takes a Unix timestamp (in GMT) as input, and returns + * at the local value based on the timezone and DST setting + * submitted + * + * @access public + * @param integer Unix timestamp + * @param string timezone + * @param bool whether DST is active + * @return integer + */ +if ( ! function_exists('gmt_to_local')) +{ + function gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE) + { + if ($time == '') + { + return now(); + } + + $time += timezones($timezone) * 3600; + + if ($dst == TRUE) + { + $time += 3600; + } + + return $time; + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts a MySQL Timestamp to Unix + * + * @access public + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('mysql_to_unix')) +{ + function mysql_to_unix($time = '') + { + // We'll remove certain characters for backward compatibility + // since the formatting changed with MySQL 4.1 + // YYYY-MM-DD HH:MM:SS + + $time = str_replace('-', '', $time); + $time = str_replace(':', '', $time); + $time = str_replace(' ', '', $time); + + // YYYYMMDDHHMMSS + return mktime( + substr($time, 8, 2), + substr($time, 10, 2), + substr($time, 12, 2), + substr($time, 4, 2), + substr($time, 6, 2), + substr($time, 0, 4) + ); + } +} + +// ------------------------------------------------------------------------ + +/** + * Unix to "Human" + * + * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM + * + * @access public + * @param integer Unix timestamp + * @param bool whether to show seconds + * @param string format: us or euro + * @return string + */ +if ( ! function_exists('unix_to_human')) +{ + function unix_to_human($time = '', $seconds = FALSE, $fmt = 'us') + { + $r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' '; + + if ($fmt == 'us') + { + $r .= date('h', $time).':'.date('i', $time); + } + else + { + $r .= date('H', $time).':'.date('i', $time); + } + + if ($seconds) + { + $r .= ':'.date('s', $time); + } + + if ($fmt == 'us') + { + $r .= ' '.date('A', $time); + } + + return $r; + } +} + +// ------------------------------------------------------------------------ + +/** + * Convert "human" date to GMT + * + * Reverses the above process + * + * @access public + * @param string format: us or euro + * @return integer + */ +if ( ! function_exists('human_to_unix')) +{ + function human_to_unix($datestr = '') + { + if ($datestr == '') + { + return FALSE; + } + + $datestr = trim($datestr); + $datestr = preg_replace("/\040+/", "\040", $datestr); + + if ( ! preg_match('/^[0-9]{2,4}\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr)) + { + return FALSE; + } + + $split = preg_split("/\040/", $datestr); + + $ex = explode("-", $split['0']); + + $year = (strlen($ex['0']) == 2) ? '20'.$ex['0'] : $ex['0']; + $month = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; + $day = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; + + $ex = explode(":", $split['1']); + + $hour = (strlen($ex['0']) == 1) ? '0'.$ex['0'] : $ex['0']; + $min = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; + + if (isset($ex['2']) AND ereg("[0-9]{1,2}", $ex['2'])) + { + $sec = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; + } + else + { + // Unless specified, seconds get set to zero. + $sec = '00'; + } + + if (isset($split['2'])) + { + $ampm = strtolower($split['2']); + + if (substr($ampm, 0, 1) == 'p' AND $hour < 12) + $hour = $hour + 12; + + if (substr($ampm, 0, 1) == 'a' AND $hour == 12) + $hour = '00'; + + if (strlen($hour) == 1) + $hour = '0'.$hour; + } + + return mktime($hour, $min, $sec, $month, $day, $year); + } +} + +// ------------------------------------------------------------------------ + +/** + * Timezone Menu + * + * Generates a drop-down menu of timezones. + * + * @access public + * @param string timezone + * @param string classname + * @param string menu name + * @return string + */ +if ( ! function_exists('timezone_menu')) +{ + function timezone_menu($default = 'UTC', $class = "", $name = 'timezones') + { + $CI =& get_instance(); + $CI->lang->load('date'); + + if ($default == 'GMT') + $default = 'UTC'; + + $menu = '"; + + return $menu; + } +} + +// ------------------------------------------------------------------------ + +/** + * Timezones + * + * Returns an array of timezones. This is a helper function + * for various other ones in this library + * + * @access public + * @param string timezone + * @return string + */ +if ( ! function_exists('timezones')) +{ + function timezones($tz = '') + { + // Note: Don't change the order of these even though + // some items appear to be in the wrong order + + $zones = array( + 'UM12' => -12, + 'UM11' => -11, + 'UM10' => -10, + 'UM95' => -9.5, + 'UM9' => -9, + 'UM8' => -8, + 'UM7' => -7, + 'UM6' => -6, + 'UM5' => -5, + 'UM45' => -4.5, + 'UM4' => -4, + 'UM35' => -3.5, + 'UM3' => -3, + 'UM2' => -2, + 'UM1' => -1, + 'UTC' => 0, + 'UP1' => +1, + 'UP2' => +2, + 'UP3' => +3, + 'UP35' => +3.5, + 'UP4' => +4, + 'UP45' => +4.5, + 'UP5' => +5, + 'UP55' => +5.5, + 'UP575' => +5.75, + 'UP6' => +6, + 'UP65' => +6.5, + 'UP7' => +7, + 'UP8' => +8, + 'UP875' => +8.75, + 'UP9' => +9, + 'UP95' => +9.5, + 'UP10' => +10, + 'UP105' => +10.5, + 'UP11' => +11, + 'UP115' => +11.5, + 'UP12' => +12, + 'UP1275' => +12.75, + 'UP13' => +13, + 'UP14' => +14 + ); + + if ($tz == '') + { + return $zones; + } + + if ($tz == 'GMT') + $tz = 'UTC'; + + return ( ! isset($zones[$tz])) ? 0 : $zones[$tz]; + } +} + + +/* End of file date_helper.php */ +/* Location: ./system/helpers/date_helper.php */ \ No newline at end of file diff --git a/helpers/directory_helper.php b/helpers/directory_helper.php new file mode 100644 index 0000000..8a1b999 --- /dev/null +++ b/helpers/directory_helper.php @@ -0,0 +1,80 @@ + 0) + { + $data =& fread($fp, filesize($file)); + } + + flock($fp, LOCK_UN); + fclose($fp); + + return $data; + } +} + +// ------------------------------------------------------------------------ + +/** + * Write File + * + * Writes data to the file specified in the path. + * Creates a new file if non-existent. + * + * @access public + * @param string path to file + * @param string file data + * @return bool + */ +if ( ! function_exists('write_file')) +{ + function write_file($path, $data, $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE) + { + if ( ! $fp = @fopen($path, $mode)) + { + return FALSE; + } + + flock($fp, LOCK_EX); + fwrite($fp, $data); + flock($fp, LOCK_UN); + fclose($fp); + + return TRUE; + } +} + +// ------------------------------------------------------------------------ + +/** + * Delete Files + * + * Deletes all files contained in the supplied directory path. + * Files must be writable or owned by the system in order to be deleted. + * If the second parameter is set to TRUE, any directories contained + * within the supplied base directory will be nuked as well. + * + * @access public + * @param string path to file + * @param bool whether to delete any directories found in the path + * @return bool + */ +if ( ! function_exists('delete_files')) +{ + function delete_files($path, $del_dir = FALSE, $level = 0) + { + // Trim the trailing slash + $path = preg_replace("|^(.+?)/*$|", "\\1", $path); + + if ( ! $current_dir = @opendir($path)) + return; + + while(FALSE !== ($filename = @readdir($current_dir))) + { + if ($filename != "." and $filename != "..") + { + if (is_dir($path.'/'.$filename)) + { + // Ignore empty folders + if (substr($filename, 0, 1) != '.') + { + delete_files($path.'/'.$filename, $del_dir, $level + 1); + } + } + else + { + unlink($path.'/'.$filename); + } + } + } + @closedir($current_dir); + + if ($del_dir == TRUE AND $level > 0) + { + @rmdir($path); + } + } +} + +// ------------------------------------------------------------------------ + +/** + * Get Filenames + * + * Reads the specified directory and builds an array containing the filenames. + * Any sub-folders contained within the specified path are read as well. + * + * @access public + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ +if ( ! function_exists('get_filenames')) +{ + function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + static $_filedata = array(); + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0) + { + get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif (strncmp($file, '.', 1) !== 0) + { + + $_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file; + } + } + return $_filedata; + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @access public + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ +if ( ! function_exists('get_dir_file_info')) +{ + function get_dir_file_info($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + $_filedata = array(); + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0) + { + get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif (strncmp($file, '.', 1) !== 0) + { + $_filedata[$file] = get_file_info($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + return $_filedata; + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** +* Get File Info +* +* Given a file and path, returns the name, path, size, date modified +* Second parameter allows you to explicitly declare what information you want returned +* Options are: name, server_path, size, date, readable, writable, executable, fileperms +* Returns FALSE if the file cannot be found. +* +* @access public +* @param string path to file +* @param mixed array or comma separated string of information returned +* @return array +*/ +if ( ! function_exists('get_file_info')) +{ + function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date')) + { + + if ( ! file_exists($file)) + { + return FALSE; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = substr(strrchr($file, '/'), 1); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filectime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + // There are known problems using is_weritable on IIS. It may not be reliable - consider fileperms() + $fileinfo['writable'] = is_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } +} + +// -------------------------------------------------------------------- + +/** + * Get Mime by Extension + * + * Translates a file extension into a mime type based on config/mimes.php. + * Returns FALSE if it can't determine the type, or open the mime config file + * + * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience + * It should NOT be trusted, and should certainly NOT be used for security + * + * @access public + * @param string path to file + * @return mixed + */ +if ( ! function_exists('get_mime_by_extension')) +{ + function get_mime_by_extension($file) + { + $extension = substr(strrchr($file, '.'), 1); + + global $mimes; + + if ( ! is_array($mimes)) + { + if ( ! require_once(APPPATH.'config/mimes.php')) + { + return FALSE; + } + } + + if (array_key_exists($extension, $mimes)) + { + if (is_array($mimes[$extension])) + { + // Multiple mime types, just give the first one + return current($mimes[$extension]); + } + else + { + return $mimes[$extension]; + } + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** + * Symbolic Permissions + * + * Takes a numeric value representing a file's permissions and returns + * standard symbolic notation representing that value + * + * @access public + * @param int + * @return string + */ +if ( ! function_exists('symbolic_permissions')) +{ + function symbolic_permissions($perms) + { + if (($perms & 0xC000) == 0xC000) + { + $symbolic = 's'; // Socket + } + elseif (($perms & 0xA000) == 0xA000) + { + $symbolic = 'l'; // Symbolic Link + } + elseif (($perms & 0x8000) == 0x8000) + { + $symbolic = '-'; // Regular + } + elseif (($perms & 0x6000) == 0x6000) + { + $symbolic = 'b'; // Block special + } + elseif (($perms & 0x4000) == 0x4000) + { + $symbolic = 'd'; // Directory + } + elseif (($perms & 0x2000) == 0x2000) + { + $symbolic = 'c'; // Character special + } + elseif (($perms & 0x1000) == 0x1000) + { + $symbolic = 'p'; // FIFO pipe + } + else + { + $symbolic = 'u'; // Unknown + } + + // Owner + $symbolic .= (($perms & 0x0100) ? 'r' : '-'); + $symbolic .= (($perms & 0x0080) ? 'w' : '-'); + $symbolic .= (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); + + // Group + $symbolic .= (($perms & 0x0020) ? 'r' : '-'); + $symbolic .= (($perms & 0x0010) ? 'w' : '-'); + $symbolic .= (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); + + // World + $symbolic .= (($perms & 0x0004) ? 'r' : '-'); + $symbolic .= (($perms & 0x0002) ? 'w' : '-'); + $symbolic .= (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); + + return $symbolic; + } +} + +// -------------------------------------------------------------------- + +/** + * Octal Permissions + * + * Takes a numeric value representing a file's permissions and returns + * a three character string representing the file's octal permissions + * + * @access public + * @param int + * @return string + */ +if ( ! function_exists('octal_permissions')) +{ + function octal_permissions($perms) + { + return substr(sprintf('%o', $perms), -3); + } +} + + +/* End of file file_helper.php */ +/* Location: ./system/helpers/file_helper.php */ \ No newline at end of file diff --git a/helpers/form_helper.php b/helpers/form_helper.php new file mode 100644 index 0000000..109d8c1 --- /dev/null +++ b/helpers/form_helper.php @@ -0,0 +1,963 @@ +config->site_url($action) : $action; + + $form = '

0) + { + $form .= form_hidden($hidden); + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Declaration - Multipart type + * + * Creates the opening portion of the form, but with "multipart/form-data". + * + * @access public + * @param string the URI segments of the form destination + * @param array a key/value pair of attributes + * @param array a key/value pair hidden data + * @return string + */ +if ( ! function_exists('form_open_multipart')) +{ + function form_open_multipart($action, $attributes = array(), $hidden = array()) + { + $attributes['enctype'] = 'multipart/form-data'; + return form_open($action, $attributes, $hidden); + } +} + +// ------------------------------------------------------------------------ + +/** + * Hidden Input Field + * + * Generates hidden fields. You can pass a simple key/value string or an associative + * array with multiple values. + * + * @access public + * @param mixed + * @param string + * @return string + */ +if ( ! function_exists('form_hidden')) +{ + function form_hidden($name, $value = '') + { + if ( ! is_array($name)) + { + return ''; + } + + $form = ''; + + foreach ($name as $name => $value) + { + $form .= "\n"; + $form .= ''; + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Text Input Field + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_input')) +{ + function form_input($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'text', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Password Field + * + * Identical to the input function but adds the "password" type + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_password')) +{ + function form_password($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'password'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Upload Field + * + * Identical to the input function but adds the "file" type + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_upload')) +{ + function form_upload($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'file'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Textarea field + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_textarea')) +{ + function form_textarea($data = '', $value = '', $extra = '') + { + $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '90', 'rows' => '12'); + + if ( ! is_array($data) OR ! isset($data['value'])) + { + $val = $value; + } + else + { + $val = $data['value']; + unset($data['value']); // textareas don't use the value attribute + } + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Drop-down Menu + * + * @access public + * @param string + * @param array + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_dropdown')) +{ + function form_dropdown($name = '', $options = array(), $selected = array(), $extra = '') + { + if ( ! is_array($selected)) + { + $selected = array($selected); + } + + // If no selected state was submitted we will attempt to set it automatically + if (count($selected) === 0) + { + // If the form name appears in the $_POST array we have a winner! + if (isset($_POST[$name])) + { + $selected = array($_POST[$name]); + } + } + + if ($extra != '') $extra = ' '.$extra; + + $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + + $form = ''; + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Checkbox Field + * + * @access public + * @param mixed + * @param string + * @param bool + * @param string + * @return string + */ +if ( ! function_exists('form_checkbox')) +{ + function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') + { + $defaults = array('type' => 'checkbox', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + if (is_array($data) AND array_key_exists('checked', $data)) + { + $checked = $data['checked']; + + if ($checked == FALSE) + { + unset($data['checked']); + } + else + { + $data['checked'] = 'checked'; + } + } + + if ($checked == TRUE) + { + $defaults['checked'] = 'checked'; + } + else + { + unset($defaults['checked']); + } + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Radio Button + * + * @access public + * @param mixed + * @param string + * @param bool + * @param string + * @return string + */ +if ( ! function_exists('form_radio')) +{ + function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'radio'; + return form_checkbox($data, $value, $checked, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Submit Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_submit')) +{ + function form_submit($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'submit', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Reset Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_reset')) +{ + function form_reset($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'reset', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_button')) +{ + function form_button($data = '', $content = '', $extra = '') + { + $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'type' => 'submit'); + + if ( is_array($data) AND isset($data['content'])) + { + $content = $data['content']; + unset($data['content']); // content is not an attribute + } + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Label Tag + * + * @access public + * @param string The text to appear onscreen + * @param string The id the label applies to + * @param string Additional attributes + * @return string + */ +if ( ! function_exists('form_label')) +{ + function form_label($label_text = '', $id = '', $attributes = array()) + { + + $label = ' 0) + { + foreach ($attributes as $key => $val) + { + $label .= ' '.$key.'="'.$val.'"'; + } + } + + $label .= ">$label_text"; + + return $label; + } +} + +// ------------------------------------------------------------------------ +/** + * Fieldset Tag + * + * Used to produce
text. To close fieldset + * use form_fieldset_close() + * + * @access public + * @param string The legend text + * @param string Additional attributes + * @return string + */ +if ( ! function_exists('form_fieldset')) +{ + function form_fieldset($legend_text = '', $attributes = array()) + { + $fieldset = "".$extra; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Close Tag + * + * @access public + * @param string + * @return string + */ +if ( ! function_exists('form_close')) +{ + function form_close($extra = '') + { + return "".$extra; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Prep + * + * Formats text so that it can be safely placed in a form field in the event it has HTML tags. + * + * @access public + * @param string + * @return string + */ +if ( ! function_exists('form_prep')) +{ + function form_prep($str = '') + { + // if the field name is an array we do this recursively + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = form_prep($val); + } + + return $str; + } + + if ($str === '') + { + return ''; + } + + $temp = '__TEMP_AMPERSANDS__'; + + // Replace entities to temporary markers so that + // htmlspecialchars won't mess them up + $str = preg_replace("/&#(\d+);/", "$temp\\1;", $str); + $str = preg_replace("/&(\w+);/", "$temp\\1;", $str); + + $str = htmlspecialchars($str); + + // In case htmlspecialchars misses these. + $str = str_replace(array("'", '"'), array("'", """), $str); + + // Decode the temp markers back to entities + $str = preg_replace("/$temp(\d+);/","&#\\1;",$str); + $str = preg_replace("/$temp(\w+);/","&\\1;",$str); + + return $str; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Value + * + * Grabs a value from the POST array for the specified field so you can + * re-populate an input field or textarea. If Form Validation + * is active it retrieves the info from the validation class + * + * @access public + * @param string + * @return mixed + */ +if ( ! function_exists('set_value')) +{ + function set_value($field = '', $default = '') + { + if (FALSE === ($OBJ =& _get_validation_object())) + { + if ( ! isset($_POST[$field])) + { + return $default; + } + + return form_prep($_POST[$field]); + } + + return form_prep($OBJ->set_value($field, $default)); + } +} + +// ------------------------------------------------------------------------ + +/** + * Set Select + * + * Let's you set the selected value of a ', + 'heading_row_start' => '', + 'heading_previous_cell' => '', + 'heading_title_cell' => '', + 'heading_next_cell' => '', + 'heading_row_end' => '', + 'week_row_start' => '', + 'week_day_cell' => '', + 'week_row_end' => '', + 'cal_row_start' => '', + 'cal_cell_start' => '', + 'cal_cell_end_today' => '', + 'cal_row_end' => '', + 'table_close' => '
<<{heading}>>
{week_day}
', + 'cal_cell_start_today' => '', + 'cal_cell_content' => '{day}', + 'cal_cell_content_today' => '{day}', + 'cal_cell_no_content' => '{day}', + 'cal_cell_no_content_today' => '{day}', + 'cal_cell_blank' => ' ', + 'cal_cell_end' => '
' + ); + } + + // -------------------------------------------------------------------- + + /** + * Parse Template + * + * Harvests the data within the template {pseudo-variables} + * used to display the calendar + * + * @access public + * @return void + */ + function parse_template() + { + $this->temp = $this->default_template(); + + if ($this->template == '') + { + return; + } + + $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today'); + + foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today') as $val) + { + if (preg_match("/\{".$val."\}(.*?)\{\/".$val."\}/si", $this->template, $match)) + { + $this->temp[$val] = $match['1']; + } + else + { + if (in_array($val, $today, TRUE)) + { + $this->temp[$val] = $this->temp[str_replace('_today', '', $val)]; + } + } + } + } + +} + +// END CI_Calendar class + +/* End of file Calendar.php */ +/* Location: ./system/libraries/Calendar.php */ \ No newline at end of file diff --git a/libraries/Config.php b/libraries/Config.php new file mode 100644 index 0000000..a9cf9f4 --- /dev/null +++ b/libraries/Config.php @@ -0,0 +1,244 @@ +config =& get_config(); + log_message('debug', "Config Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Load Config File + * + * @access public + * @param string the config file name + * @return boolean if the file was loaded correctly + */ + function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) + { + $file = ($file == '') ? 'config' : str_replace(EXT, '', $file); + + if (in_array($file, $this->is_loaded, TRUE)) + { + return TRUE; + } + + if ( ! file_exists(APPPATH.'config/'.$file.EXT)) + { + if ($fail_gracefully === TRUE) + { + return FALSE; + } + show_error('The configuration file '.$file.EXT.' does not exist.'); + } + + include(APPPATH.'config/'.$file.EXT); + + if ( ! isset($config) OR ! is_array($config)) + { + if ($fail_gracefully === TRUE) + { + return FALSE; + } + show_error('Your '.$file.EXT.' file does not appear to contain a valid configuration array.'); + } + + if ($use_sections === TRUE) + { + if (isset($this->config[$file])) + { + $this->config[$file] = array_merge($this->config[$file], $config); + } + else + { + $this->config[$file] = $config; + } + } + else + { + $this->config = array_merge($this->config, $config); + } + + $this->is_loaded[] = $file; + unset($config); + + log_message('debug', 'Config file loaded: config/'.$file.EXT); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a config file item + * + * + * @access public + * @param string the config item name + * @param string the index name + * @param bool + * @return string + */ + function item($item, $index = '') + { + if ($index == '') + { + if ( ! isset($this->config[$item])) + { + return FALSE; + } + + $pref = $this->config[$item]; + } + else + { + if ( ! isset($this->config[$index])) + { + return FALSE; + } + + if ( ! isset($this->config[$index][$item])) + { + return FALSE; + } + + $pref = $this->config[$index][$item]; + } + + return $pref; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a config file item - adds slash after item + * + * The second parameter allows a slash to be added to the end of + * the item, in the case of a path. + * + * @access public + * @param string the config item name + * @param bool + * @return string + */ + function slash_item($item) + { + if ( ! isset($this->config[$item])) + { + return FALSE; + } + + $pref = $this->config[$item]; + + if ($pref != '' && substr($pref, -1) != '/') + { + $pref .= '/'; + } + + return $pref; + } + + // -------------------------------------------------------------------- + + /** + * Site URL + * + * @access public + * @param string the URI string + * @return string + */ + function site_url($uri = '') + { + if (is_array($uri)) + { + $uri = implode('/', $uri); + } + + if ($uri == '') + { + return $this->slash_item('base_url').$this->item('index_page'); + } + else + { + $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix'); + return $this->slash_item('base_url').$this->slash_item('index_page').preg_replace("|^/*(.+?)/*$|", "\\1", $uri).$suffix; + } + } + + // -------------------------------------------------------------------- + + /** + * System URL + * + * @access public + * @return string + */ + function system_url() + { + $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH)); + return $this->slash_item('base_url').end($x).'/'; + } + + // -------------------------------------------------------------------- + + /** + * Set a config file item + * + * @access public + * @param string the config item key + * @param string the config item value + * @return void + */ + function set_item($item, $value) + { + $this->config[$item] = $value; + } + +} + +// END CI_Config class + +/* End of file Config.php */ +/* Location: ./system/libraries/Config.php */ \ No newline at end of file diff --git a/libraries/Controller.php b/libraries/Controller.php new file mode 100644 index 0000000..95c0208 --- /dev/null +++ b/libraries/Controller.php @@ -0,0 +1,127 @@ +_ci_initialize(); + log_message('debug', "Controller Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize + * + * Assigns all the bases classes loaded by the front controller to + * variables in this class. Also calls the autoload routine. + * + * @access private + * @return void + */ + function _ci_initialize() + { + // Assign all the class objects that were instantiated by the + // front controller to local class variables so that CI can be + // run as one big super object. + $classes = array( + 'config' => 'Config', + 'input' => 'Input', + 'benchmark' => 'Benchmark', + 'uri' => 'URI', + 'output' => 'Output', + 'lang' => 'Language', + 'router' => 'Router' + ); + + foreach ($classes as $var => $class) + { + $this->$var =& load_class($class); + } + + // In PHP 5 the Loader class is run as a discreet + // class. In PHP 4 it extends the Controller + if (floor(phpversion()) >= 5) + { + $this->load =& load_class('Loader'); + $this->load->_ci_autoloader(); + } + else + { + $this->_ci_autoloader(); + + // sync up the objects since PHP4 was working from a copy + foreach (array_keys(get_object_vars($this)) as $attribute) + { + if (is_object($this->$attribute)) + { + $this->load->$attribute =& $this->$attribute; + } + } + } + } + + // -------------------------------------------------------------------- + + /** + * Run Scaffolding + * + * @access private + * @return void + */ + function _ci_scaffolding() + { + if ($this->_ci_scaffolding === FALSE OR $this->_ci_scaff_table === FALSE) + { + show_404('Scaffolding unavailable'); + } + + $method = ( ! in_array($this->uri->segment(3), array('add', 'insert', 'edit', 'update', 'view', 'delete', 'do_delete'), TRUE)) ? 'view' : $this->uri->segment(3); + + require_once(BASEPATH.'scaffolding/Scaffolding'.EXT); + $scaff = new Scaffolding($this->_ci_scaff_table); + $scaff->$method(); + } + + +} +// END _Controller class + +/* End of file Controller.php */ +/* Location: ./system/libraries/Controller.php */ \ No newline at end of file diff --git a/libraries/Email.php b/libraries/Email.php new file mode 100644 index 0000000..e9a5344 --- /dev/null +++ b/libraries/Email.php @@ -0,0 +1,1953 @@ + 0) + { + $this->initialize($config); + } + else + { + $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE; + $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE; + } + + log_message('debug', "Email Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize preferences + * + * @access public + * @param array + * @return void + */ + function initialize($config = array()) + { + $this->clear(); + foreach ($config as $key => $val) + { + if (isset($this->$key)) + { + $method = 'set_'.$key; + + if (method_exists($this, $method)) + { + $this->$method($val); + } + else + { + $this->$key = $val; + } + } + } + + $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE; + $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Email Data + * + * @access public + * @return void + */ + function clear($clear_attachments = FALSE) + { + $this->_subject = ""; + $this->_body = ""; + $this->_finalbody = ""; + $this->_header_str = ""; + $this->_replyto_flag = FALSE; + $this->_recipients = array(); + $this->_headers = array(); + $this->_debug_msg = array(); + + $this->_set_header('User-Agent', $this->useragent); + $this->_set_header('Date', $this->_set_date()); + + if ($clear_attachments !== FALSE) + { + $this->_attach_name = array(); + $this->_attach_type = array(); + $this->_attach_disp = array(); + } + } + + // -------------------------------------------------------------------- + + /** + * Set FROM + * + * @access public + * @param string + * @param string + * @return void + */ + function from($from, $name = '') + { + if (preg_match( '/\<(.*)\>/', $from, $match)) + { + $from = $match['1']; + } + + if ($this->validate) + { + $this->validate_email($this->_str_to_array($from)); + } + + if ($name != '' && strncmp($name, '"', 1) != 0) + { + $name = '"'.$name.'"'; + } + + $this->_set_header('From', $name.' <'.$from.'>'); + $this->_set_header('Return-Path', '<'.$from.'>'); + } + + // -------------------------------------------------------------------- + + /** + * Set Reply-to + * + * @access public + * @param string + * @param string + * @return void + */ + function reply_to($replyto, $name = '') + { + if (preg_match( '/\<(.*)\>/', $replyto, $match)) + { + $replyto = $match['1']; + } + + if ($this->validate) + { + $this->validate_email($this->_str_to_array($replyto)); + } + + if ($name == '') + { + $name = $replyto; + } + + if (strncmp($name, '"', 1) != 0) + { + $name = '"'.$name.'"'; + } + + $this->_set_header('Reply-To', $name.' <'.$replyto.'>'); + $this->_replyto_flag = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set Recipients + * + * @access public + * @param string + * @return void + */ + function to($to) + { + $to = $this->_str_to_array($to); + $to = $this->clean_email($to); + + if ($this->validate) + { + $this->validate_email($to); + } + + if ($this->_get_protocol() != 'mail') + { + $this->_set_header('To', implode(", ", $to)); + } + + switch ($this->_get_protocol()) + { + case 'smtp' : $this->_recipients = $to; + break; + case 'sendmail' : $this->_recipients = implode(", ", $to); + break; + case 'mail' : $this->_recipients = implode(", ", $to); + break; + } + } + + // -------------------------------------------------------------------- + + /** + * Set CC + * + * @access public + * @param string + * @return void + */ + function cc($cc) + { + $cc = $this->_str_to_array($cc); + $cc = $this->clean_email($cc); + + if ($this->validate) + { + $this->validate_email($cc); + } + + $this->_set_header('Cc', implode(", ", $cc)); + + if ($this->_get_protocol() == "smtp") + { + $this->_cc_array = $cc; + } + } + + // -------------------------------------------------------------------- + + /** + * Set BCC + * + * @access public + * @param string + * @param string + * @return void + */ + function bcc($bcc, $limit = '') + { + if ($limit != '' && is_numeric($limit)) + { + $this->bcc_batch_mode = TRUE; + $this->bcc_batch_size = $limit; + } + + $bcc = $this->_str_to_array($bcc); + $bcc = $this->clean_email($bcc); + + if ($this->validate) + { + $this->validate_email($bcc); + } + + if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size)) + { + $this->_bcc_array = $bcc; + } + else + { + $this->_set_header('Bcc', implode(", ", $bcc)); + } + } + + // -------------------------------------------------------------------- + + /** + * Set Email Subject + * + * @access public + * @param string + * @return void + */ + function subject($subject) + { + if (strpos($subject, "\r") !== FALSE OR strpos($subject, "\n") !== FALSE) + { + $subject = str_replace(array("\r\n", "\r", "\n"), '', $subject); + } + + if (strpos($subject, "\t")) + { + $subject = str_replace("\t", ' ', $subject); + } + + $this->_set_header('Subject', trim($subject)); + } + + // -------------------------------------------------------------------- + + /** + * Set Body + * + * @access public + * @param string + * @return void + */ + function message($body) + { + $this->_body = stripslashes(rtrim(str_replace("\r", "", $body))); + } + + // -------------------------------------------------------------------- + + /** + * Assign file attachments + * + * @access public + * @param string + * @return string + */ + function attach($filename, $disposition = 'attachment') + { + $this->_attach_name[] = $filename; + $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename)))); + $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters + } + + // -------------------------------------------------------------------- + + /** + * Add a Header Item + * + * @access private + * @param string + * @param string + * @return void + */ + function _set_header($header, $value) + { + $this->_headers[$header] = $value; + } + + // -------------------------------------------------------------------- + + /** + * Convert a String to an Array + * + * @access private + * @param string + * @return array + */ + function _str_to_array($email) + { + if ( ! is_array($email)) + { + if (strpos($email, ',') !== FALSE) + { + $email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY); + } + else + { + $email = trim($email); + settype($email, "array"); + } + } + return $email; + } + + // -------------------------------------------------------------------- + + /** + * Set Multipart Value + * + * @access public + * @param string + * @return void + */ + function set_alt_message($str = '') + { + $this->alt_message = ($str == '') ? '' : $str; + } + + // -------------------------------------------------------------------- + + /** + * Set Mailtype + * + * @access public + * @param string + * @return void + */ + function set_mailtype($type = 'text') + { + $this->mailtype = ($type == 'html') ? 'html' : 'text'; + } + + // -------------------------------------------------------------------- + + /** + * Set Wordwrap + * + * @access public + * @param string + * @return void + */ + function set_wordwrap($wordwrap = TRUE) + { + $this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set Protocol + * + * @access public + * @param string + * @return void + */ + function set_protocol($protocol = 'mail') + { + $this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol); + } + + // -------------------------------------------------------------------- + + /** + * Set Priority + * + * @access public + * @param integer + * @return void + */ + function set_priority($n = 3) + { + if ( ! is_numeric($n)) + { + $this->priority = 3; + return; + } + + if ($n < 1 OR $n > 5) + { + $this->priority = 3; + return; + } + + $this->priority = $n; + } + + // -------------------------------------------------------------------- + + /** + * Set Newline Character + * + * @access public + * @param string + * @return void + */ + function set_newline($newline = "\n") + { + if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r") + { + $this->newline = "\n"; + return; + } + + $this->newline = $newline; + } + + // -------------------------------------------------------------------- + + /** + * Set CRLF + * + * @access public + * @param string + * @return void + */ + function set_crlf($crlf = "\n") + { + if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r") + { + $this->crlf = "\n"; + return; + } + + $this->crlf = $crlf; + } + + // -------------------------------------------------------------------- + + /** + * Set Message Boundary + * + * @access private + * @return void + */ + function _set_boundaries() + { + $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative + $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary + } + + // -------------------------------------------------------------------- + + /** + * Get the Message ID + * + * @access private + * @return string + */ + function _get_message_id() + { + $from = $this->_headers['Return-Path']; + $from = str_replace(">", "", $from); + $from = str_replace("<", "", $from); + + return "<".uniqid('').strstr($from, '@').">"; + } + + // -------------------------------------------------------------------- + + /** + * Get Mail Protocol + * + * @access private + * @param bool + * @return string + */ + function _get_protocol($return = TRUE) + { + $this->protocol = strtolower($this->protocol); + $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol; + + if ($return == TRUE) + { + return $this->protocol; + } + } + + // -------------------------------------------------------------------- + + /** + * Get Mail Encoding + * + * @access private + * @param bool + * @return string + */ + function _get_encoding($return = TRUE) + { + $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding; + + foreach ($this->_base_charsets as $charset) + { + if (strncmp($charset, $this->charset, strlen($charset)) == 0) + { + $this->_encoding = '7bit'; + } + } + + if ($return == TRUE) + { + return $this->_encoding; + } + } + + // -------------------------------------------------------------------- + + /** + * Get content type (text/html/attachment) + * + * @access private + * @return string + */ + function _get_content_type() + { + if ($this->mailtype == 'html' && count($this->_attach_name) == 0) + { + return 'html'; + } + elseif ($this->mailtype == 'html' && count($this->_attach_name) > 0) + { + return 'html-attach'; + } + elseif ($this->mailtype == 'text' && count($this->_attach_name) > 0) + { + return 'plain-attach'; + } + else + { + return 'plain'; + } + } + + // -------------------------------------------------------------------- + + /** + * Set RFC 822 Date + * + * @access private + * @return string + */ + function _set_date() + { + $timezone = date("Z"); + $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+'; + $timezone = abs($timezone); + $timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60; + + return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone); + } + + // -------------------------------------------------------------------- + + /** + * Mime message + * + * @access private + * @return string + */ + function _get_mime_message() + { + return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format."; + } + + // -------------------------------------------------------------------- + + /** + * Validate Email Address + * + * @access public + * @param string + * @return bool + */ + function validate_email($email) + { + if ( ! is_array($email)) + { + $this->_set_error_message('email_must_be_array'); + return FALSE; + } + + foreach ($email as $val) + { + if ( ! $this->valid_email($val)) + { + $this->_set_error_message('email_invalid_address', $val); + return FALSE; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Email Validation + * + * @access public + * @param string + * @return bool + */ + function valid_email($address) + { + return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Clean Extended Email Address: Joe Smith + * + * @access public + * @param string + * @return string + */ + function clean_email($email) + { + if ( ! is_array($email)) + { + if (preg_match('/\<(.*)\>/', $email, $match)) + { + return $match['1']; + } + else + { + return $email; + } + } + + $clean_email = array(); + + foreach ($email as $addy) + { + if (preg_match( '/\<(.*)\>/', $addy, $match)) + { + $clean_email[] = $match['1']; + } + else + { + $clean_email[] = $addy; + } + } + + return $clean_email; + } + + // -------------------------------------------------------------------- + + /** + * Build alternative plain text message + * + * This function provides the raw message for use + * in plain-text headers of HTML-formatted emails. + * If the user hasn't specified his own alternative message + * it creates one by stripping the HTML + * + * @access private + * @return string + */ + function _get_alt_message() + { + if ($this->alt_message != "") + { + return $this->word_wrap($this->alt_message, '76'); + } + + if (preg_match('/\(.*)\<\/body\>/si', $this->_body, $match)) + { + $body = $match['1']; + } + else + { + $body = $this->_body; + } + + $body = trim(strip_tags($body)); + $body = preg_replace( '# '.$message. ' '.$filepath.' '.$line, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * 404 Page Not Found Handler + * + * @access private + * @param string + * @return string + */ + function show_404($page = '') + { + $heading = "404 Page Not Found"; + $message = "The page you requested was not found."; + + log_message('error', '404 Page Not Found --> '.$page); + echo $this->show_error($heading, $message, 'error_404'); + exit; + } + + // -------------------------------------------------------------------- + + /** + * General Error Page + * + * This function takes an error message as input + * (either as a string or an array) and displays + * it using the specified template. + * + * @access private + * @param string the heading + * @param string the message + * @param string the template name + * @return string + */ + function show_error($heading, $message, $template = 'error_general') + { + $message = '

'.implode('

', ( ! is_array($message)) ? array($message) : $message).'

'; + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_flush(); + } + ob_start(); + include(APPPATH.'errors/'.$template.EXT); + $buffer = ob_get_contents(); + ob_end_clean(); + return $buffer; + } + + // -------------------------------------------------------------------- + + /** + * Native PHP error handler + * + * @access private + * @param string the error severity + * @param string the error string + * @param string the error filepath + * @param string the error line number + * @return string + */ + function show_php_error($severity, $message, $filepath, $line) + { + $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity]; + + $filepath = str_replace("\\", "/", $filepath); + + // For safety reasons we do not show the full file path + if (FALSE !== strpos($filepath, '/')) + { + $x = explode('/', $filepath); + $filepath = $x[count($x)-2].'/'.end($x); + } + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_flush(); + } + ob_start(); + include(APPPATH.'errors/error_php'.EXT); + $buffer = ob_get_contents(); + ob_end_clean(); + echo $buffer; + } + + +} +// END Exceptions Class + +/* End of file Exceptions.php */ +/* Location: ./system/libraries/Exceptions.php */ \ No newline at end of file diff --git a/libraries/Form_validation.php b/libraries/Form_validation.php new file mode 100644 index 0000000..1215e13 --- /dev/null +++ b/libraries/Form_validation.php @@ -0,0 +1,1280 @@ +'; + var $_error_suffix = '

'; + var $error_string = ''; + var $_safe_form_data = FALSE; + + + /** + * Constructor + * + */ + function CI_Form_validation($rules = array()) + { + $this->CI =& get_instance(); + + // Validation rules can be stored in a config file. + $this->_config_rules = $rules; + + // Automatically load the form helper + $this->CI->load->helper('form'); + + // Set the character encoding in MB. + if (function_exists('mb_internal_encoding')) + { + mb_internal_encoding($this->CI->config->item('charset')); + } + + log_message('debug', "Validation Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Set Rules + * + * This function takes an array of field names and validation + * rules as input, validates the info, and stores it + * + * @access public + * @param mixed + * @param string + * @return void + */ + function set_rules($field, $label = '', $rules = '') + { + // No reason to set rules if we have no POST data + if (count($_POST) == 0) + { + return; + } + + // If an array was passed via the first parameter instead of indidual string + // values we cycle through it and recursively call this function. + if (is_array($field)) + { + foreach ($field as $row) + { + // Houston, we have a problem... + if ( ! isset($row['field']) OR ! isset($row['rules'])) + { + continue; + } + + // If the field label wasn't passed we use the field name + $label = ( ! isset($row['label'])) ? $row['field'] : $row['label']; + + // Here we go! + $this->set_rules($row['field'], $label, $row['rules']); + } + return; + } + + // No fields? Nothing to do... + if ( ! is_string($field) OR ! is_string($rules) OR $field == '') + { + return; + } + + // If the field label wasn't passed we use the field name + $label = ($label == '') ? $field : $label; + + // Is the field name an array? We test for the existence of a bracket "[" in + // the field name to determine this. If it is an array, we break it apart + // into its components so that we can fetch the corresponding POST data later + if (strpos($field, '[') !== FALSE AND preg_match_all('/\[(.*?)\]/', $field, $matches)) + { + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + $x = explode('[', $field); + $indexes[] = current($x); + + for ($i = 0; $i < count($matches['0']); $i++) + { + if ($matches['1'][$i] != '') + { + $indexes[] = $matches['1'][$i]; + } + } + + $is_array = TRUE; + } + else + { + $indexes = array(); + $is_array = FALSE; + } + + // Build our master array + $this->_field_data[$field] = array( + 'field' => $field, + 'label' => $label, + 'rules' => $rules, + 'is_array' => $is_array, + 'keys' => $indexes, + 'postdata' => NULL, + 'error' => '' + ); + } + + // -------------------------------------------------------------------- + + /** + * Set Error Message + * + * Lets users set their own error messages on the fly. Note: The key + * name has to match the function name that it corresponds to. + * + * @access public + * @param string + * @param string + * @return string + */ + function set_message($lang, $val = '') + { + if ( ! is_array($lang)) + { + $lang = array($lang => $val); + } + + $this->_error_messages = array_merge($this->_error_messages, $lang); + } + + // -------------------------------------------------------------------- + + /** + * Set The Error Delimiter + * + * Permits a prefix/suffix to be added to each error message + * + * @access public + * @param string + * @param string + * @return void + */ + function set_error_delimiters($prefix = '

', $suffix = '

') + { + $this->_error_prefix = $prefix; + $this->_error_suffix = $suffix; + } + + // -------------------------------------------------------------------- + + /** + * Get Error Message + * + * Gets the error message associated with a particular field + * + * @access public + * @param string the field name + * @return void + */ + function error($field = '', $prefix = '', $suffix = '') + { + if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '') + { + return ''; + } + + if ($prefix == '') + { + $prefix = $this->_error_prefix; + } + + if ($suffix == '') + { + $suffix = $this->_error_suffix; + } + + return $prefix.$this->_field_data[$field]['error'].$suffix; + } + + // -------------------------------------------------------------------- + + /** + * Error String + * + * Returns the error messages as a string, wrapped in the error delimiters + * + * @access public + * @param string + * @param string + * @return str + */ + function error_string($prefix = '', $suffix = '') + { + // No errrors, validation passes! + if (count($this->_error_array) === 0) + { + return ''; + } + + if ($prefix == '') + { + $prefix = $this->_error_prefix; + } + + if ($suffix == '') + { + $suffix = $this->_error_suffix; + } + + // Generate the error string + $str = ''; + foreach ($this->_error_array as $val) + { + if ($val != '') + { + $str .= $prefix.$val.$suffix."\n"; + } + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Run the Validator + * + * This function does all the work. + * + * @access public + * @return bool + */ + function run($group = '') + { + // Do we even have any data to process? Mm? + if (count($_POST) == 0) + { + return FALSE; + } + + // Does the _field_data array containing the validation rules exist? + // If not, we look to see if they were assigned via a config file + if (count($this->_field_data) == 0) + { + // No validation rules? We're done... + if (count($this->_config_rules) == 0) + { + return FALSE; + } + + // Is there a validation rule for the particular URI being accessed? + $uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group; + + if ($uri != '' AND isset($this->_config_rules[$uri])) + { + $this->set_rules($this->_config_rules[$uri]); + } + else + { + $this->set_rules($this->_config_rules); + } + + // We're we able to set the rules correctly? + if (count($this->_field_data) == 0) + { + log_message('debug', "Unable to find validation rules"); + return FALSE; + } + } + + // Load the language file containing error messages + $this->CI->lang->load('form_validation'); + + // Cycle through the rules for each field, match the + // corresponding $_POST item and test for errors + foreach ($this->_field_data as $field => $row) + { + // Fetch the data from the corresponding $_POST array and cache it in the _field_data array. + // Depending on whether the field name is an array or a string will determine where we get it from. + + if ($row['is_array'] == TRUE) + { + $this->_field_data[$field]['postdata'] = $this->_reduce_array($_POST, $row['keys']); + } + else + { + if (isset($_POST[$field]) AND $_POST[$field] != "") + { + $this->_field_data[$field]['postdata'] = $_POST[$field]; + } + } + + $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']); + } + + // Did we end up with any errors? + $total_errors = count($this->_error_array); + + if ($total_errors > 0) + { + $this->_safe_form_data = TRUE; + } + + // Now we need to re-set the POST data with the new, processed data + $this->_reset_post_array(); + + // No errors, validation passes! + if ($total_errors == 0) + { + return TRUE; + } + + // Validation fails + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Traverse a multidimensional $_POST array index until the data is found + * + * @access private + * @param array + * @param array + * @param integer + * @return mixed + */ + function _reduce_array($array, $keys, $i = 0) + { + if (is_array($array)) + { + if (isset($keys[$i])) + { + if (isset($array[$keys[$i]])) + { + $array = $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)); + } + else + { + return NULL; + } + } + else + { + return $array; + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Re-populate the _POST array with our finalized and processed data + * + * @access private + * @return null + */ + function _reset_post_array() + { + foreach ($this->_field_data as $field => $row) + { + if ( ! is_null($row['postdata'])) + { + if ($row['is_array'] == FALSE) + { + if (isset($_POST[$row['field']])) + { + $_POST[$row['field']] = $this->prep_for_form($row['postdata']); + } + } + else + { + $post = '$_POST["'; + + if (count($row['keys']) == 1) + { + $post .= current($row['keys']); + $post .= '"]'; + } + else + { + $i = 0; + foreach ($row['keys'] as $val) + { + if ($i == 0) + { + $post .= $val.'"]'; + $i++; + continue; + } + + $post .= '["'.$val.'"]'; + } + } + + if (is_array($row['postdata'])) + { + $array = array(); + foreach ($row['postdata'] as $k => $v) + { + $array[$k] = $this->prep_for_form($v); + } + + $post .= ' = $array;'; + } + else + { + $post .= ' = "'.$this->prep_for_form($row['postdata']).'";'; + } + + eval($post); + } + } + } + } + + // -------------------------------------------------------------------- + + /** + * Executes the Validation routines + * + * @access private + * @param array + * @param array + * @param mixed + * @param integer + * @return mixed + */ + function _execute($row, $rules, $postdata = NULL, $cycles = 0) + { + // If the $_POST data is an array we will run a recursive call + if (is_array($postdata)) + { + foreach ($postdata as $key => $val) + { + $this->_execute($row, $rules, $val, $cycles); + $cycles++; + } + + return; + } + + // -------------------------------------------------------------------- + + // If the field is blank, but NOT required, no further tests are necessary + $callback = FALSE; + if ( ! in_array('required', $rules) AND is_null($postdata)) + { + // Before we bail out, does the rule contain a callback? + if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match)) + { + $callback = TRUE; + $rules = (array('1' => $match[1])); + } + else + { + return; + } + } + + // -------------------------------------------------------------------- + + // Isset Test. Typically this rule will only apply to checkboxes. + if (is_null($postdata) AND $callback == FALSE) + { + if (in_array('isset', $rules, TRUE) OR in_array('required', $rules)) + { + // Set the message type + $type = (in_array('required', $rules)) ? 'required' : 'isset'; + + if ( ! isset($this->_error_messages[$type])) + { + if (FALSE === ($line = $this->CI->lang->line($type))) + { + $line = 'The field was not set'; + } + } + else + { + $line = $this->_error_messages[$type]; + } + + // Build the error message + $message = sprintf($line, $this->_translate_fieldname($row['label'])); + + // Save the error message + $this->_field_data[$row['field']]['error'] = $message; + + if ( ! isset($this->_error_array[$row['field']])) + { + $this->_error_array[$row['field']] = $message; + } + } + + return; + } + + // -------------------------------------------------------------------- + + // Cycle through each rule and run it + foreach ($rules As $rule) + { + $_in_array = FALSE; + + // We set the $postdata variable with the current data in our master array so that + // each cycle of the loop is dealing with the processed data from the last cycle + if ($row['is_array'] == TRUE AND is_array($this->_field_data[$row['field']]['postdata'])) + { + // We shouldn't need this safety, but just in case there isn't an array index + // associated with this cycle we'll bail out + if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles])) + { + continue; + } + + $postdata = $this->_field_data[$row['field']]['postdata'][$cycles]; + $_in_array = TRUE; + } + else + { + $postdata = $this->_field_data[$row['field']]['postdata']; + } + + // -------------------------------------------------------------------- + + // Is the rule a callback? + $callback = FALSE; + if (substr($rule, 0, 9) == 'callback_') + { + $rule = substr($rule, 9); + $callback = TRUE; + } + + // Strip the parameter (if exists) from the rule + // Rules can contain a parameter: max_length[5] + $param = FALSE; + if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match)) + { + $rule = $match[1]; + $param = $match[2]; + } + + // Call the function that corresponds to the rule + if ($callback === TRUE) + { + if ( ! method_exists($this->CI, $rule)) + { + continue; + } + + // Run the function and grab the result + $result = $this->CI->$rule($postdata, $param); + + // Re-assign the result to the master data array + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + + // If the field isn't required and we just processed a callback we'll move on... + if ( ! in_array('required', $rules, TRUE) AND $result !== FALSE) + { + return; + } + } + else + { + if ( ! method_exists($this, $rule)) + { + // If our own wrapper function doesn't exist we see if a native PHP function does. + // Users can use any native PHP function call that has one param. + if (function_exists($rule)) + { + $result = $rule($postdata); + + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + } + + continue; + } + + $result = $this->$rule($postdata, $param); + + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + } + + // Did the rule test negatively? If so, grab the error. + if ($result === FALSE) + { + if ( ! isset($this->_error_messages[$rule])) + { + if (FALSE === ($line = $this->CI->lang->line($rule))) + { + $line = 'Unable to access an error message corresponding to your field name.'; + } + } + else + { + $line = $this->_error_messages[$rule]; + } + + // Build the error message + $message = sprintf($line, $this->_translate_fieldname($row['label']), $param); + + // Save the error message + $this->_field_data[$row['field']]['error'] = $message; + + if ( ! isset($this->_error_array[$row['field']])) + { + $this->_error_array[$row['field']] = $message; + } + + return; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Translate a field name + * + * @access private + * @param string the field name + * @return string + */ + function _translate_fieldname($fieldname) + { + // Do we need to translate the field name? + // We look for the prefix lang: to determine this + if (substr($fieldname, 0, 5) == 'lang:') + { + // Grab the variable + $line = substr($fieldname, 5); + + // Were we able to translate the field name? If not we use $line + if (FALSE === ($fieldname = $this->CI->lang->line($line))) + { + return $line; + } + } + + return $fieldname; + } + + // -------------------------------------------------------------------- + + /** + * Get the value from a form + * + * Permits you to repopulate a form field with the value it was submitted + * with, or, if that value doesn't exist, with the default + * + * @access public + * @param string the field name + * @param string + * @return void + */ + function set_value($field = '', $default = '') + { + if ( ! isset($this->_field_data[$field])) + { + return $default; + } + + return $this->_field_data[$field]['postdata']; + } + + // -------------------------------------------------------------------- + + /** + * Set Select + * + * Enables pull-down lists to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_select($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' selected="selected"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' selected="selected"'; + } + + // -------------------------------------------------------------------- + + /** + * Set Radio + * + * Enables radio buttons to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_radio($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' checked="checked"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' checked="checked"'; + } + + // -------------------------------------------------------------------- + + /** + * Set Checkbox + * + * Enables checkboxes to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_checkbox($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' checked="checked"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' checked="checked"'; + } + + // -------------------------------------------------------------------- + + /** + * Required + * + * @access public + * @param string + * @return bool + */ + function required($str) + { + if ( ! is_array($str)) + { + return (trim($str) == '') ? FALSE : TRUE; + } + else + { + return ( ! empty($str)); + } + } + + // -------------------------------------------------------------------- + + /** + * Match one field to another + * + * @access public + * @param string + * @param field + * @return bool + */ + function matches($str, $field) + { + if ( ! isset($_POST[$field])) + { + return FALSE; + } + + $field = $_POST[$field]; + + return ($str !== $field) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Minimum Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function min_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) < $val) ? FALSE : TRUE; + } + + return (strlen($str) < $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Max Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function max_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) > $val) ? FALSE : TRUE; + } + + return (strlen($str) > $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Exact Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function exact_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) != $val) ? FALSE : TRUE; + } + + return (strlen($str) != $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Email + * + * @access public + * @param string + * @return bool + */ + function valid_email($str) + { + return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Emails + * + * @access public + * @param string + * @return bool + */ + function valid_emails($str) + { + if (strpos($str, ',') === FALSE) + { + return $this->valid_email(trim($str)); + } + + foreach(explode(',', $str) as $email) + { + if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * @access public + * @param string + * @return string + */ + function valid_ip($ip) + { + return $this->CI->input->valid_ip($ip); + } + + // -------------------------------------------------------------------- + + /** + * Alpha + * + * @access public + * @param string + * @return bool + */ + function alpha($str) + { + return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric + * + * @access public + * @param string + * @return bool + */ + function alpha_numeric($str) + { + return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric with underscores and dashes + * + * @access public + * @param string + * @return bool + */ + function alpha_dash($str) + { + return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Numeric + * + * @access public + * @param string + * @return bool + */ + function numeric($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str); + + } + + // -------------------------------------------------------------------- + + /** + * Is Numeric + * + * @access public + * @param string + * @return bool + */ + function is_numeric($str) + { + return ( ! is_numeric($str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Integer + * + * @access public + * @param string + * @return bool + */ + function integer($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number (0,1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural($str) + { + return (bool)preg_match( '/^[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number, but not a zero (1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural_no_zero($str) + { + if ( ! preg_match( '/^[0-9]+$/', $str)) + { + return FALSE; + } + + if ($str == 0) + { + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Base64 + * + * Tests a string for characters outside of the Base64 alphabet + * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 + * + * @access public + * @param string + * @return bool + */ + function valid_base64($str) + { + return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Prep data for form + * + * This function allows HTML to be safely shown in a form. + * Special characters are converted. + * + * @access public + * @param string + * @return string + */ + function prep_for_form($data = '') + { + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = $this->prep_for_form($val); + } + + return $data; + } + + if ($this->_safe_form_data == FALSE OR $data === '') + { + return $data; + } + + return str_replace(array("'", '"', '<', '>'), array("'", """, '<', '>'), stripslashes($data)); + } + + // -------------------------------------------------------------------- + + /** + * Prep URL + * + * @access public + * @param string + * @return string + */ + function prep_url($str = '') + { + if ($str == 'http://' OR $str == '') + { + return ''; + } + + if (substr($str, 0, 7) != 'http://' && substr($str, 0, 8) != 'https://') + { + $str = 'http://'.$str; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Strip Image Tags + * + * @access public + * @param string + * @return string + */ + function strip_image_tags($str) + { + return $this->CI->input->strip_image_tags($str); + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * @access public + * @param string + * @return string + */ + function xss_clean($str) + { + return $this->CI->input->xss_clean($str); + } + + // -------------------------------------------------------------------- + + /** + * Convert PHP tags to entities + * + * @access public + * @param string + * @return string + */ + function encode_php_tags($str) + { + return str_replace(array(''), array('<?php', '<?PHP', '<?', '?>'), $str); + } + +} +// END Form Validation Class + +/* End of file Form_validation.php */ +/* Location: ./system/libraries/Form_validation.php */ \ No newline at end of file diff --git a/libraries/Ftp.php b/libraries/Ftp.php new file mode 100644 index 0000000..0e3dd5e --- /dev/null +++ b/libraries/Ftp.php @@ -0,0 +1,618 @@ + 0) + { + $this->initialize($config); + } + + log_message('debug', "FTP Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize preferences + * + * @access public + * @param array + * @return void + */ + function initialize($config = array()) + { + foreach ($config as $key => $val) + { + if (isset($this->$key)) + { + $this->$key = $val; + } + } + + // Prep the hostname + $this->hostname = preg_replace('|.+?://|', '', $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * FTP Connect + * + * @access public + * @param array the connection values + * @return bool + */ + function connect($config = array()) + { + if (count($config) > 0) + { + $this->initialize($config); + } + + if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port))) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_connect'); + } + return FALSE; + } + + if ( ! $this->_login()) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_login'); + } + return FALSE; + } + + // Set passive mode if needed + if ($this->passive == TRUE) + { + ftp_pasv($this->conn_id, TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * FTP Login + * + * @access private + * @return bool + */ + function _login() + { + return @ftp_login($this->conn_id, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Validates the connection ID + * + * @access private + * @return bool + */ + function _is_conn() + { + if ( ! is_resource($this->conn_id)) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_no_connection'); + } + return FALSE; + } + return TRUE; + } + + // -------------------------------------------------------------------- + + + /** + * Change direcotry + * + * The second parameter lets us momentarily turn off debugging so that + * this function can be used to test for the existance of a folder + * without throwing an error. There's no FTP equivalent to is_dir() + * so we do it by trying to change to a particular directory. + * Internally, this paramter is only used by the "mirror" function below. + * + * @access public + * @param string + * @param bool + * @return bool + */ + function changedir($path = '', $supress_debug = FALSE) + { + if ($path == '' OR ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_chdir($this->conn_id, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE AND $supress_debug == FALSE) + { + $this->_error('ftp_unable_to_changedir'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Create a directory + * + * @access public + * @param string + * @return bool + */ + function mkdir($path = '', $permissions = NULL) + { + if ($path == '' OR ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_mkdir($this->conn_id, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_makdir'); + } + return FALSE; + } + + // Set file permissions if needed + if ( ! is_null($permissions)) + { + $this->chmod($path, (int)$permissions); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Upload a file to the server + * + * @access public + * @param string + * @param string + * @param string + * @return bool + */ + function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + if ( ! file_exists($locpath)) + { + $this->_error('ftp_no_source_file'); + return FALSE; + } + + // Set the mode if not specified + if ($mode == 'auto') + { + // Get the file extension so we can set the upload type + $ext = $this->_getext($locpath); + $mode = $this->_settype($ext); + } + + $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY; + + $result = @ftp_put($this->conn_id, $rempath, $locpath, $mode); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_upload'); + } + return FALSE; + } + + // Set file permissions if needed + if ( ! is_null($permissions)) + { + $this->chmod($rempath, (int)$permissions); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rename (or move) a file + * + * @access public + * @param string + * @param string + * @param bool + * @return bool + */ + function rename($old_file, $new_file, $move = FALSE) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_rename($this->conn_id, $old_file, $new_file); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move'; + + $this->_error($msg); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Move a file + * + * @access public + * @param string + * @param string + * @return bool + */ + function move($old_file, $new_file) + { + return $this->rename($old_file, $new_file, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Rename (or move) a file + * + * @access public + * @param string + * @return bool + */ + function delete_file($filepath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_delete($this->conn_id, $filepath); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_delete'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Delete a folder and recursively delete everything (including sub-folders) + * containted within it. + * + * @access public + * @param string + * @return bool + */ + function delete_dir($filepath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Add a trailing slash to the file path if needed + $filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath); + + $list = $this->list_files($filepath); + + if ($list !== FALSE AND count($list) > 0) + { + foreach ($list as $item) + { + // If we can't delete the item it's probaly a folder so + // we'll recursively call delete_dir() + if ( ! @ftp_delete($this->conn_id, $item)) + { + $this->delete_dir($item); + } + } + } + + $result = @ftp_rmdir($this->conn_id, $filepath); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_delete'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set file permissions + * + * @access public + * @param string the file path + * @param string the permissions + * @return bool + */ + function chmod($path, $perm) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Permissions can only be set when running PHP 5 + if ( ! function_exists('ftp_chmod')) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_chmod'); + } + return FALSE; + } + + $result = @ftp_chmod($this->conn_id, $perm, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_chmod'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * FTP List files in the specified directory + * + * @access public + * @return array + */ + function list_files($path = '.') + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + return ftp_nlist($this->conn_id, $path); + } + + // ------------------------------------------------------------------------ + + /** + * Read a directory and recreate it remotely + * + * This function recursively reads a folder and everything it contains (including + * sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure + * of the original file path will be recreated on the server. + * + * @access public + * @param string path to source with trailing slash + * @param string path to destination - include the base folder with trailing slash + * @return bool + */ + function mirror($locpath, $rempath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Open the local file path + if ($fp = @opendir($locpath)) + { + // Attempt to open the remote file path. + if ( ! $this->changedir($rempath, TRUE)) + { + // If it doesn't exist we'll attempt to create the direcotory + if ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath)) + { + return FALSE; + } + } + + // Recursively read the local directory + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.') + { + $this->mirror($locpath.$file."/", $rempath.$file."/"); + } + elseif (substr($file, 0, 1) != ".") + { + // Get the file extension so we can se the upload type + $ext = $this->_getext($file); + $mode = $this->_settype($ext); + + $this->upload($locpath.$file, $rempath.$file, $mode); + } + } + return TRUE; + } + + return FALSE; + } + + + // -------------------------------------------------------------------- + + /** + * Extract the file extension + * + * @access private + * @param string + * @return string + */ + function _getext($filename) + { + if (FALSE === strpos($filename, '.')) + { + return 'txt'; + } + + $x = explode('.', $filename); + return end($x); + } + + + // -------------------------------------------------------------------- + + /** + * Set the upload type + * + * @access private + * @param string + * @return string + */ + function _settype($ext) + { + $text_types = array( + 'txt', + 'text', + 'php', + 'phps', + 'php4', + 'js', + 'css', + 'htm', + 'html', + 'phtml', + 'shtml', + 'log', + 'xml' + ); + + + return (in_array($ext, $text_types)) ? 'ascii' : 'binary'; + } + + // ------------------------------------------------------------------------ + + /** + * Close the connection + * + * @access public + * @param string path to source + * @param string path to destination + * @return bool + */ + function close() + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + @ftp_close($this->conn_id); + } + + // ------------------------------------------------------------------------ + + /** + * Display error message + * + * @access private + * @param string + * @return bool + */ + function _error($line) + { + $CI =& get_instance(); + $CI->lang->load('ftp'); + show_error($CI->lang->line($line)); + } + + +} +// END FTP Class + +/* End of file Ftp.php */ +/* Location: ./system/libraries/Ftp.php */ \ No newline at end of file diff --git a/libraries/Hooks.php b/libraries/Hooks.php new file mode 100644 index 0000000..34dd98f --- /dev/null +++ b/libraries/Hooks.php @@ -0,0 +1,226 @@ +_initialize(); + log_message('debug', "Hooks Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Hooks Preferences + * + * @access private + * @return void + */ + function _initialize() + { + $CFG =& load_class('Config'); + + // If hooks are not enabled in the config file + // there is nothing else to do + + if ($CFG->item('enable_hooks') == FALSE) + { + return; + } + + // Grab the "hooks" definition file. + // If there are no hooks, we're done. + + @include(APPPATH.'config/hooks'.EXT); + + if ( ! isset($hook) OR ! is_array($hook)) + { + return; + } + + $this->hooks =& $hook; + $this->enabled = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Call Hook + * + * Calls a particular hook + * + * @access private + * @param string the hook name + * @return mixed + */ + function _call_hook($which = '') + { + if ( ! $this->enabled OR ! isset($this->hooks[$which])) + { + return FALSE; + } + + if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])) + { + foreach ($this->hooks[$which] as $val) + { + $this->_run_hook($val); + } + } + else + { + $this->_run_hook($this->hooks[$which]); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Run Hook + * + * Runs a particular hook + * + * @access private + * @param array the hook details + * @return bool + */ + function _run_hook($data) + { + if ( ! is_array($data)) + { + return FALSE; + } + + // ----------------------------------- + // Safety - Prevents run-away loops + // ----------------------------------- + + // If the script being called happens to have the same + // hook call within it a loop can happen + + if ($this->in_progress == TRUE) + { + return; + } + + // ----------------------------------- + // Set file path + // ----------------------------------- + + if ( ! isset($data['filepath']) OR ! isset($data['filename'])) + { + return FALSE; + } + + $filepath = APPPATH.$data['filepath'].'/'.$data['filename']; + + if ( ! file_exists($filepath)) + { + return FALSE; + } + + // ----------------------------------- + // Set class/function name + // ----------------------------------- + + $class = FALSE; + $function = FALSE; + $params = ''; + + if (isset($data['class']) AND $data['class'] != '') + { + $class = $data['class']; + } + + if (isset($data['function'])) + { + $function = $data['function']; + } + + if (isset($data['params'])) + { + $params = $data['params']; + } + + if ($class === FALSE AND $function === FALSE) + { + return FALSE; + } + + // ----------------------------------- + // Set the in_progress flag + // ----------------------------------- + + $this->in_progress = TRUE; + + // ----------------------------------- + // Call the requested class and/or function + // ----------------------------------- + + if ($class !== FALSE) + { + if ( ! class_exists($class)) + { + require($filepath); + } + + $HOOK = new $class; + $HOOK->$function($params); + } + else + { + if ( ! function_exists($function)) + { + require($filepath); + } + + $function($params); + } + + $this->in_progress = FALSE; + return TRUE; + } + +} + +// END CI_Hooks class + +/* End of file Hooks.php */ +/* Location: ./system/libraries/Hooks.php */ \ No newline at end of file diff --git a/libraries/Image_lib.php b/libraries/Image_lib.php new file mode 100644 index 0000000..67b7309 --- /dev/null +++ b/libraries/Image_lib.php @@ -0,0 +1,1546 @@ + 0) + { + $this->initialize($props); + } + + log_message('debug', "Image Lib Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize image properties + * + * Resets values in case this class is used in a loop + * + * @access public + * @return void + */ + function clear() + { + $props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity'); + + foreach ($props as $val) + { + $this->$val = ''; + } + + // special consideration for master_dim + $this->master_dim = 'auto'; + } + + // -------------------------------------------------------------------- + + /** + * initialize image preferences + * + * @access public + * @param array + * @return void + */ + function initialize($props = array()) + { + /* + * Convert array elements into class variables + */ + if (count($props) > 0) + { + foreach ($props as $key => $val) + { + $this->$key = $val; + } + } + + /* + * Is there a source image? + * + * If not, there's no reason to continue + * + */ + if ($this->source_image == '') + { + $this->set_error('imglib_source_image_required'); + return FALSE; + } + + /* + * Is getimagesize() Available? + * + * We use it to determine the image properties (width/height). + * Note: We need to figure out how to determine image + * properties using ImageMagick and NetPBM + * + */ + if ( ! function_exists('getimagesize')) + { + $this->set_error('imglib_gd_required_for_props'); + return FALSE; + } + + $this->image_library = strtolower($this->image_library); + + /* + * Set the full server path + * + * The source image may or may not contain a path. + * Either way, we'll try use realpath to generate the + * full server path in order to more reliably read it. + * + */ + if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE) + { + $full_source_path = str_replace("\\", "/", realpath($this->source_image)); + } + else + { + $full_source_path = $this->source_image; + } + + $x = explode('/', $full_source_path); + $this->source_image = end($x); + $this->source_folder = str_replace($this->source_image, '', $full_source_path); + + // Set the Image Properties + if ( ! $this->get_image_properties($this->source_folder.$this->source_image)) + { + return FALSE; + } + + /* + * Assign the "new" image name/path + * + * If the user has set a "new_image" name it means + * we are making a copy of the source image. If not + * it means we are altering the original. We'll + * set the destination filename and path accordingly. + * + */ + if ($this->new_image == '') + { + $this->dest_image = $this->source_image; + $this->dest_folder = $this->source_folder; + } + else + { + if (strpos($this->new_image, '/') === FALSE) + { + $this->dest_folder = $this->source_folder; + $this->dest_image = $this->new_image; + } + else + { + if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE) + { + $full_dest_path = str_replace("\\", "/", realpath($this->new_image)); + } + else + { + $full_dest_path = $this->new_image; + } + + // Is there a file name? + if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path)) + { + $this->dest_folder = $full_dest_path.'/'; + $this->dest_image = $this->source_image; + } + else + { + $x = explode('/', $full_dest_path); + $this->dest_image = end($x); + $this->dest_folder = str_replace($this->dest_image, '', $full_dest_path); + } + } + } + + /* + * Compile the finalized filenames/paths + * + * We'll create two master strings containing the + * full server path to the source image and the + * full server path to the destination image. + * We'll also split the destination image name + * so we can insert the thumbnail marker if needed. + * + */ + if ($this->create_thumb === FALSE OR $this->thumb_marker == '') + { + $this->thumb_marker = ''; + } + + $xp = $this->explode_name($this->dest_image); + + $filename = $xp['name']; + $file_ext = $xp['ext']; + + $this->full_src_path = $this->source_folder.$this->source_image; + $this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext; + + /* + * Should we maintain image proportions? + * + * When creating thumbs or copies, the target width/height + * might not be in correct proportion with the source + * image's width/height. We'll recalculate it here. + * + */ + if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != '')) + { + $this->image_reproportion(); + } + + /* + * Was a width and height specified? + * + * If the destination width/height was + * not submitted we will use the values + * from the actual file + * + */ + if ($this->width == '') + $this->width = $this->orig_width; + + if ($this->height == '') + $this->height = $this->orig_height; + + // Set the quality + $this->quality = trim(str_replace("%", "", $this->quality)); + + if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality)) + $this->quality = 90; + + // Set the x/y coordinates + $this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis; + $this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis; + + // Watermark-related Stuff... + if ($this->wm_font_color != '') + { + if (strlen($this->wm_font_color) == 6) + { + $this->wm_font_color = '#'.$this->wm_font_color; + } + } + + if ($this->wm_shadow_color != '') + { + if (strlen($this->wm_shadow_color) == 6) + { + $this->wm_shadow_color = '#'.$this->wm_shadow_color; + } + } + + if ($this->wm_overlay_path != '') + { + $this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path)); + } + + if ($this->wm_shadow_color != '') + { + $this->wm_use_drop_shadow = TRUE; + } + + if ($this->wm_font_path != '') + { + $this->wm_use_truetype = TRUE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Resize + * + * This is a wrapper function that chooses the proper + * resize function based on the protocol specified + * + * @access public + * @return bool + */ + function resize() + { + $protocol = 'image_process_'.$this->image_library; + + if (eregi("gd2$", $protocol)) + { + $protocol = 'image_process_gd'; + } + + return $this->$protocol('resize'); + } + + // -------------------------------------------------------------------- + + /** + * Image Crop + * + * This is a wrapper function that chooses the proper + * cropping function based on the protocol specified + * + * @access public + * @return bool + */ + function crop() + { + $protocol = 'image_process_'.$this->image_library; + + if (eregi("gd2$", $protocol)) + { + $protocol = 'image_process_gd'; + } + + return $this->$protocol('crop'); + } + + // -------------------------------------------------------------------- + + /** + * Image Rotate + * + * This is a wrapper function that chooses the proper + * rotation function based on the protocol specified + * + * @access public + * @return bool + */ + function rotate() + { + // Allowed rotation values + $degs = array(90, 180, 270, 'vrt', 'hor'); + + if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs, TRUE)) + { + $this->set_error('imglib_rotation_angle_required'); + return FALSE; + } + + // Reassign the width and height + if ($this->rotation_angle == 90 OR $this->rotation_angle == 270) + { + $this->width = $this->orig_height; + $this->height = $this->orig_width; + } + else + { + $this->width = $this->orig_width; + $this->height = $this->orig_height; + } + + + // Choose resizing function + if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm') + { + $protocol = 'image_process_'.$this->image_library; + + return $this->$protocol('rotate'); + } + + if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt') + { + return $this->image_mirror_gd(); + } + else + { + return $this->image_rotate_gd(); + } + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using GD/GD2 + * + * This function will resize or crop + * + * @access public + * @param string + * @return bool + */ + function image_process_gd($action = 'resize') + { + $v2_override = FALSE; + + // If the target width/height match the source, AND if the new file name is not equal to the old file name + // we'll simply make a copy of the original with the new name... assuming dynamic rendering is off. + if ($this->dynamic_output === FALSE) + { + if ($this->orig_width == $this->width AND $this->orig_height == $this->height) + { + if ($this->source_image != $this->new_image) + { + if (@copy($this->full_src_path, $this->full_dst_path)) + { + @chmod($this->full_dst_path, DIR_WRITE_MODE); + } + } + + return TRUE; + } + } + + // Let's set up our values based on the action + if ($action == 'crop') + { + // Reassign the source width/height if cropping + $this->orig_width = $this->width; + $this->orig_height = $this->height; + + // GD 2.0 has a cropping bug so we'll test for it + if ($this->gd_version() !== FALSE) + { + $gd_version = str_replace('0', '', $this->gd_version()); + $v2_override = ($gd_version == 2) ? TRUE : FALSE; + } + } + else + { + // If resizing the x/y axis must be zero + $this->x_axis = 0; + $this->y_axis = 0; + } + + // Create the image handle + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + // Create The Image + // + // old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater" + // it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment + // below should that ever prove inaccurate. + // + // if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE) + if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor')) + { + $create = 'imagecreatetruecolor'; + $copy = 'imagecopyresampled'; + } + else + { + $create = 'imagecreate'; + $copy = 'imagecopyresized'; + } + + $dst_img = $create($this->width, $this->height); + $copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height); + + // Show the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($dst_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($dst_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($dst_img); + imagedestroy($src_img); + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using ImageMagick + * + * This function will resize, crop or rotate + * + * @access public + * @param string + * @return bool + */ + function image_process_imagemagick($action = 'resize') + { + // Do we have a vaild library path? + if ($this->library_path == '') + { + $this->set_error('imglib_libpath_invalid'); + return FALSE; + } + + if ( ! eregi("convert$", $this->library_path)) + { + if ( ! eregi("/$", $this->library_path)) $this->library_path .= "/"; + + $this->library_path .= 'convert'; + } + + // Execute the command + $cmd = $this->library_path." -quality ".$this->quality; + + if ($action == 'crop') + { + $cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + elseif ($action == 'rotate') + { + switch ($this->rotation_angle) + { + case 'hor' : $angle = '-flop'; + break; + case 'vrt' : $angle = '-flip'; + break; + default : $angle = '-rotate '.$this->rotation_angle; + break; + } + + $cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + else // Resize + { + $cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + + $retval = 1; + + @exec($cmd, $output, $retval); + + // Did it work? + if ($retval > 0) + { + $this->set_error('imglib_image_process_failed'); + return FALSE; + } + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using NetPBM + * + * This function will resize, crop or rotate + * + * @access public + * @param string + * @return bool + */ + function image_process_netpbm($action = 'resize') + { + if ($this->library_path == '') + { + $this->set_error('imglib_libpath_invalid'); + return FALSE; + } + + // Build the resizing command + switch ($this->image_type) + { + case 1 : + $cmd_in = 'giftopnm'; + $cmd_out = 'ppmtogif'; + break; + case 2 : + $cmd_in = 'jpegtopnm'; + $cmd_out = 'ppmtojpeg'; + break; + case 3 : + $cmd_in = 'pngtopnm'; + $cmd_out = 'ppmtopng'; + break; + } + + if ($action == 'crop') + { + $cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height; + } + elseif ($action == 'rotate') + { + switch ($this->rotation_angle) + { + case 90 : $angle = 'r270'; + break; + case 180 : $angle = 'r180'; + break; + case 270 : $angle = 'r90'; + break; + case 'vrt' : $angle = 'tb'; + break; + case 'hor' : $angle = 'lr'; + break; + } + + $cmd_inner = 'pnmflip -'.$angle.' '; + } + else // Resize + { + $cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height; + } + + $cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp'; + + $retval = 1; + + @exec($cmd, $output, $retval); + + // Did it work? + if ($retval > 0) + { + $this->set_error('imglib_image_process_failed'); + return FALSE; + } + + // With NetPBM we have to create a temporary image. + // If you try manipulating the original it fails so + // we have to rename the temp file. + copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path); + unlink ($this->dest_folder.'netpbm.tmp'); + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Rotate Using GD + * + * @access public + * @return bool + */ + function image_rotate_gd() + { + // Is Image Rotation Supported? + // this function is only supported as of PHP 4.3 + if ( ! function_exists('imagerotate')) + { + $this->set_error('imglib_rotate_unsupported'); + return FALSE; + } + + // Create the image handle + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + // Set the background color + // This won't work with transparent PNG files so we are + // going to have to figure out how to determine the color + // of the alpha channel in a future release. + + $white = imagecolorallocate($src_img, 255, 255, 255); + + // Rotate it! + $dst_img = imagerotate($src_img, $this->rotation_angle, $white); + + // Save the Image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($dst_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($dst_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($dst_img); + imagedestroy($src_img); + + // Set the file to 777 + + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return true; + } + + // -------------------------------------------------------------------- + + /** + * Create Mirror Image using GD + * + * This function will flip horizontal or vertical + * + * @access public + * @return bool + */ + function image_mirror_gd() + { + if ( ! $src_img = $this->image_create_gd()) + { + return FALSE; + } + + $width = $this->orig_width; + $height = $this->orig_height; + + if ($this->rotation_angle == 'hor') + { + for ($i = 0; $i < $height; $i++) + { + $left = 0; + $right = $width-1; + + while ($left < $right) + { + $cl = imagecolorat($src_img, $left, $i); + $cr = imagecolorat($src_img, $right, $i); + + imagesetpixel($src_img, $left, $i, $cr); + imagesetpixel($src_img, $right, $i, $cl); + + $left++; + $right--; + } + } + } + else + { + for ($i = 0; $i < $width; $i++) + { + $top = 0; + $bot = $height-1; + + while ($top < $bot) + { + $ct = imagecolorat($src_img, $i, $top); + $cb = imagecolorat($src_img, $i, $bot); + + imagesetpixel($src_img, $i, $top, $cb); + imagesetpixel($src_img, $i, $bot, $ct); + + $top++; + $bot--; + } + } + } + + // Show the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($src_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($src_img); + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Watermark + * + * This is a wrapper function that chooses the type + * of watermarking based on the specified preference. + * + * @access public + * @param string + * @return bool + */ + function watermark() + { + if ($this->wm_type == 'overlay') + { + return $this->overlay_watermark(); + } + else + { + return $this->text_watermark(); + } + } + + // -------------------------------------------------------------------- + + /** + * Watermark - Graphic Version + * + * @access public + * @return bool + */ + function overlay_watermark() + { + if ( ! function_exists('imagecolortransparent')) + { + $this->set_error('imglib_gd_required'); + return FALSE; + } + + // Fetch source image properties + $this->get_image_properties(); + + // Fetch watermark image properties + $props = $this->get_image_properties($this->wm_overlay_path, TRUE); + $wm_img_type = $props['image_type']; + $wm_width = $props['width']; + $wm_height = $props['height']; + + // Create two image resources + $wm_img = $this->image_create_gd($this->wm_overlay_path, $wm_img_type); + $src_img = $this->image_create_gd($this->full_src_path); + + // Reverse the offset if necessary + // When the image is positioned at the bottom + // we don't want the vertical offset to push it + // further down. We want the reverse, so we'll + // invert the offset. Same with the horizontal + // offset when the image is at the right + + $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1)); + $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1)); + + if ($this->wm_vrt_alignment == 'B') + $this->wm_vrt_offset = $this->wm_vrt_offset * -1; + + if ($this->wm_hor_alignment == 'R') + $this->wm_hor_offset = $this->wm_hor_offset * -1; + + // Set the base x and y axis values + $x_axis = $this->wm_hor_offset + $this->wm_padding; + $y_axis = $this->wm_vrt_offset + $this->wm_padding; + + // Set the vertical position + switch ($this->wm_vrt_alignment) + { + case 'T': + break; + case 'M': $y_axis += ($this->orig_height / 2) - ($wm_height / 2); + break; + case 'B': $y_axis += $this->orig_height - $wm_height; + break; + } + + // Set the horizontal position + switch ($this->wm_hor_alignment) + { + case 'L': + break; + case 'C': $x_axis += ($this->orig_width / 2) - ($wm_width / 2); + break; + case 'R': $x_axis += $this->orig_width - $wm_width; + break; + } + + // Build the finalized image + if ($wm_img_type == 3 AND function_exists('imagealphablending')) + { + @imagealphablending($src_img, TRUE); + } + + // Set RGB values for text and shadow + $rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp); + $alpha = ($rgba & 0x7F000000) >> 24; + + // make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency + if ($alpha > 0) + { + // copy the image directly, the image's alpha transparency being the sole determinant of blending + imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height); + } + else + { + // set our RGB value from above to be transparent and merge the images with the specified opacity + imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp)); + imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity); + } + + // Output the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + if ( ! $this->image_save_gd($src_img)) + { + return FALSE; + } + } + + imagedestroy($src_img); + imagedestroy($wm_img); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Watermark - Text Version + * + * @access public + * @return bool + */ + function text_watermark() + { + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path)) + { + $this->set_error('imglib_missing_font'); + return FALSE; + } + + // Fetch source image properties + $this->get_image_properties(); + + // Set RGB values for text and shadow + $this->wm_font_color = str_replace('#', '', $this->wm_font_color); + $this->wm_shadow_color = str_replace('#', '', $this->wm_shadow_color); + + $R1 = hexdec(substr($this->wm_font_color, 0, 2)); + $G1 = hexdec(substr($this->wm_font_color, 2, 2)); + $B1 = hexdec(substr($this->wm_font_color, 4, 2)); + + $R2 = hexdec(substr($this->wm_shadow_color, 0, 2)); + $G2 = hexdec(substr($this->wm_shadow_color, 2, 2)); + $B2 = hexdec(substr($this->wm_shadow_color, 4, 2)); + + $txt_color = imagecolorclosest($src_img, $R1, $G1, $B1); + $drp_color = imagecolorclosest($src_img, $R2, $G2, $B2); + + // Reverse the vertical offset + // When the image is positioned at the bottom + // we don't want the vertical offset to push it + // further down. We want the reverse, so we'll + // invert the offset. Note: The horizontal + // offset flips itself automatically + + if ($this->wm_vrt_alignment == 'B') + $this->wm_vrt_offset = $this->wm_vrt_offset * -1; + + if ($this->wm_hor_alignment == 'R') + $this->wm_hor_offset = $this->wm_hor_offset * -1; + + // Set font width and height + // These are calculated differently depending on + // whether we are using the true type font or not + if ($this->wm_use_truetype == TRUE) + { + if ($this->wm_font_size == '') + $this->wm_font_size = '17'; + + $fontwidth = $this->wm_font_size-($this->wm_font_size/4); + $fontheight = $this->wm_font_size; + $this->wm_vrt_offset += $this->wm_font_size; + } + else + { + $fontwidth = imagefontwidth($this->wm_font_size); + $fontheight = imagefontheight($this->wm_font_size); + } + + // Set base X and Y axis values + $x_axis = $this->wm_hor_offset + $this->wm_padding; + $y_axis = $this->wm_vrt_offset + $this->wm_padding; + + // Set verticle alignment + if ($this->wm_use_drop_shadow == FALSE) + $this->wm_shadow_distance = 0; + + $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1)); + $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1)); + + switch ($this->wm_vrt_alignment) + { + case "T" : + break; + case "M": $y_axis += ($this->orig_height/2)+($fontheight/2); + break; + case "B": $y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2)); + break; + } + + $x_shad = $x_axis + $this->wm_shadow_distance; + $y_shad = $y_axis + $this->wm_shadow_distance; + + // Set horizontal alignment + switch ($this->wm_hor_alignment) + { + case "L": + break; + case "R": + if ($this->wm_use_drop_shadow) + $x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text)); + $x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text)); + break; + case "C": + if ($this->wm_use_drop_shadow) + $x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2); + $x_axis += floor(($this->orig_width -$fontwidth*strlen($this->wm_text))/2); + break; + } + + // Add the text to the source image + if ($this->wm_use_truetype) + { + if ($this->wm_use_drop_shadow) + imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text); + imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text); + } + else + { + if ($this->wm_use_drop_shadow) + imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); + imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); + } + + // Output the final image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + $this->image_save_gd($src_img); + } + + imagedestroy($src_img); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Create Image - GD + * + * This simply creates an image resource handle + * based on the type of image being processed + * + * @access public + * @param string + * @return resource + */ + function image_create_gd($path = '', $image_type = '') + { + if ($path == '') + $path = $this->full_src_path; + + if ($image_type == '') + $image_type = $this->image_type; + + + switch ($image_type) + { + case 1 : + if ( ! function_exists('imagecreatefromgif')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); + return FALSE; + } + + return imagecreatefromgif($path); + break; + case 2 : + if ( ! function_exists('imagecreatefromjpeg')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); + return FALSE; + } + + return imagecreatefromjpeg($path); + break; + case 3 : + if ( ! function_exists('imagecreatefrompng')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); + return FALSE; + } + + return imagecreatefrompng($path); + break; + + } + + $this->set_error(array('imglib_unsupported_imagecreate')); + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Write image file to disk - GD + * + * Takes an image resource as input and writes the file + * to the specified destination + * + * @access public + * @param resource + * @return bool + */ + function image_save_gd($resource) + { + switch ($this->image_type) + { + case 1 : + if ( ! function_exists('imagegif')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); + return FALSE; + } + + @imagegif($resource, $this->full_dst_path); + break; + case 2 : + if ( ! function_exists('imagejpeg')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); + return FALSE; + } + + if (phpversion() == '4.4.1') + { + @touch($this->full_dst_path); // PHP 4.4.1 bug #35060 - workaround + } + + @imagejpeg($resource, $this->full_dst_path, $this->quality); + break; + case 3 : + if ( ! function_exists('imagepng')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); + return FALSE; + } + + @imagepng($resource, $this->full_dst_path); + break; + default : + $this->set_error(array('imglib_unsupported_imagecreate')); + return FALSE; + break; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Dynamically outputs an image + * + * @access public + * @param resource + * @return void + */ + function image_display_gd($resource) + { + header("Content-Disposition: filename={$this->source_image};"); + header("Content-Type: {$this->mime_type}"); + header('Content-Transfer-Encoding: binary'); + header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT'); + + switch ($this->image_type) + { + case 1 : imagegif($resource); + break; + case 2 : imagejpeg($resource, '', $this->quality); + break; + case 3 : imagepng($resource); + break; + default : echo 'Unable to display the image'; + break; + } + } + + // -------------------------------------------------------------------- + + /** + * Re-proportion Image Width/Height + * + * When creating thumbs, the desired width/height + * can end up warping the image due to an incorrect + * ratio between the full-sized image and the thumb. + * + * This function lets us re-proportion the width/height + * if users choose to maintain the aspect ratio when resizing. + * + * @access public + * @return void + */ + function image_reproportion() + { + if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0) + return; + + if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0) + return; + + $new_width = ceil($this->orig_width*$this->height/$this->orig_height); + $new_height = ceil($this->width*$this->orig_height/$this->orig_width); + + $ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width)); + + if ($this->master_dim != 'width' AND $this->master_dim != 'height') + { + $this->master_dim = ($ratio < 0) ? 'width' : 'height'; + } + + if (($this->width != $new_width) AND ($this->height != $new_height)) + { + if ($this->master_dim == 'height') + { + $this->width = $new_width; + } + else + { + $this->height = $new_height; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Get image properties + * + * A helper function that gets info about the file + * + * @access public + * @param string + * @return mixed + */ + function get_image_properties($path = '', $return = FALSE) + { + // For now we require GD but we should + // find a way to determine this using IM or NetPBM + + if ($path == '') + $path = $this->full_src_path; + + if ( ! file_exists($path)) + { + $this->set_error('imglib_invalid_path'); + return FALSE; + } + + $vals = @getimagesize($path); + + $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); + + $mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg'; + + if ($return == TRUE) + { + $v['width'] = $vals['0']; + $v['height'] = $vals['1']; + $v['image_type'] = $vals['2']; + $v['size_str'] = $vals['3']; + $v['mime_type'] = $mime; + + return $v; + } + + $this->orig_width = $vals['0']; + $this->orig_height = $vals['1']; + $this->image_type = $vals['2']; + $this->size_str = $vals['3']; + $this->mime_type = $mime; + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Size calculator + * + * This function takes a known width x height and + * recalculates it to a new size. Only one + * new variable needs to be known + * + * $props = array( + * 'width' => $width, + * 'height' => $height, + * 'new_width' => 40, + * 'new_height' => '' + * ); + * + * @access public + * @param array + * @return array + */ + function size_calculator($vals) + { + if ( ! is_array($vals)) + return; + + $allowed = array('new_width', 'new_height', 'width', 'height'); + + foreach ($allowed as $item) + { + if ( ! isset($vals[$item]) OR $vals[$item] == '') + $vals[$item] = 0; + } + + if ($vals['width'] == 0 OR $vals['height'] == 0) + { + return $vals; + } + + if ($vals['new_width'] == 0) + { + $vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']); + } + elseif ($vals['new_height'] == 0) + { + $vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']); + } + + return $vals; + } + + // -------------------------------------------------------------------- + + /** + * Explode source_image + * + * This is a helper function that extracts the extension + * from the source_image. This function lets us deal with + * source_images with multiple periods, like: my.cool.jpg + * It returns an associative array with two elements: + * $array['ext'] = '.jpg'; + * $array['name'] = 'my.cool'; + * + * @access public + * @param array + * @return array + */ + function explode_name($source_image) + { + $x = explode('.', $source_image); + $ret['ext'] = '.'.end($x); + + $name = ''; + + $ct = count($x)-1; + + for ($i = 0; $i < $ct; $i++) + { + $name .= $x[$i]; + + if ($i < ($ct - 1)) + { + $name .= '.'; + } + } + + $ret['name'] = $name; + + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Is GD Installed? + * + * @access public + * @return bool + */ + function gd_loaded() + { + if ( ! extension_loaded('gd')) + { + if ( ! dl('gd.so')) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Get GD version + * + * @access public + * @return mixed + */ + function gd_version() + { + if (function_exists('gd_info')) + { + $gd_version = @gd_info(); + $gd_version = preg_replace("/\D/", "", $gd_version['GD Version']); + + return $gd_version; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set error message + * + * @access public + * @param string + * @return void + */ + function set_error($msg) + { + $CI =& get_instance(); + $CI->lang->load('imglib'); + + if (is_array($msg)) + { + foreach ($msg as $val) + { + + $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + else + { + $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + + // -------------------------------------------------------------------- + + /** + * Show error messages + * + * @access public + * @param string + * @return string + */ + function display_errors($open = '

', $close = '

') + { + $str = ''; + foreach ($this->error_msg as $val) + { + $str .= $open.$val.$close; + } + + return $str; + } + +} +// END Image_lib Class + +/* End of file Image_lib.php */ +/* Location: ./system/libraries/Image_lib.php */ \ No newline at end of file diff --git a/libraries/Input.php b/libraries/Input.php new file mode 100644 index 0000000..e66d481 --- /dev/null +++ b/libraries/Input.php @@ -0,0 +1,1059 @@ + '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + 'window.location' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[' + ); + /* never allowed, regex replacement */ + var $never_allowed_regex = array( + "javascript\s*:" => '[removed]', + "expression\s*\(" => '[removed]', // CSS and IE + "Redirect\s+302" => '[removed]' + ); + + /** + * Constructor + * + * Sets whether to globally enable the XSS processing + * and whether to allow the $_GET array + * + * @access public + */ + function CI_Input() + { + log_message('debug', "Input Class Initialized"); + + $CFG =& load_class('Config'); + $this->use_xss_clean = ($CFG->item('global_xss_filtering') === TRUE) ? TRUE : FALSE; + $this->allow_get_array = ($CFG->item('enable_query_strings') === TRUE) ? TRUE : FALSE; + $this->_sanitize_globals(); + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Globals + * + * This function does the following: + * + * Unsets $_GET data (if query strings are not enabled) + * + * Unsets all globals if register_globals is enabled + * + * Standardizes newline characters to \n + * + * @access private + * @return void + */ + function _sanitize_globals() + { + // Would kind of be "wrong" to unset any of these GLOBALS + $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA', + 'system_folder', 'application_folder', 'BM', 'EXT', 'CFG', 'URI', 'RTR', 'OUT', 'IN'); + + // Unset globals for security. + // This is effectively the same as register_globals = off + foreach (array($_GET, $_POST, $_COOKIE, $_SERVER, $_FILES, $_ENV, (isset($_SESSION) && is_array($_SESSION)) ? $_SESSION : array()) as $global) + { + if ( ! is_array($global)) + { + if ( ! in_array($global, $protected)) + { + unset($GLOBALS[$global]); + } + } + else + { + foreach ($global as $key => $val) + { + if ( ! in_array($key, $protected)) + { + unset($GLOBALS[$key]); + } + + if (is_array($val)) + { + foreach($val as $k => $v) + { + if ( ! in_array($k, $protected)) + { + unset($GLOBALS[$k]); + } + } + } + } + } + } + + // Is $_GET data allowed? If not we'll set the $_GET to an empty array + if ($this->allow_get_array == FALSE) + { + $_GET = array(); + } + else + { + $_GET = $this->_clean_input_data($_GET); + } + + // Clean $_POST Data + $_POST = $this->_clean_input_data($_POST); + + // Clean $_COOKIE Data + // Also get rid of specially treated cookies that might be set by a server + // or silly application, that are of no use to a CI application anyway + // but that when present will trip our 'Disallowed Key Characters' alarm + // http://www.ietf.org/rfc/rfc2109.txt + // note that the key names below are single quoted strings, and are not PHP variables + unset($_COOKIE['$Version']); + unset($_COOKIE['$Path']); + unset($_COOKIE['$Domain']); + $_COOKIE = $this->_clean_input_data($_COOKIE); + + log_message('debug', "Global POST and COOKIE data sanitized"); + } + + // -------------------------------------------------------------------- + + /** + * Clean Input Data + * + * This is a helper function. It escapes data and + * standardizes newline characters to \n + * + * @access private + * @param string + * @return string + */ + function _clean_input_data($str) + { + if (is_array($str)) + { + $new_array = array(); + foreach ($str as $key => $val) + { + $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + return $new_array; + } + + // We strip slashes if magic quotes is on to keep things consistent + if (get_magic_quotes_gpc()) + { + $str = stripslashes($str); + } + + // Should we filter the input data? + if ($this->use_xss_clean === TRUE) + { + $str = $this->xss_clean($str); + } + + // Standardize newlines + if (strpos($str, "\r") !== FALSE) + { + $str = str_replace(array("\r\n", "\r"), "\n", $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Clean Keys + * + * This is a helper function. To prevent malicious users + * from trying to exploit keys we make sure that keys are + * only named with alpha-numeric text and a few other items. + * + * @access private + * @param string + * @return string + */ + function _clean_input_keys($str) + { + if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str)) + { + exit('Disallowed Key Characters.'); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Fetch from array + * + * This is a helper function to retrieve values from global arrays + * + * @access private + * @param array + * @param string + * @param bool + * @return string + */ + function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) + { + if ( ! isset($array[$index])) + { + return FALSE; + } + + if ($xss_clean === TRUE) + { + return $this->xss_clean($array[$index]); + } + + return $array[$index]; + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the GET array + * + * @access public + * @param string + * @param bool + * @return string + */ + function get($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_GET, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the POST array + * + * @access public + * @param string + * @param bool + * @return string + */ + function post($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_POST, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from either the GET array or the POST + * + * @access public + * @param string The index key + * @param bool XSS cleaning + * @return string + */ + function get_post($index = '', $xss_clean = FALSE) + { + if ( ! isset($_POST[$index]) ) + { + return $this->get($index, $xss_clean); + } + else + { + return $this->post($index, $xss_clean); + } + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the COOKIE array + * + * @access public + * @param string + * @param bool + * @return string + */ + function cookie($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the SERVER array + * + * @access public + * @param string + * @param bool + * @return string + */ + function server($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_SERVER, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the IP Address + * + * @access public + * @return string + */ + function ip_address() + { + if ($this->ip_address !== FALSE) + { + return $this->ip_address; + } + + if ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP')) + { + $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; + } + elseif ($this->server('REMOTE_ADDR')) + { + $this->ip_address = $_SERVER['REMOTE_ADDR']; + } + elseif ($this->server('HTTP_CLIENT_IP')) + { + $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; + } + elseif ($this->server('HTTP_X_FORWARDED_FOR')) + { + $this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; + } + + if ($this->ip_address === FALSE) + { + $this->ip_address = '0.0.0.0'; + return $this->ip_address; + } + + if (strstr($this->ip_address, ',')) + { + $x = explode(',', $this->ip_address); + $this->ip_address = end($x); + } + + if ( ! $this->valid_ip($this->ip_address)) + { + $this->ip_address = '0.0.0.0'; + } + + return $this->ip_address; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * Updated version suggested by Geert De Deckere + * + * @access public + * @param string + * @return string + */ + function valid_ip($ip) + { + $ip_segments = explode('.', $ip); + + // Always 4 segments needed + if (count($ip_segments) != 4) + { + return FALSE; + } + // IP can not start with 0 + if ($ip_segments[0][0] == '0') + { + return FALSE; + } + // Check each segment + foreach ($ip_segments as $segment) + { + // IP segments must be digits and can not be + // longer than 3 digits or greater then 255 + if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * User Agent + * + * @access public + * @return string + */ + function user_agent() + { + if ($this->user_agent !== FALSE) + { + return $this->user_agent; + } + + $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT']; + + return $this->user_agent; + } + + // -------------------------------------------------------------------- + + /** + * Filename Security + * + * @access public + * @param string + * @return string + */ + function filename_security($str) + { + $bad = array( + "../", + "./", + "", + "<", + ">", + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); + + return stripslashes(str_replace($bad, '', $str)); + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. This function does a fair amount of work but + * it is extremely thorough, designed to prevent even the + * most obscure XSS attempts. Nothing is ever 100% foolproof, + * of course, but I haven't been able to get anything passed + * the filter. + * + * Note: This function should only be used to deal with data + * upon submission. It's not something that should + * be used for general runtime processing. + * + * This function was based in part on some code and ideas I + * got from Bitflux: http://blog.bitflux.ch/wiki/XSS_Prevention + * + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs: + * http://ha.ckers.org/xss.html + * + * @access public + * @param string + * @return string + */ + function xss_clean($str, $is_image = FALSE) + { + /* + * Is the string an array? + * + */ + if (is_array($str)) + { + while (list($key) = each($str)) + { + $str[$key] = $this->xss_clean($str[$key]); + } + + return $str; + } + + /* + * Remove Invisible Characters + */ + $str = $this->_remove_invisible_characters($str); + + /* + * Protect GET variables in URLs + */ + + // 901119URL5918AMP18930PROTECT8198 + + $str = preg_replace('|\&([a-z\_0-9]+)\=([a-z\_0-9]+)|i', $this->xss_hash()."\\1=\\2", $str); + + /* + * Validate standard character entities + * + * Add a semicolon if missing. We do this to enable + * the conversion of entities to ASCII later. + * + */ + $str = preg_replace('#(&\#?[0-9a-z]{2,})[\x00-\x20]*;?#i', "\\1;", $str); + + /* + * Validate UTF16 two byte encoding (x00) + * + * Just as above, adds a semicolon if missing. + * + */ + $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); + + /* + * Un-Protect GET variables in URLs + */ + $str = str_replace($this->xss_hash(), '&', $str); + + /* + * URL Decode + * + * Just in case stuff like this is submitted: + * + * Google + * + * Note: Use rawurldecode() so it does not remove plus signs + * + */ + $str = rawurldecode($str); + + /* + * Convert character entities to ASCII + * + * This permits our tests below to work reliably. + * We only convert entities that are within tags since + * these are the ones that will pose security problems. + * + */ + + $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); + + $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_html_entity_decode_callback'), $str); + + /* + * Remove Invisible Characters Again! + */ + $str = $this->_remove_invisible_characters($str); + + /* + * Convert all tabs to spaces + * + * This prevents strings like this: ja vascript + * NOTE: we deal with spaces between characters later. + * NOTE: preg_replace was found to be amazingly slow here on large blocks of data, + * so we use str_replace. + * + */ + + if (strpos($str, "\t") !== FALSE) + { + $str = str_replace("\t", ' ', $str); + } + + /* + * Capture converted string for later comparison + */ + $converted_string = $str; + + /* + * Not Allowed Under Any Conditions + */ + + foreach ($this->never_allowed_str as $key => $val) + { + $str = str_replace($key, $val, $str); + } + + foreach ($this->never_allowed_regex as $key => $val) + { + $str = preg_replace("#".$key."#i", $val, $str); + } + + /* + * Makes PHP tags safe + * + * Note: XML tags are inadvertently replaced too: + * + * '), array('<?php', '<?PHP', '<?', '?>'), $str); + } + + /* + * Compact any exploded words + * + * This corrects words like: j a v a s c r i p t + * These words are compacted back to their correct state. + * + */ + $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window'); + foreach ($words as $word) + { + $temp = ''; + + for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) + { + $temp .= substr($word, $i, 1)."\s*"; + } + + // We only want to do this when it is followed by a non-word character + // That way valid stuff like "dealer to" does not become "dealerto" + $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); + } + + /* + * Remove disallowed Javascript in links or img tags + * We used to do some version comparisons and use of stripos for PHP5, but it is dog slow compared + * to these simplified non-capturing preg_match(), especially if the pattern exists in the string + */ + do + { + $original = $str; + + if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str); + } + + if (preg_match("/]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str); + } + + if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str)) + { + $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str); + } + } + while($original != $str); + + unset($original); + + /* + * Remove JavaScript Event Handlers + * + * Note: This code is a little blunt. It removes + * the event handler and anything up to the closing >, + * but it's unlikely to be a problem. + * + */ + $event_handlers = array('[^a-z_\-]on\w*','xmlns'); + + if ($is_image === TRUE) + { + /* + * Adobe Photoshop puts XML metadata into JFIF images, including namespacing, + * so we have to allow this for images. -Paul + */ + unset($event_handlers[array_search('xmlns', $event_handlers)]); + } + + $str = preg_replace("#<([^><]+?)(".implode('|', $event_handlers).")(\s*=\s*[^><]*)([><]*)#i", "<\\1\\4", $str); + + /* + * Sanitize naughty HTML elements + * + * If a tag containing any of the words in the list + * below is found, the tag gets converted to entities. + * + * So this: + * Becomes: <blink> + * + */ + $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; + $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); + + /* + * Sanitize naughty scripting elements + * + * Similar to above, only instead of looking for + * tags it looks for PHP and JavaScript commands + * that are disallowed. Rather than removing the + * code, it simply converts the parenthesis to entities + * rendering the code un-executable. + * + * For example: eval('some code') + * Becomes: eval('some code') + * + */ + $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2(\\3)", $str); + + /* + * Final clean up + * + * This adds a bit of extra precaution in case + * something got through the above filters + * + */ + foreach ($this->never_allowed_str as $key => $val) + { + $str = str_replace($key, $val, $str); + } + + foreach ($this->never_allowed_regex as $key => $val) + { + $str = preg_replace("#".$key."#i", $val, $str); + } + + /* + * Images are Handled in a Special Way + * - Essentially, we want to know that after all of the character conversion is done whether + * any unwanted, likely XSS, code was found. If not, we return TRUE, as the image is clean. + * However, if the string post-conversion does not matched the string post-removal of XSS, + * then it fails, as there was unwanted XSS code found and removed/changed during processing. + */ + + if ($is_image === TRUE) + { + if ($str == $converted_string) + { + return TRUE; + } + else + { + return FALSE; + } + } + + log_message('debug', "XSS Filtering completed"); + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Random Hash for protecting URLs + * + * @access public + * @return string + */ + function xss_hash() + { + if ($this->xss_hash == '') + { + if (phpversion() >= 4.2) + mt_srand(); + else + mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff); + + $this->xss_hash = md5(time() + mt_rand(0, 1999999999)); + } + + return $this->xss_hash; + } + + // -------------------------------------------------------------------- + + /** + * Remove Invisible Characters + * + * This prevents sandwiching null characters + * between ascii characters, like Java\0script. + * + * @access public + * @param string + * @return string + */ + function _remove_invisible_characters($str) + { + static $non_displayables; + + if ( ! isset($non_displayables)) + { + // every control character except newline (dec 10), carriage return (dec 13), and horizontal tab (dec 09), + $non_displayables = array( + '/%0[0-8bcef]/', // url encoded 00-08, 11, 12, 14, 15 + '/%1[0-9a-f]/', // url encoded 16-31 + '/[\x00-\x08]/', // 00-08 + '/\x0b/', '/\x0c/', // 11, 12 + '/[\x0e-\x1f]/' // 14-31 + ); + } + + do + { + $cleaned = $str; + $str = preg_replace($non_displayables, '', $str); + } + while ($cleaned != $str); + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Compact Exploded Words + * + * Callback function for xss_clean() to remove whitespace from + * things like j a v a s c r i p t + * + * @access public + * @param type + * @return type + */ + function _compact_exploded_words($matches) + { + return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Naughty HTML + * + * Callback function for xss_clean() to remove naughty HTML elements + * + * @access private + * @param array + * @return string + */ + function _sanitize_naughty_html($matches) + { + // encode opening brace + $str = '<'.$matches[1].$matches[2].$matches[3]; + + // encode captured opening or closing brace to prevent recursive vectors + $str .= str_replace(array('>', '<'), array('>', '<'), $matches[4]); + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * JS Link Removal + * + * Callback function for xss_clean() to sanitize links + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on link-heavy strings + * + * @access private + * @param array + * @return string + */ + function _js_link_removal($match) + { + $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); + return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])); + return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|', '<'), array('>', '<'), $match[0]); + } + + // -------------------------------------------------------------------- + + /** + * HTML Entity Decode Callback + * + * Used as a callback for XSS Clean + * + * @access public + * @param array + * @return string + */ + function _html_entity_decode_callback($match) + { + $CFG =& load_class('Config'); + $charset = $CFG->item('charset'); + + return $this->_html_entity_decode($match[0], strtoupper($charset)); + } + + // -------------------------------------------------------------------- + + /** + * HTML Entities Decode + * + * This function is a replacement for html_entity_decode() + * + * In some versions of PHP the native function does not work + * when UTF-8 is the specified character set, so this gives us + * a work-around. More info here: + * http://bugs.php.net/bug.php?id=25670 + * + * @access private + * @param string + * @param string + * @return string + */ + /* ------------------------------------------------- + /* Replacement for html_entity_decode() + /* -------------------------------------------------*/ + + /* + NOTE: html_entity_decode() has a bug in some PHP versions when UTF-8 is the + character set, and the PHP developers said they were not back porting the + fix to versions other than PHP 5.x. + */ + function _html_entity_decode($str, $charset='UTF-8') + { + if (stristr($str, '&') === FALSE) return $str; + + // The reason we are not using html_entity_decode() by itself is because + // while it is not technically correct to leave out the semicolon + // at the end of an entity most browsers will still interpret the entity + // correctly. html_entity_decode() does not convert entities without + // semicolons, so we are left with our own little solution here. Bummer. + + if (function_exists('html_entity_decode') && (strtolower($charset) != 'utf-8' OR version_compare(phpversion(), '5.0.0', '>='))) + { + $str = html_entity_decode($str, ENT_COMPAT, $charset); + $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); + return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); + } + + // Numeric Entities + $str = preg_replace('~&#x(0*[0-9a-f]{2,5});{0,1}~ei', 'chr(hexdec("\\1"))', $str); + $str = preg_replace('~&#([0-9]{2,4});{0,1}~e', 'chr(\\1)', $str); + + // Literal Entities - Slightly slow so we do another check + if (stristr($str, '&') === FALSE) + { + $str = strtr($str, array_flip(get_html_translation_table(HTML_ENTITIES))); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Filter Attributes + * + * Filters tag attributes for consistency and safety + * + * @access public + * @param string + * @return string + */ + function _filter_attributes($str) + { + $out = ''; + + if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) + { + foreach ($matches[0] as $match) + { + $out .= "{$match}"; + } + } + + return $out; + } + + // -------------------------------------------------------------------- + +} +// END Input class + +/* End of file Input.php */ +/* Location: ./system/libraries/Input.php */ \ No newline at end of file diff --git a/libraries/Language.php b/libraries/Language.php new file mode 100644 index 0000000..eb4613d --- /dev/null +++ b/libraries/Language.php @@ -0,0 +1,124 @@ +is_loaded, TRUE)) + { + return; + } + + if ($idiom == '') + { + $CI =& get_instance(); + $deft_lang = $CI->config->item('language'); + $idiom = ($deft_lang == '') ? 'english' : $deft_lang; + } + + // Determine where the language file is and load it + if (file_exists(APPPATH.'language/'.$idiom.'/'.$langfile)) + { + include(APPPATH.'language/'.$idiom.'/'.$langfile); + } + else + { + if (file_exists(BASEPATH.'language/'.$idiom.'/'.$langfile)) + { + include(BASEPATH.'language/'.$idiom.'/'.$langfile); + } + else + { + show_error('Unable to load the requested language file: language/'.$langfile); + } + } + + + if ( ! isset($lang)) + { + log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile); + return; + } + + if ($return == TRUE) + { + return $lang; + } + + $this->is_loaded[] = $langfile; + $this->language = array_merge($this->language, $lang); + unset($lang); + + log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a single line of text from the language array + * + * @access public + * @param string $line the language line + * @return string + */ + function line($line = '') + { + $line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line]; + return $line; + } + +} +// END Language Class + +/* End of file Language.php */ +/* Location: ./system/libraries/Language.php */ \ No newline at end of file diff --git a/libraries/Loader.php b/libraries/Loader.php new file mode 100644 index 0000000..da8d9e7 --- /dev/null +++ b/libraries/Loader.php @@ -0,0 +1,1088 @@ + 'unit', 'user_agent' => 'agent'); + + + /** + * Constructor + * + * Sets the path to the view files and gets the initial output buffering level + * + * @access public + */ + function CI_Loader() + { + $this->_ci_is_php5 = (floor(phpversion()) >= 5) ? TRUE : FALSE; + $this->_ci_view_path = APPPATH.'views/'; + $this->_ci_ob_level = ob_get_level(); + + log_message('debug', "Loader Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Class Loader + * + * This function lets users load and instantiate classes. + * It is designed to be called from a user's app controllers. + * + * @access public + * @param string the name of the class + * @param mixed the optional parameters + * @param string an optional object name + * @return void + */ + function library($library = '', $params = NULL, $object_name = NULL) + { + if ($library == '') + { + return FALSE; + } + + if ( ! is_null($params) AND ! is_array($params)) + { + $params = NULL; + } + + if (is_array($library)) + { + foreach ($library as $class) + { + $this->_ci_load_class($class, $params, $object_name); + } + } + else + { + $this->_ci_load_class($library, $params, $object_name); + } + + $this->_ci_assign_to_models(); + } + + // -------------------------------------------------------------------- + + /** + * Model Loader + * + * This function lets users load and instantiate models. + * + * @access public + * @param string the name of the class + * @param string name for the model + * @param bool database connection + * @return void + */ + function model($model, $name = '', $db_conn = FALSE) + { + if (is_array($model)) + { + foreach($model as $babe) + { + $this->model($babe); + } + return; + } + + if ($model == '') + { + return; + } + + // Is the model in a sub-folder? If so, parse out the filename and path. + if (strpos($model, '/') === FALSE) + { + $path = ''; + } + else + { + $x = explode('/', $model); + $model = end($x); + unset($x[count($x)-1]); + $path = implode('/', $x).'/'; + } + + if ($name == '') + { + $name = $model; + } + + if (in_array($name, $this->_ci_models, TRUE)) + { + return; + } + + $CI =& get_instance(); + if (isset($CI->$name)) + { + show_error('The model name you are loading is the name of a resource that is already being used: '.$name); + } + + $model = strtolower($model); + + if ( ! file_exists(APPPATH.'models/'.$path.$model.EXT)) + { + show_error('Unable to locate the model you have specified: '.$model); + } + + if ($db_conn !== FALSE AND ! class_exists('CI_DB')) + { + if ($db_conn === TRUE) + $db_conn = ''; + + $CI->load->database($db_conn, FALSE, TRUE); + } + + if ( ! class_exists('Model')) + { + load_class('Model', FALSE); + } + + require_once(APPPATH.'models/'.$path.$model.EXT); + + $model = ucfirst($model); + + $CI->$name = new $model(); + $CI->$name->_assign_libraries(); + + $this->_ci_models[] = $name; + } + + // -------------------------------------------------------------------- + + /** + * Database Loader + * + * @access public + * @param string the DB credentials + * @param bool whether to return the DB object + * @param bool whether to enable active record (this allows us to override the config setting) + * @return object + */ + function database($params = '', $return = FALSE, $active_record = FALSE) + { + // Grab the super object + $CI =& get_instance(); + + // Do we even need to load the database class? + if (class_exists('CI_DB') AND $return == FALSE AND $active_record == FALSE AND isset($CI->db) AND is_object($CI->db)) + { + return FALSE; + } + + require_once(BASEPATH.'database/DB'.EXT); + + if ($return === TRUE) + { + return DB($params, $active_record); + } + + // Initialize the db variable. Needed to prevent + // reference errors with some configurations + $CI->db = ''; + + // Load the DB class + $CI->db =& DB($params, $active_record); + + // Assign the DB object to any existing models + $this->_ci_assign_to_models(); + } + + // -------------------------------------------------------------------- + + /** + * Load the Utilities Class + * + * @access public + * @return string + */ + function dbutil() + { + if ( ! class_exists('CI_DB')) + { + $this->database(); + } + + $CI =& get_instance(); + + // for backwards compatibility, load dbforge so we can extend dbutils off it + // this use is deprecated and strongly discouraged + $CI->load->dbforge(); + + require_once(BASEPATH.'database/DB_utility'.EXT); + require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility'.EXT); + $class = 'CI_DB_'.$CI->db->dbdriver.'_utility'; + + $CI->dbutil =& new $class(); + + $CI->load->_ci_assign_to_models(); + } + + // -------------------------------------------------------------------- + + /** + * Load the Database Forge Class + * + * @access public + * @return string + */ + function dbforge() + { + if ( ! class_exists('CI_DB')) + { + $this->database(); + } + + $CI =& get_instance(); + + require_once(BASEPATH.'database/DB_forge'.EXT); + require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT); + $class = 'CI_DB_'.$CI->db->dbdriver.'_forge'; + + $CI->dbforge = new $class(); + + $CI->load->_ci_assign_to_models(); + } + + // -------------------------------------------------------------------- + + /** + * Load View + * + * This function is used to load a "view" file. It has three parameters: + * + * 1. The name of the "view" file to be included. + * 2. An associative array of data to be extracted for use in the view. + * 3. TRUE/FALSE - whether to return the data or load it. In + * some cases it's advantageous to be able to return data so that + * a developer can process it in some way. + * + * @access public + * @param string + * @param array + * @param bool + * @return void + */ + function view($view, $vars = array(), $return = FALSE) + { + return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); + } + + // -------------------------------------------------------------------- + + /** + * Load File + * + * This is a generic file loader + * + * @access public + * @param string + * @param bool + * @return string + */ + function file($path, $return = FALSE) + { + return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return)); + } + + // -------------------------------------------------------------------- + + /** + * Set Variables + * + * Once variables are set they become available within + * the controller class and its "view" files. + * + * @access public + * @param array + * @return void + */ + function vars($vars = array(), $val = '') + { + if ($val != '' AND is_string($vars)) + { + $vars = array($vars => $val); + } + + $vars = $this->_ci_object_to_array($vars); + + if (is_array($vars) AND count($vars) > 0) + { + foreach ($vars as $key => $val) + { + $this->_ci_cached_vars[$key] = $val; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Load Helper + * + * This function loads the specified helper file. + * + * @access public + * @param mixed + * @return void + */ + function helper($helpers = array()) + { + if ( ! is_array($helpers)) + { + $helpers = array($helpers); + } + + foreach ($helpers as $helper) + { + $helper = strtolower(str_replace(EXT, '', str_replace('_helper', '', $helper)).'_helper'); + + if (isset($this->_ci_helpers[$helper])) + { + continue; + } + + $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.EXT; + + // Is this a helper extension request? + if (file_exists($ext_helper)) + { + $base_helper = BASEPATH.'helpers/'.$helper.EXT; + + if ( ! file_exists($base_helper)) + { + show_error('Unable to load the requested file: helpers/'.$helper.EXT); + } + + include_once($ext_helper); + include_once($base_helper); + } + elseif (file_exists(APPPATH.'helpers/'.$helper.EXT)) + { + include_once(APPPATH.'helpers/'.$helper.EXT); + } + else + { + if (file_exists(BASEPATH.'helpers/'.$helper.EXT)) + { + include_once(BASEPATH.'helpers/'.$helper.EXT); + } + else + { + show_error('Unable to load the requested file: helpers/'.$helper.EXT); + } + } + + $this->_ci_helpers[$helper] = TRUE; + log_message('debug', 'Helper loaded: '.$helper); + } + } + + // -------------------------------------------------------------------- + + /** + * Load Helpers + * + * This is simply an alias to the above function in case the + * user has written the plural form of this function. + * + * @access public + * @param array + * @return void + */ + function helpers($helpers = array()) + { + $this->helper($helpers); + } + + // -------------------------------------------------------------------- + + /** + * Load Plugin + * + * This function loads the specified plugin. + * + * @access public + * @param array + * @return void + */ + function plugin($plugins = array()) + { + if ( ! is_array($plugins)) + { + $plugins = array($plugins); + } + + foreach ($plugins as $plugin) + { + $plugin = strtolower(str_replace(EXT, '', str_replace('_pi', '', $plugin)).'_pi'); + + if (isset($this->_ci_plugins[$plugin])) + { + continue; + } + + if (file_exists(APPPATH.'plugins/'.$plugin.EXT)) + { + include_once(APPPATH.'plugins/'.$plugin.EXT); + } + else + { + if (file_exists(BASEPATH.'plugins/'.$plugin.EXT)) + { + include_once(BASEPATH.'plugins/'.$plugin.EXT); + } + else + { + show_error('Unable to load the requested file: plugins/'.$plugin.EXT); + } + } + + $this->_ci_plugins[$plugin] = TRUE; + log_message('debug', 'Plugin loaded: '.$plugin); + } + } + + // -------------------------------------------------------------------- + + /** + * Load Plugins + * + * This is simply an alias to the above function in case the + * user has written the plural form of this function. + * + * @access public + * @param array + * @return void + */ + function plugins($plugins = array()) + { + $this->plugin($plugins); + } + + // -------------------------------------------------------------------- + + /** + * Loads a language file + * + * @access public + * @param array + * @param string + * @return void + */ + function language($file = array(), $lang = '') + { + $CI =& get_instance(); + + if ( ! is_array($file)) + { + $file = array($file); + } + + foreach ($file as $langfile) + { + $CI->lang->load($langfile, $lang); + } + } + + /** + * Loads language files for scaffolding + * + * @access public + * @param string + * @return arra + */ + function scaffold_language($file = '', $lang = '', $return = FALSE) + { + $CI =& get_instance(); + return $CI->lang->load($file, $lang, $return); + } + + // -------------------------------------------------------------------- + + /** + * Loads a config file + * + * @access public + * @param string + * @return void + */ + function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) + { + $CI =& get_instance(); + $CI->config->load($file, $use_sections, $fail_gracefully); + } + + // -------------------------------------------------------------------- + + /** + * Scaffolding Loader + * + * This initializing function works a bit different than the + * others. It doesn't load the class. Instead, it simply + * sets a flag indicating that scaffolding is allowed to be + * used. The actual scaffolding function below is + * called by the front controller based on whether the + * second segment of the URL matches the "secret" scaffolding + * word stored in the application/config/routes.php + * + * @access public + * @param string + * @return void + */ + function scaffolding($table = '') + { + if ($table === FALSE) + { + show_error('You must include the name of the table you would like to access when you initialize scaffolding'); + } + + $CI =& get_instance(); + $CI->_ci_scaffolding = TRUE; + $CI->_ci_scaff_table = $table; + } + + // -------------------------------------------------------------------- + + /** + * Loader + * + * This function is used to load views and files. + * Variables are prefixed with _ci_ to avoid symbol collision with + * variables made available to view files + * + * @access private + * @param array + * @return void + */ + function _ci_load($_ci_data) + { + // Set the default data variables + foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) + { + $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val]; + } + + // Set the path to the requested file + if ($_ci_path == '') + { + $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); + $_ci_file = ($_ci_ext == '') ? $_ci_view.EXT : $_ci_view; + $_ci_path = $this->_ci_view_path.$_ci_file; + } + else + { + $_ci_x = explode('/', $_ci_path); + $_ci_file = end($_ci_x); + } + + if ( ! file_exists($_ci_path)) + { + show_error('Unable to load the requested file: '.$_ci_file); + } + + // This allows anything loaded using $this->load (views, files, etc.) + // to become accessible from within the Controller and Model functions. + // Only needed when running PHP 5 + + if ($this->_ci_is_instance()) + { + $_ci_CI =& get_instance(); + foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var) + { + if ( ! isset($this->$_ci_key)) + { + $this->$_ci_key =& $_ci_CI->$_ci_key; + } + } + } + + /* + * Extract and cache variables + * + * You can either set variables using the dedicated $this->load_vars() + * function or via the second parameter of this function. We'll merge + * the two types and cache them so that views that are embedded within + * other views can have access to these variables. + */ + if (is_array($_ci_vars)) + { + $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); + } + extract($this->_ci_cached_vars); + + /* + * Buffer the output + * + * We buffer the output for two reasons: + * 1. Speed. You get a significant speed boost. + * 2. So that the final rendered template can be + * post-processed by the output class. Why do we + * need post processing? For one thing, in order to + * show the elapsed page load time. Unless we + * can intercept the content right before it's sent to + * the browser and then stop the timer it won't be accurate. + */ + ob_start(); + + // If the PHP installation does not support short tags we'll + // do a little string replacement, changing the short tags + // to standard PHP echo statements. + + if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE) + { + echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace(' $this->_ci_ob_level + 1) + { + ob_end_flush(); + } + else + { + // PHP 4 requires that we use a global + global $OUT; + $OUT->append_output(ob_get_contents()); + @ob_end_clean(); + } + } + + // -------------------------------------------------------------------- + + /** + * Load class + * + * This function loads the requested class. + * + * @access private + * @param string the item that is being loaded + * @param mixed any additional parameters + * @param string an optional object name + * @return void + */ + function _ci_load_class($class, $params = NULL, $object_name = NULL) + { + // Get the class name, and while we're at it trim any slashes. + // The directory path can be included as part of the class name, + // but we don't want a leading slash + $class = str_replace(EXT, '', trim($class, '/')); + + // Was the path included with the class name? + // We look for a slash to determine this + $subdir = ''; + if (strpos($class, '/') !== FALSE) + { + // explode the path so we can separate the filename from the path + $x = explode('/', $class); + + // Reset the $class variable now that we know the actual filename + $class = end($x); + + // Kill the filename from the array + unset($x[count($x)-1]); + + // Glue the path back together, sans filename + $subdir = implode($x, '/').'/'; + } + + // We'll test for both lowercase and capitalized versions of the file name + foreach (array(ucfirst($class), strtolower($class)) as $class) + { + $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.EXT; + + // Is this a class extension request? + if (file_exists($subclass)) + { + $baseclass = BASEPATH.'libraries/'.ucfirst($class).EXT; + + if ( ! file_exists($baseclass)) + { + log_message('error', "Unable to load the requested class: ".$class); + show_error("Unable to load the requested class: ".$class); + } + + // Safety: Was the class already loaded by a previous call? + if (in_array($subclass, $this->_ci_loaded_files)) + { + // Before we deem this to be a duplicate request, let's see + // if a custom object name is being supplied. If so, we'll + // return a new instance of the object + if ( ! is_null($object_name)) + { + $CI =& get_instance(); + if ( ! isset($CI->$object_name)) + { + return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); + } + } + + $is_duplicate = TRUE; + log_message('debug', $class." class already loaded. Second attempt ignored."); + return; + } + + include_once($baseclass); + include_once($subclass); + $this->_ci_loaded_files[] = $subclass; + + return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); + } + + // Lets search for the requested library file and load it. + $is_duplicate = FALSE; + for ($i = 1; $i < 3; $i++) + { + $path = ($i % 2) ? APPPATH : BASEPATH; + $filepath = $path.'libraries/'.$subdir.$class.EXT; + + // Does the file exist? No? Bummer... + if ( ! file_exists($filepath)) + { + continue; + } + + // Safety: Was the class already loaded by a previous call? + if (in_array($filepath, $this->_ci_loaded_files)) + { + // Before we deem this to be a duplicate request, let's see + // if a custom object name is being supplied. If so, we'll + // return a new instance of the object + if ( ! is_null($object_name)) + { + $CI =& get_instance(); + if ( ! isset($CI->$object_name)) + { + return $this->_ci_init_class($class, '', $params, $object_name); + } + } + + $is_duplicate = TRUE; + log_message('debug', $class." class already loaded. Second attempt ignored."); + return; + } + + include_once($filepath); + $this->_ci_loaded_files[] = $filepath; + return $this->_ci_init_class($class, '', $params, $object_name); + } + } // END FOREACH + + // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? + if ($subdir == '') + { + $path = strtolower($class).'/'.$class; + return $this->_ci_load_class($path, $params); + } + + // If we got this far we were unable to find the requested class. + // We do not issue errors if the load call failed due to a duplicate request + if ($is_duplicate == FALSE) + { + log_message('error', "Unable to load the requested class: ".$class); + show_error("Unable to load the requested class: ".$class); + } + } + + // -------------------------------------------------------------------- + + /** + * Instantiates a class + * + * @access private + * @param string + * @param string + * @param string an optional object name + * @return null + */ + function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) + { + // Is there an associated config file for this class? + if ($config === NULL) + { + // We test for both uppercase and lowercase, for servers that + // are case-sensitive with regard to file names + if (file_exists(APPPATH.'config/'.strtolower($class).EXT)) + { + include_once(APPPATH.'config/'.strtolower($class).EXT); + } + else + { + if (file_exists(APPPATH.'config/'.ucfirst(strtolower($class)).EXT)) + { + include_once(APPPATH.'config/'.ucfirst(strtolower($class)).EXT); + } + } + } + + if ($prefix == '') + { + if (class_exists('CI_'.$class)) + { + $name = 'CI_'.$class; + } + elseif (class_exists(config_item('subclass_prefix').$class)) + { + $name = config_item('subclass_prefix').$class; + } + else + { + $name = $class; + } + } + else + { + $name = $prefix.$class; + } + + // Is the class name valid? + if ( ! class_exists($name)) + { + log_message('error', "Non-existent class: ".$name); + show_error("Non-existent class: ".$class); + } + + // Set the variable name we will assign the class to + // Was a custom class name supplied? If so we'll use it + $class = strtolower($class); + + if (is_null($object_name)) + { + $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class]; + } + else + { + $classvar = $object_name; + } + + // Save the class name and object name + $this->_ci_classes[$class] = $classvar; + + // Instantiate the class + $CI =& get_instance(); + if ($config !== NULL) + { + $CI->$classvar = new $name($config); + } + else + { + $CI->$classvar = new $name; + } + } + + // -------------------------------------------------------------------- + + /** + * Autoloader + * + * The config/autoload.php file contains an array that permits sub-systems, + * libraries, plugins, and helpers to be loaded automatically. + * + * @access private + * @param array + * @return void + */ + function _ci_autoloader() + { + include_once(APPPATH.'config/autoload'.EXT); + + if ( ! isset($autoload)) + { + return FALSE; + } + + // Load any custom config file + if (count($autoload['config']) > 0) + { + $CI =& get_instance(); + foreach ($autoload['config'] as $key => $val) + { + $CI->config->load($val); + } + } + + // Autoload plugins, helpers and languages + foreach (array('helper', 'plugin', 'language') as $type) + { + if (isset($autoload[$type]) AND count($autoload[$type]) > 0) + { + $this->$type($autoload[$type]); + } + } + + // A little tweak to remain backward compatible + // The $autoload['core'] item was deprecated + if ( ! isset($autoload['libraries'])) + { + $autoload['libraries'] = $autoload['core']; + } + + // Load libraries + if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0) + { + // Load the database driver. + if (in_array('database', $autoload['libraries'])) + { + $this->database(); + $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); + } + + // Load scaffolding + if (in_array('scaffolding', $autoload['libraries'])) + { + $this->scaffolding(); + $autoload['libraries'] = array_diff($autoload['libraries'], array('scaffolding')); + } + + // Load all other libraries + foreach ($autoload['libraries'] as $item) + { + $this->library($item); + } + } + + // Autoload models + if (isset($autoload['model'])) + { + $this->model($autoload['model']); + } + + } + + // -------------------------------------------------------------------- + + /** + * Assign to Models + * + * Makes sure that anything loaded by the loader class (libraries, plugins, etc.) + * will be available to models, if any exist. + * + * @access private + * @param object + * @return array + */ + function _ci_assign_to_models() + { + if (count($this->_ci_models) == 0) + { + return; + } + + if ($this->_ci_is_instance()) + { + $CI =& get_instance(); + foreach ($this->_ci_models as $model) + { + $CI->$model->_assign_libraries(); + } + } + else + { + foreach ($this->_ci_models as $model) + { + $this->$model->_assign_libraries(); + } + } + } + + // -------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @access private + * @param object + * @return array + */ + function _ci_object_to_array($object) + { + return (is_object($object)) ? get_object_vars($object) : $object; + } + + // -------------------------------------------------------------------- + + /** + * Determines whether we should use the CI instance or $this + * + * @access private + * @return bool + */ + function _ci_is_instance() + { + if ($this->_ci_is_php5 == TRUE) + { + return TRUE; + } + + global $CI; + return (is_object($CI)) ? TRUE : FALSE; + } + +} + +/* End of file Loader.php */ +/* Location: ./system/libraries/Loader.php */ \ No newline at end of file diff --git a/libraries/Log.php b/libraries/Log.php new file mode 100644 index 0000000..811c873 --- /dev/null +++ b/libraries/Log.php @@ -0,0 +1,117 @@ + '1', 'DEBUG' => '2', 'INFO' => '3', 'ALL' => '4'); + + /** + * Constructor + * + * @access public + */ + function CI_Log() + { + $config =& get_config(); + + $this->log_path = ($config['log_path'] != '') ? $config['log_path'] : BASEPATH.'logs/'; + + if ( ! is_dir($this->log_path) OR ! is_really_writable($this->log_path)) + { + $this->_enabled = FALSE; + } + + if (is_numeric($config['log_threshold'])) + { + $this->_threshold = $config['log_threshold']; + } + + if ($config['log_date_format'] != '') + { + $this->_date_fmt = $config['log_date_format']; + } + } + + // -------------------------------------------------------------------- + + /** + * Write Log File + * + * Generally this function will be called using the global log_message() function + * + * @access public + * @param string the error level + * @param string the error message + * @param bool whether the error is a native PHP error + * @return bool + */ + function write_log($level = 'error', $msg, $php_error = FALSE) + { + if ($this->_enabled === FALSE) + { + return FALSE; + } + + $level = strtoupper($level); + + if ( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold)) + { + return FALSE; + } + + $filepath = $this->log_path.'log-'.date('Y-m-d').EXT; + $message = ''; + + if ( ! file_exists($filepath)) + { + $message .= "<"."?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); ?".">\n\n"; + } + + if ( ! $fp = @fopen($filepath, FOPEN_WRITE_CREATE)) + { + return FALSE; + } + + $message .= $level.' '.(($level == 'INFO') ? ' -' : '-').' '.date($this->_date_fmt). ' --> '.$msg."\n"; + + flock($fp, LOCK_EX); + fwrite($fp, $message); + flock($fp, LOCK_UN); + fclose($fp); + + @chmod($filepath, FILE_WRITE_MODE); + return TRUE; + } + +} +// END Log Class + +/* End of file Log.php */ +/* Location: ./system/libraries/Log.php */ \ No newline at end of file diff --git a/libraries/Model.php b/libraries/Model.php new file mode 100644 index 0000000..1d3cbdf --- /dev/null +++ b/libraries/Model.php @@ -0,0 +1,83 @@ +_assign_libraries( (method_exists($this, '__get') OR method_exists($this, '__set')) ? FALSE : TRUE ); + + // We don't want to assign the model object to itself when using the + // assign_libraries function below so we'll grab the name of the model parent + $this->_parent_name = ucfirst(get_class($this)); + + log_message('debug', "Model Class Initialized"); + } + + /** + * Assign Libraries + * + * Creates local references to all currently instantiated objects + * so that any syntax that can be legally used in a controller + * can be used within models. + * + * @access private + */ + function _assign_libraries($use_reference = TRUE) + { + $CI =& get_instance(); + foreach (array_keys(get_object_vars($CI)) as $key) + { + if ( ! isset($this->$key) AND $key != $this->_parent_name) + { + // In some cases using references can cause + // problems so we'll conditionally use them + if ($use_reference == TRUE) + { + $this->$key = NULL; // Needed to prevent reference errors with some configurations + $this->$key =& $CI->$key; + } + else + { + $this->$key = $CI->$key; + } + } + } + } + +} +// END Model Class + +/* End of file Model.php */ +/* Location: ./system/libraries/Model.php */ \ No newline at end of file diff --git a/libraries/Output.php b/libraries/Output.php new file mode 100644 index 0000000..98a941a --- /dev/null +++ b/libraries/Output.php @@ -0,0 +1,478 @@ +final_output; + } + + // -------------------------------------------------------------------- + + /** + * Set Output + * + * Sets the output string + * + * @access public + * @param string + * @return void + */ + function set_output($output) + { + $this->final_output = $output; + } + + // -------------------------------------------------------------------- + + /** + * Append Output + * + * Appends data onto the output string + * + * @access public + * @param string + * @return void + */ + function append_output($output) + { + if ($this->final_output == '') + { + $this->final_output = $output; + } + else + { + $this->final_output .= $output; + } + } + + // -------------------------------------------------------------------- + + /** + * Set Header + * + * Lets you set a server header which will be outputted with the final display. + * + * Note: If a file is cached, headers will not be sent. We need to figure out + * how to permit header data to be saved with the cache data... + * + * @access public + * @param string + * @return void + */ + function set_header($header, $replace = TRUE) + { + $this->headers[] = array($header, $replace); + } + + // -------------------------------------------------------------------- + + /** + * Set HTTP Status Header + * + * @access public + * @param int the status code + * @param string + * @return void + */ + function set_status_header($code = '200', $text = '') + { + $stati = array( + '200' => 'OK', + '201' => 'Created', + '202' => 'Accepted', + '203' => 'Non-Authoritative Information', + '204' => 'No Content', + '205' => 'Reset Content', + '206' => 'Partial Content', + + '300' => 'Multiple Choices', + '301' => 'Moved Permanently', + '302' => 'Found', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '307' => 'Temporary Redirect', + + '400' => 'Bad Request', + '401' => 'Unauthorized', + '403' => 'Forbidden', + '404' => 'Not Found', + '405' => 'Method Not Allowed', + '406' => 'Not Acceptable', + '407' => 'Proxy Authentication Required', + '408' => 'Request Timeout', + '409' => 'Conflict', + '410' => 'Gone', + '411' => 'Length Required', + '412' => 'Precondition Failed', + '413' => 'Request Entity Too Large', + '414' => 'Request-URI Too Long', + '415' => 'Unsupported Media Type', + '416' => 'Requested Range Not Satisfiable', + '417' => 'Expectation Failed', + + '500' => 'Internal Server Error', + '501' => 'Not Implemented', + '502' => 'Bad Gateway', + '503' => 'Service Unavailable', + '504' => 'Gateway Timeout', + '505' => 'HTTP Version Not Supported' + ); + + if ($code == '' OR ! is_numeric($code)) + { + show_error('Status codes must be numeric'); + } + + if (isset($stati[$code]) AND $text == '') + { + $text = $stati[$code]; + } + + if ($text == '') + { + show_error('No status text available. Please check your status code number or supply your own message text.'); + } + + $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE; + + if (substr(php_sapi_name(), 0, 3) == 'cgi') + { + header("Status: {$code} {$text}", TRUE); + } + elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0') + { + header($server_protocol." {$code} {$text}", TRUE, $code); + } + else + { + header("HTTP/1.1 {$code} {$text}", TRUE, $code); + } + } + + // -------------------------------------------------------------------- + + /** + * Enable/disable Profiler + * + * @access public + * @param bool + * @return void + */ + function enable_profiler($val = TRUE) + { + $this->enable_profiler = (is_bool($val)) ? $val : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set Cache + * + * @access public + * @param integer + * @return void + */ + function cache($time) + { + $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time; + } + + // -------------------------------------------------------------------- + + /** + * Display Output + * + * All "view" data is automatically put into this variable by the controller class: + * + * $this->final_output + * + * This function sends the finalized output data to the browser along + * with any server headers and profile data. It also stops the + * benchmark timer so the page rendering speed and memory usage can be shown. + * + * @access public + * @return mixed + */ + function _display($output = '') + { + // Note: We use globals because we can't use $CI =& get_instance() + // since this function is sometimes called by the caching mechanism, + // which happens before the CI super object is available. + global $BM, $CFG; + + // -------------------------------------------------------------------- + + // Set the output data + if ($output == '') + { + $output =& $this->final_output; + } + + // -------------------------------------------------------------------- + + // Do we need to write a cache file? + if ($this->cache_expiration > 0) + { + $this->_write_cache($output); + } + + // -------------------------------------------------------------------- + + // Parse out the elapsed time and memory usage, + // then swap the pseudo-variables with the data + + $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end'); + $output = str_replace('{elapsed_time}', $elapsed, $output); + + $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB'; + $output = str_replace('{memory_usage}', $memory, $output); + + // -------------------------------------------------------------------- + + // Is compression requested? + if ($CFG->item('compress_output') === TRUE) + { + if (extension_loaded('zlib')) + { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { + ob_start('ob_gzhandler'); + } + } + } + + // -------------------------------------------------------------------- + + // Are there any server headers to send? + if (count($this->headers) > 0) + { + foreach ($this->headers as $header) + { + @header($header[0], $header[1]); + } + } + + // -------------------------------------------------------------------- + + // Does the get_instance() function exist? + // If not we know we are dealing with a cache file so we'll + // simply echo out the data and exit. + if ( ! function_exists('get_instance')) + { + echo $output; + log_message('debug', "Final output sent to browser"); + log_message('debug', "Total execution time: ".$elapsed); + return TRUE; + } + + // -------------------------------------------------------------------- + + // Grab the super object. We'll need it in a moment... + $CI =& get_instance(); + + // Do we need to generate profile data? + // If so, load the Profile class and run it. + if ($this->enable_profiler == TRUE) + { + $CI->load->library('profiler'); + + // If the output data contains closing and tags + // we will remove them and add them back after we insert the profile data + if (preg_match("|.*?|is", $output)) + { + $output = preg_replace("|.*?|is", '', $output); + $output .= $CI->profiler->run(); + $output .= ''; + } + else + { + $output .= $CI->profiler->run(); + } + } + + // -------------------------------------------------------------------- + + // Does the controller contain a function named _output()? + // If so send the output there. Otherwise, echo it. + if (method_exists($CI, '_output')) + { + $CI->_output($output); + } + else + { + echo $output; // Send it to the browser! + } + + log_message('debug', "Final output sent to browser"); + log_message('debug', "Total execution time: ".$elapsed); + } + + // -------------------------------------------------------------------- + + /** + * Write a Cache File + * + * @access public + * @return void + */ + function _write_cache($output) + { + $CI =& get_instance(); + $path = $CI->config->item('cache_path'); + + $cache_path = ($path == '') ? BASEPATH.'cache/' : $path; + + if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) + { + return; + } + + $uri = $CI->config->item('base_url'). + $CI->config->item('index_page'). + $CI->uri->uri_string(); + + $cache_path .= md5($uri); + + if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE)) + { + log_message('error', "Unable to write cache file: ".$cache_path); + return; + } + + $expire = time() + ($this->cache_expiration * 60); + + if (flock($fp, LOCK_EX)) + { + fwrite($fp, $expire.'TS--->'.$output); + flock($fp, LOCK_UN); + } + else + { + log_message('error', "Unable to secure a file lock for file at: ".$cache_path); + return; + } + fclose($fp); + @chmod($cache_path, DIR_WRITE_MODE); + + log_message('debug', "Cache file written: ".$cache_path); + } + + // -------------------------------------------------------------------- + + /** + * Update/serve a cached file + * + * @access public + * @return void + */ + function _display_cache(&$CFG, &$URI) + { + $cache_path = ($CFG->item('cache_path') == '') ? BASEPATH.'cache/' : $CFG->item('cache_path'); + + if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) + { + return FALSE; + } + + // Build the file path. The file name is an MD5 hash of the full URI + $uri = $CFG->item('base_url'). + $CFG->item('index_page'). + $URI->uri_string; + + $filepath = $cache_path.md5($uri); + + if ( ! @file_exists($filepath)) + { + return FALSE; + } + + if ( ! $fp = @fopen($filepath, FOPEN_READ)) + { + return FALSE; + } + + flock($fp, LOCK_SH); + + $cache = ''; + if (filesize($filepath) > 0) + { + $cache = fread($fp, filesize($filepath)); + } + + flock($fp, LOCK_UN); + fclose($fp); + + // Strip out the embedded timestamp + if ( ! preg_match("/(\d+TS--->)/", $cache, $match)) + { + return FALSE; + } + + // Has the file expired? If so we'll delete it. + if (time() >= trim(str_replace('TS--->', '', $match['1']))) + { + @unlink($filepath); + log_message('debug', "Cache file has expired. File deleted"); + return FALSE; + } + + // Display the cache + $this->_display(str_replace($match['0'], '', $cache)); + log_message('debug', "Cache file is current. Sending it to browser."); + return TRUE; + } + + +} +// END Output Class + +/* End of file Output.php */ +/* Location: ./system/libraries/Output.php */ \ No newline at end of file diff --git a/libraries/Pagination.php b/libraries/Pagination.php new file mode 100644 index 0000000..5435f42 --- /dev/null +++ b/libraries/Pagination.php @@ -0,0 +1,244 @@ +'; + var $cur_tag_close = '
'; + var $next_tag_open = ' '; + var $next_tag_close = ' '; + var $prev_tag_open = ' '; + var $prev_tag_close = ''; + var $num_tag_open = ' '; + var $num_tag_close = ''; + var $page_query_string = FALSE; + var $query_string_segment = 'per_page'; + + /** + * Constructor + * + * @access public + * @param array initialization parameters + */ + function CI_Pagination($params = array()) + { + if (count($params) > 0) + { + $this->initialize($params); + } + + log_message('debug', "Pagination Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize Preferences + * + * @access public + * @param array initialization parameters + * @return void + */ + function initialize($params = array()) + { + if (count($params) > 0) + { + foreach ($params as $key => $val) + { + if (isset($this->$key)) + { + $this->$key = $val; + } + } + } + } + + // -------------------------------------------------------------------- + + /** + * Generate the pagination links + * + * @access public + * @return string + */ + function create_links() + { + // If our item count or per-page total is zero there is no need to continue. + if ($this->total_rows == 0 OR $this->per_page == 0) + { + return ''; + } + + // Calculate the total number of pages + $num_pages = ceil($this->total_rows / $this->per_page); + + // Is there only one page? Hm... nothing more to do here then. + if ($num_pages == 1) + { + return ''; + } + + // Determine the current page number. + $CI =& get_instance(); + + if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE) + { + if ($CI->input->get($this->query_string_segment) != 0) + { + $this->cur_page = $CI->input->get($this->query_string_segment); + + // Prep the current page - no funny business! + $this->cur_page = (int) $this->cur_page; + } + } + else + { + if ($CI->uri->segment($this->uri_segment) != 0) + { + $this->cur_page = $CI->uri->segment($this->uri_segment); + + // Prep the current page - no funny business! + $this->cur_page = (int) $this->cur_page; + } + } + + $this->num_links = (int)$this->num_links; + + if ($this->num_links < 1) + { + show_error('Your number of links must be a positive number.'); + } + + if ( ! is_numeric($this->cur_page)) + { + $this->cur_page = 0; + } + + // Is the page number beyond the result range? + // If so we show the last page + if ($this->cur_page > $this->total_rows) + { + $this->cur_page = ($num_pages - 1) * $this->per_page; + } + + $uri_page_number = $this->cur_page; + $this->cur_page = floor(($this->cur_page/$this->per_page) + 1); + + // Calculate the start and end numbers. These determine + // which number to start and end the digit links with + $start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1; + $end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages; + + // Is pagination being used over GET or POST? If get, add a per_page query + // string. If post, add a trailing slash to the base URL if needed + if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE) + { + $this->base_url = rtrim($this->base_url).'&'.$this->query_string_segment.'='; + } + else + { + $this->base_url = rtrim($this->base_url, '/') .'/'; + } + + // And here we go... + $output = ''; + + // Render the "First" link + if ($this->cur_page > ($this->num_links + 1)) + { + $output .= $this->first_tag_open.''.$this->first_link.''.$this->first_tag_close; + } + + // Render the "previous" link + if ($this->cur_page != 1) + { + $i = $uri_page_number - $this->per_page; + if ($i == 0) $i = ''; + $output .= $this->prev_tag_open.''.$this->prev_link.''.$this->prev_tag_close; + } + + // Write the digit links + for ($loop = $start -1; $loop <= $end; $loop++) + { + $i = ($loop * $this->per_page) - $this->per_page; + + if ($i >= 0) + { + if ($this->cur_page == $loop) + { + $output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page + } + else + { + $n = ($i == 0) ? '' : $i; + $output .= $this->num_tag_open.''.$loop.''.$this->num_tag_close; + } + } + } + + // Render the "next" link + if ($this->cur_page < $num_pages) + { + $output .= $this->next_tag_open.''.$this->next_link.''.$this->next_tag_close; + } + + // Render the "Last" link + if (($this->cur_page + $this->num_links) < $num_pages) + { + $i = (($num_pages * $this->per_page) - $this->per_page); + $output .= $this->last_tag_open.''.$this->last_link.''.$this->last_tag_close; + } + + // Kill double slashes. Note: Sometimes we can end up with a double slash + // in the penultimate link so we'll kill all double slashes. + $output = preg_replace("#([^:])//+#", "\\1/", $output); + + // Add the wrapper HTML if exists + $output = $this->full_tag_open.$output.$this->full_tag_close; + + return $output; + } +} +// END Pagination Class + +/* End of file Pagination.php */ +/* Location: ./system/libraries/Pagination.php */ \ No newline at end of file diff --git a/libraries/Parser.php b/libraries/Parser.php new file mode 100644 index 0000000..e276a0d --- /dev/null +++ b/libraries/Parser.php @@ -0,0 +1,173 @@ +load->view($template, $data, TRUE); + + if ($template == '') + { + return FALSE; + } + + foreach ($data as $key => $val) + { + if (is_array($val)) + { + $template = $this->_parse_pair($key, $val, $template); + } + else + { + $template = $this->_parse_single($key, (string)$val, $template); + } + } + + if ($return == FALSE) + { + $CI->output->append_output($template); + } + + return $template; + } + + // -------------------------------------------------------------------- + + /** + * Set the left/right variable delimiters + * + * @access public + * @param string + * @param string + * @return void + */ + function set_delimiters($l = '{', $r = '}') + { + $this->l_delim = $l; + $this->r_delim = $r; + } + + // -------------------------------------------------------------------- + + /** + * Parse a single key/value + * + * @access private + * @param string + * @param string + * @param string + * @return string + */ + function _parse_single($key, $val, $string) + { + return str_replace($this->l_delim.$key.$this->r_delim, $val, $string); + } + + // -------------------------------------------------------------------- + + /** + * Parse a tag pair + * + * Parses tag pairs: {some_tag} string... {/some_tag} + * + * @access private + * @param string + * @param array + * @param string + * @return string + */ + function _parse_pair($variable, $data, $string) + { + if (FALSE === ($match = $this->_match_pair($string, $variable))) + { + return $string; + } + + $str = ''; + foreach ($data as $row) + { + $temp = $match['1']; + foreach ($row as $key => $val) + { + if ( ! is_array($val)) + { + $temp = $this->_parse_single($key, $val, $temp); + } + else + { + $temp = $this->_parse_pair($key, $val, $temp); + } + } + + $str .= $temp; + } + + return str_replace($match['0'], $str, $string); + } + + // -------------------------------------------------------------------- + + /** + * Matches a variable pair + * + * @access private + * @param string + * @param string + * @return mixed + */ + function _match_pair($string, $variable) + { + if ( ! preg_match("|".$this->l_delim . $variable . $this->r_delim."(.+?)".$this->l_delim . '/' . $variable . $this->r_delim."|s", $string, $match)) + { + return FALSE; + } + + return $match; + } + +} +// END Parser Class + +/* End of file Parser.php */ +/* Location: ./system/libraries/Parser.php */ \ No newline at end of file diff --git a/libraries/Profiler.php b/libraries/Profiler.php new file mode 100644 index 0000000..7f869f4 --- /dev/null +++ b/libraries/Profiler.php @@ -0,0 +1,392 @@ +CI =& get_instance(); + $this->CI->load->language('profiler'); + } + + // -------------------------------------------------------------------- + + /** + * Auto Profiler + * + * This function cycles through the entire array of mark points and + * matches any two points that are named identically (ending in "_start" + * and "_end" respectively). It then compiles the execution times for + * all points and returns it as an array + * + * @access private + * @return array + */ + function _compile_benchmarks() + { + $profile = array(); + foreach ($this->CI->benchmark->marker as $key => $val) + { + // We match the "end" marker so that the list ends + // up in the order that it was defined + if (preg_match("/(.+?)_end/i", $key, $match)) + { + if (isset($this->CI->benchmark->marker[$match[1].'_end']) AND isset($this->CI->benchmark->marker[$match[1].'_start'])) + { + $profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key); + } + } + } + + // Build a table containing the profile data. + // Note: At some point we should turn this into a template that can + // be modified. We also might want to make this data available to be logged + + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_benchmarks').'  '; + $output .= "\n"; + $output .= "\n\n\n"; + + foreach ($profile as $key => $val) + { + $key = ucwords(str_replace(array('_', '-'), ' ', $key)); + $output .= "\n"; + } + + $output .= "
".$key."  ".$val."
\n"; + $output .= "
"; + + return $output; + } + + // -------------------------------------------------------------------- + + /** + * Compile Queries + * + * @access private + * @return string + */ + function _compile_queries() + { + $dbs = array(); + + // Let's determine which databases are currently connected to + foreach (get_object_vars($this->CI) as $CI_object) + { + if ( is_subclass_of(get_class($CI_object), 'CI_DB') ) + { + $dbs[] = $CI_object; + } + } + + if (count($dbs) == 0) + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_queries').'  '; + $output .= "\n"; + $output .= "\n\n\n"; + $output .="\n"; + $output .= "
".$this->CI->lang->line('profiler_no_db')."
\n"; + $output .= "
"; + + return $output; + } + + // Load the text helper so we can highlight the SQL + $this->CI->load->helper('text'); + + // Key words we want bolded + $highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'GROUP BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR', 'HAVING', 'OFFSET', 'NOT IN', 'IN', 'LIKE', 'NOT LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')'); + + $output = "\n\n"; + + foreach ($dbs as $db) + { + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_database').':  '.$db->database.'   '.$this->CI->lang->line('profiler_queries').': '.count($this->CI->db->queries).'   '; + $output .= "\n"; + $output .= "\n\n\n"; + + if (count($db->queries) == 0) + { + $output .= "\n"; + } + else + { + foreach ($db->queries as $key => $val) + { + $time = number_format($db->query_times[$key], 4); + + $val = highlight_code($val, ENT_QUOTES); + + foreach ($highlight as $bold) + { + $val = str_replace($bold, ''.$bold.'', $val); + } + + $output .= "\n"; + } + } + + $output .= "
".$this->CI->lang->line('profiler_no_queries')."
".$time."  ".$val."
\n"; + $output .= "
"; + + } + + return $output; + } + + + // -------------------------------------------------------------------- + + /** + * Compile $_GET Data + * + * @access private + * @return string + */ + function _compile_get() + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_get_data').'  '; + $output .= "\n"; + + if (count($_GET) == 0) + { + $output .= "
".$this->CI->lang->line('profiler_no_get')."
"; + } + else + { + $output .= "\n\n\n"; + + foreach ($_GET as $key => $val) + { + if ( ! is_numeric($key)) + { + $key = "'".$key."'"; + } + + $output .= "\n"; + } + + $output .= "
$_GET[".$key."]   "; + if (is_array($val)) + { + $output .= "
" . htmlspecialchars(stripslashes(print_r($val, true))) . "
"; + } + else + { + $output .= htmlspecialchars(stripslashes($val)); + } + $output .= "
\n"; + } + $output .= "
"; + + return $output; + } + + // -------------------------------------------------------------------- + + /** + * Compile $_POST Data + * + * @access private + * @return string + */ + function _compile_post() + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_post_data').'  '; + $output .= "\n"; + + if (count($_POST) == 0) + { + $output .= "
".$this->CI->lang->line('profiler_no_post')."
"; + } + else + { + $output .= "\n\n\n"; + + foreach ($_POST as $key => $val) + { + if ( ! is_numeric($key)) + { + $key = "'".$key."'"; + } + + $output .= "\n"; + } + + $output .= "
$_POST[".$key."]   "; + if (is_array($val)) + { + $output .= "
" . htmlspecialchars(stripslashes(print_r($val, true))) . "
"; + } + else + { + $output .= htmlspecialchars(stripslashes($val)); + } + $output .= "
\n"; + } + $output .= "
"; + + return $output; + } + + // -------------------------------------------------------------------- + + /** + * Show query string + * + * @access private + * @return string + */ + function _compile_uri_string() + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_uri_string').'  '; + $output .= "\n"; + + if ($this->CI->uri->uri_string == '') + { + $output .= "
".$this->CI->lang->line('profiler_no_uri')."
"; + } + else + { + $output .= "
".$this->CI->uri->uri_string."
"; + } + + $output .= "
"; + + return $output; + } + + // -------------------------------------------------------------------- + + /** + * Show the controller and function that were called + * + * @access private + * @return string + */ + function _compile_controller_info() + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_controller_info').'  '; + $output .= "\n"; + + $output .= "
".$this->CI->router->fetch_class()."/".$this->CI->router->fetch_method()."
"; + + + $output .= "
"; + + return $output; + } + // -------------------------------------------------------------------- + + /** + * Compile memory usage + * + * Display total used memory + * + * @access public + * @return string + */ + function _compile_memory_usage() + { + $output = "\n\n"; + $output .= '
'; + $output .= "\n"; + $output .= '  '.$this->CI->lang->line('profiler_memory_usage').'  '; + $output .= "\n"; + + if (function_exists('memory_get_usage') && ($usage = memory_get_usage()) != '') + { + $output .= "
".number_format($usage).' bytes
'; + } + else + { + $output .= "
".$this->CI->lang->line('profiler_no_memory_usage')."
"; + } + + $output .= "
"; + + return $output; + } + + // -------------------------------------------------------------------- + + /** + * Run the Profiler + * + * @access private + * @return string + */ + function run() + { + $output = "
"; + + $output .= $this->_compile_uri_string(); + $output .= $this->_compile_controller_info(); + $output .= $this->_compile_memory_usage(); + $output .= $this->_compile_benchmarks(); + $output .= $this->_compile_get(); + $output .= $this->_compile_post(); + $output .= $this->_compile_queries(); + + $output .= '
'; + + return $output; + } + +} + +// END CI_Profiler class + +/* End of file Profiler.php */ +/* Location: ./system/libraries/Profiler.php */ \ No newline at end of file diff --git a/libraries/Router.php b/libraries/Router.php new file mode 100644 index 0000000..cf42516 --- /dev/null +++ b/libraries/Router.php @@ -0,0 +1,385 @@ +config =& load_class('Config'); + $this->uri =& load_class('URI'); + $this->_set_routing(); + log_message('debug', "Router Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Set the route mapping + * + * This function determines what should be served based on the URI request, + * as well as any "routes" that have been set in the routing config file. + * + * @access private + * @return void + */ + function _set_routing() + { + // Are query strings enabled in the config file? + // If so, we're done since segment based URIs are not used with query strings. + if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')])) + { + $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]))); + + if (isset($_GET[$this->config->item('function_trigger')])) + { + $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')]))); + } + + return; + } + + // Load the routes.php file. + @include(APPPATH.'config/routes'.EXT); + $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; + unset($route); + + // Set the default controller so we can display it in the event + // the URI doesn't correlated to a valid controller. + $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']); + + // Fetch the complete URI string + $this->uri->_fetch_uri_string(); + + // Is there a URI string? If not, the default controller specified in the "routes" file will be shown. + if ($this->uri->uri_string == '') + { + if ($this->default_controller === FALSE) + { + show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file."); + } + + // Turn the default route into an array. We explode it in the event that + // the controller is located in a subfolder + $segments = $this->_validate_request(explode('/', $this->default_controller)); + + // Set the class and method + $this->set_class($segments[0]); + $this->set_method('index'); + + // Assign the segments to the URI class + $this->uri->rsegments = $segments; + + // re-index the routed segments array so it starts with 1 rather than 0 + $this->uri->_reindex_segments(); + + log_message('debug', "No URI present. Default controller set."); + return; + } + unset($this->routes['default_controller']); + + // Do we need to remove the URL suffix? + $this->uri->_remove_url_suffix(); + + // Compile the segments into an array + $this->uri->_explode_segments(); + + // Parse any custom routing that may exist + $this->_parse_routes(); + + // Re-index the segment array so that it starts with 1 rather than 0 + $this->uri->_reindex_segments(); + } + + // -------------------------------------------------------------------- + + /** + * Set the Route + * + * This function takes an array of URI segments as + * input, and sets the current class/method + * + * @access private + * @param array + * @param bool + * @return void + */ + function _set_request($segments = array()) + { + $segments = $this->_validate_request($segments); + + if (count($segments) == 0) + { + return; + } + + $this->set_class($segments[0]); + + if (isset($segments[1])) + { + // A scaffolding request. No funny business with the URL + if ($this->routes['scaffolding_trigger'] == $segments[1] AND $segments[1] != '_ci_scaffolding') + { + $this->scaffolding_request = TRUE; + unset($this->routes['scaffolding_trigger']); + } + else + { + // A standard method request + $this->set_method($segments[1]); + } + } + else + { + // This lets the "routed" segment array identify that the default + // index method is being used. + $segments[1] = 'index'; + } + + // Update our "routed" segment array to contain the segments. + // Note: If there is no custom routing, this array will be + // identical to $this->uri->segments + $this->uri->rsegments = $segments; + } + + // -------------------------------------------------------------------- + + /** + * Validates the supplied segments. Attempts to determine the path to + * the controller. + * + * @access private + * @param array + * @return array + */ + function _validate_request($segments) + { + // Does the requested controller exist in the root folder? + if (file_exists(APPPATH.'controllers/'.$segments[0].EXT)) + { + return $segments; + } + + // Is the controller in a sub-folder? + if (is_dir(APPPATH.'controllers/'.$segments[0])) + { + // Set the directory and remove it from the segment array + $this->set_directory($segments[0]); + $segments = array_slice($segments, 1); + + if (count($segments) > 0) + { + // Does the requested controller exist in the sub-folder? + if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT)) + { + show_404($this->fetch_directory().$segments[0]); + } + } + else + { + $this->set_class($this->default_controller); + $this->set_method('index'); + + // Does the default controller exist in the sub-folder? + if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT)) + { + $this->directory = ''; + return array(); + } + + } + + return $segments; + } + + // Can't find the requested controller... + show_404($segments[0]); + } + + // -------------------------------------------------------------------- + + /** + * Parse Routes + * + * This function matches any routes that may exist in + * the config/routes.php file against the URI to + * determine if the class/method need to be remapped. + * + * @access private + * @return void + */ + function _parse_routes() + { + // Do we even have any custom routing to deal with? + // There is a default scaffolding trigger, so we'll look just for 1 + if (count($this->routes) == 1) + { + $this->_set_request($this->uri->segments); + return; + } + + // Turn the segment array into a URI string + $uri = implode('/', $this->uri->segments); + + // Is there a literal match? If so we're done + if (isset($this->routes[$uri])) + { + $this->_set_request(explode('/', $this->routes[$uri])); + return; + } + + // Loop through the route array looking for wild-cards + foreach ($this->routes as $key => $val) + { + // Convert wild-cards to RegEx + $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key)); + + // Does the RegEx match? + if (preg_match('#^'.$key.'$#', $uri)) + { + // Do we have a back-reference? + if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) + { + $val = preg_replace('#^'.$key.'$#', $val, $uri); + } + + $this->_set_request(explode('/', $val)); + return; + } + } + + // If we got this far it means we didn't encounter a + // matching route so we'll set the site default route + $this->_set_request($this->uri->segments); + } + + // -------------------------------------------------------------------- + + /** + * Set the class name + * + * @access public + * @param string + * @return void + */ + function set_class($class) + { + $this->class = $class; + } + + // -------------------------------------------------------------------- + + /** + * Fetch the current class + * + * @access public + * @return string + */ + function fetch_class() + { + return $this->class; + } + + // -------------------------------------------------------------------- + + /** + * Set the method name + * + * @access public + * @param string + * @return void + */ + function set_method($method) + { + $this->method = $method; + } + + // -------------------------------------------------------------------- + + /** + * Fetch the current method + * + * @access public + * @return string + */ + function fetch_method() + { + if ($this->method == $this->fetch_class()) + { + return 'index'; + } + + return $this->method; + } + + // -------------------------------------------------------------------- + + /** + * Set the directory name + * + * @access public + * @param string + * @return void + */ + function set_directory($dir) + { + $this->directory = $dir.'/'; + } + + // -------------------------------------------------------------------- + + /** + * Fetch the sub-directory (if any) that contains the requested controller class + * + * @access public + * @return string + */ + function fetch_directory() + { + return $this->directory; + } + +} +// END Router Class + +/* End of file Router.php */ +/* Location: ./system/libraries/Router.php */ \ No newline at end of file diff --git a/libraries/Session.php b/libraries/Session.php new file mode 100644 index 0000000..ed5f9c3 --- /dev/null +++ b/libraries/Session.php @@ -0,0 +1,758 @@ +CI =& get_instance(); + + // Set all the session preferences, which can either be set + // manually via the $params array above or via the config file + foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key) + { + $this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key); + } + + // Load the string helper so we can use the strip_slashes() function + $this->CI->load->helper('string'); + + // Do we need encryption? If so, load the encryption class + if ($this->sess_encrypt_cookie == TRUE) + { + $this->CI->load->library('encrypt'); + } + + // Are we using a database? If so, load it + if ($this->sess_use_database === TRUE AND $this->sess_table_name != '') + { + $this->CI->load->database(); + } + + // Set the "now" time. Can either be GMT or server time, based on the + // config prefs. We use this to set the "last activity" time + $this->now = $this->_get_time(); + + // Set the session length. If the session expiration is + // set to zero we'll set the expiration two years from now. + if ($this->sess_expiration == 0) + { + $this->sess_expiration = (60*60*24*365*2); + } + + // Set the cookie name + $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name; + + // Run the Session routine. If a session doesn't exist we'll + // create a new one. If it does, we'll update it. + if ( ! $this->sess_read()) + { + $this->sess_create(); + } + else + { + $this->sess_update(); + } + + // Delete 'old' flashdata (from last request) + $this->_flashdata_sweep(); + + // Mark all new flashdata as old (data will be deleted before next request) + $this->_flashdata_mark(); + + // Delete expired sessions if necessary + $this->_sess_gc(); + + log_message('debug', "Session routines successfully run"); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the current session data if it exists + * + * @access public + * @return void + */ + function sess_read() + { + // Fetch the cookie + $session = $this->CI->input->cookie($this->sess_cookie_name); + + // No cookie? Goodbye cruel world!... + if ($session === FALSE) + { + log_message('debug', 'A session cookie was not found.'); + return FALSE; + } + + // Decrypt the cookie data + if ($this->sess_encrypt_cookie == TRUE) + { + $session = $this->CI->encrypt->decode($session); + } + else + { + // encryption was not used, so we need to check the md5 hash + $hash = substr($session, strlen($session)-32); // get last 32 chars + $session = substr($session, 0, strlen($session)-32); + + // Does the md5 hash match? This is to prevent manipulation of session data in userspace + if ($hash !== md5($session.$this->encryption_key)) + { + log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.'); + $this->sess_destroy(); + return FALSE; + } + } + + // Unserialize the session array + $session = $this->_unserialize($session); + + // Is the session data we unserialized an array with the correct format? + if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity'])) + { + $this->sess_destroy(); + return FALSE; + } + + // Is the session current? + if (($session['last_activity'] + $this->sess_expiration) < $this->now) + { + $this->sess_destroy(); + return FALSE; + } + + // Does the IP Match? + if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address()) + { + $this->sess_destroy(); + return FALSE; + } + + // Does the User Agent Match? + if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50))) + { + $this->sess_destroy(); + return FALSE; + } + + // Is there a corresponding session in the DB? + if ($this->sess_use_database === TRUE) + { + $this->CI->db->where('session_id', $session['session_id']); + + if ($this->sess_match_ip == TRUE) + { + $this->CI->db->where('ip_address', $session['ip_address']); + } + + if ($this->sess_match_useragent == TRUE) + { + $this->CI->db->where('user_agent', $session['user_agent']); + } + + $query = $this->CI->db->get($this->sess_table_name); + + // No result? Kill it! + if ($query->num_rows() == 0) + { + $this->sess_destroy(); + return FALSE; + } + + // Is there custom data? If so, add it to the main session array + $row = $query->row(); + if (isset($row->user_data) AND $row->user_data != '') + { + $custom_data = $this->_unserialize($row->user_data); + + if (is_array($custom_data)) + { + foreach ($custom_data as $key => $val) + { + $session[$key] = $val; + } + } + } + } + + // Session is valid! + $this->userdata = $session; + unset($session); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Write the session data + * + * @access public + * @return void + */ + function sess_write() + { + // Are we saving custom data to the DB? If not, all we do is update the cookie + if ($this->sess_use_database === FALSE) + { + $this->_set_cookie(); + return; + } + + // set the custom userdata, the session data we will set in a second + $custom_userdata = $this->userdata; + $cookie_userdata = array(); + + // Before continuing, we need to determine if there is any custom data to deal with. + // Let's determine this by removing the default indexes to see if there's anything left in the array + // and set the session data while we're at it + foreach (array('session_id','ip_address','user_agent','last_activity') as $val) + { + unset($custom_userdata[$val]); + $cookie_userdata[$val] = $this->userdata[$val]; + } + + // Did we find any custom data? If not, we turn the empty array into a string + // since there's no reason to serialize and store an empty array in the DB + if (count($custom_userdata) === 0) + { + $custom_userdata = ''; + } + else + { + // Serialize the custom data array so we can store it + $custom_userdata = $this->_serialize($custom_userdata); + } + + // Run the update query + $this->CI->db->where('session_id', $this->userdata['session_id']); + $this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata)); + + // Write the cookie. Notice that we manually pass the cookie data array to the + // _set_cookie() function. Normally that function will store $this->userdata, but + // in this case that array contains custom data, which we do not want in the cookie. + $this->_set_cookie($cookie_userdata); + } + + // -------------------------------------------------------------------- + + /** + * Create a new session + * + * @access public + * @return void + */ + function sess_create() + { + $sessid = ''; + while (strlen($sessid) < 32) + { + $sessid .= mt_rand(0, mt_getrandmax()); + } + + // To make the session ID even more secure we'll combine it with the user's IP + $sessid .= $this->CI->input->ip_address(); + + $this->userdata = array( + 'session_id' => md5(uniqid($sessid, TRUE)), + 'ip_address' => $this->CI->input->ip_address(), + 'user_agent' => substr($this->CI->input->user_agent(), 0, 50), + 'last_activity' => $this->now + ); + + + // Save the data to the DB if needed + if ($this->sess_use_database === TRUE) + { + $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata)); + } + + // Write the cookie + $this->_set_cookie(); + } + + // -------------------------------------------------------------------- + + /** + * Update an existing session + * + * @access public + * @return void + */ + function sess_update() + { + // We only update the session every five minutes by default + if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) + { + return; + } + + // Save the old session id so we know which record to + // update in the database if we need it + $old_sessid = $this->userdata['session_id']; + $new_sessid = ''; + while (strlen($new_sessid) < 32) + { + $new_sessid .= mt_rand(0, mt_getrandmax()); + } + + // To make the session ID even more secure we'll combine it with the user's IP + $new_sessid .= $this->CI->input->ip_address(); + + // Turn it into a hash + $new_sessid = md5(uniqid($new_sessid, TRUE)); + + // Update the session data in the session data array + $this->userdata['session_id'] = $new_sessid; + $this->userdata['last_activity'] = $this->now; + + // _set_cookie() will handle this for us if we aren't using database sessions + // by pushing all userdata to the cookie. + $cookie_data = NULL; + + // Update the session ID and last_activity field in the DB if needed + if ($this->sess_use_database === TRUE) + { + // set cookie explicitly to only have our session data + $cookie_data = array(); + foreach (array('session_id','ip_address','user_agent','last_activity') as $val) + { + $cookie_data[$val] = $this->userdata[$val]; + } + + $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid))); + } + + // Write the cookie + $this->_set_cookie($cookie_data); + } + + // -------------------------------------------------------------------- + + /** + * Destroy the current session + * + * @access public + * @return void + */ + function sess_destroy() + { + // Kill the session DB row + if ($this->sess_use_database === TRUE AND isset($this->userdata['session_id'])) + { + $this->CI->db->where('session_id', $this->userdata['session_id']); + $this->CI->db->delete($this->sess_table_name); + } + + // Kill the cookie + setcookie( + $this->sess_cookie_name, + addslashes(serialize(array())), + ($this->now - 31500000), + $this->cookie_path, + $this->cookie_domain, + 0 + ); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a specific item from the session array + * + * @access public + * @param string + * @return string + */ + function userdata($item) + { + return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item]; + } + + // -------------------------------------------------------------------- + + /** + * Fetch all session data + * + * @access public + * @return mixed + */ + function all_userdata() + { + return ( ! isset($this->userdata)) ? FALSE : $this->userdata; + } + + // -------------------------------------------------------------------- + + /** + * Add or change data in the "userdata" array + * + * @access public + * @param mixed + * @param string + * @return void + */ + function set_userdata($newdata = array(), $newval = '') + { + if (is_string($newdata)) + { + $newdata = array($newdata => $newval); + } + + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + $this->userdata[$key] = $val; + } + } + + $this->sess_write(); + } + + // -------------------------------------------------------------------- + + /** + * Delete a session variable from the "userdata" array + * + * @access array + * @return void + */ + function unset_userdata($newdata = array()) + { + if (is_string($newdata)) + { + $newdata = array($newdata => ''); + } + + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + unset($this->userdata[$key]); + } + } + + $this->sess_write(); + } + + // ------------------------------------------------------------------------ + + /** + * Add or change flashdata, only available + * until the next request + * + * @access public + * @param mixed + * @param string + * @return void + */ + function set_flashdata($newdata = array(), $newval = '') + { + if (is_string($newdata)) + { + $newdata = array($newdata => $newval); + } + + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + $flashdata_key = $this->flashdata_key.':new:'.$key; + $this->set_userdata($flashdata_key, $val); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Keeps existing flashdata available to next request. + * + * @access public + * @param string + * @return void + */ + function keep_flashdata($key) + { + // 'old' flashdata gets removed. Here we mark all + // flashdata as 'new' to preserve it from _flashdata_sweep() + // Note the function will return FALSE if the $key + // provided cannot be found + $old_flashdata_key = $this->flashdata_key.':old:'.$key; + $value = $this->userdata($old_flashdata_key); + + $new_flashdata_key = $this->flashdata_key.':new:'.$key; + $this->set_userdata($new_flashdata_key, $value); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch a specific flashdata item from the session array + * + * @access public + * @param string + * @return string + */ + function flashdata($key) + { + $flashdata_key = $this->flashdata_key.':old:'.$key; + return $this->userdata($flashdata_key); + } + + // ------------------------------------------------------------------------ + + /** + * Identifies flashdata as 'old' for removal + * when _flashdata_sweep() runs. + * + * @access private + * @return void + */ + function _flashdata_mark() + { + $userdata = $this->all_userdata(); + foreach ($userdata as $name => $value) + { + $parts = explode(':new:', $name); + if (is_array($parts) && count($parts) === 2) + { + $new_name = $this->flashdata_key.':old:'.$parts[1]; + $this->set_userdata($new_name, $value); + $this->unset_userdata($name); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Removes all flashdata marked as 'old' + * + * @access private + * @return void + */ + + function _flashdata_sweep() + { + $userdata = $this->all_userdata(); + foreach ($userdata as $key => $value) + { + if (strpos($key, ':old:')) + { + $this->unset_userdata($key); + } + } + + } + + // -------------------------------------------------------------------- + + /** + * Get the "now" time + * + * @access private + * @return string + */ + function _get_time() + { + if (strtolower($this->time_reference) == 'gmt') + { + $now = time(); + $time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now)); + } + else + { + $time = time(); + } + + return $time; + } + + // -------------------------------------------------------------------- + + /** + * Write the session cookie + * + * @access public + * @return void + */ + function _set_cookie($cookie_data = NULL) + { + if (is_null($cookie_data)) + { + $cookie_data = $this->userdata; + } + + // Serialize the userdata for the cookie + $cookie_data = $this->_serialize($cookie_data); + + if ($this->sess_encrypt_cookie == TRUE) + { + $cookie_data = $this->CI->encrypt->encode($cookie_data); + } + else + { + // if encryption is not used, we provide an md5 hash to prevent userside tampering + $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key); + } + + // Set the cookie + setcookie( + $this->sess_cookie_name, + $cookie_data, + $this->sess_expiration + time(), + $this->cookie_path, + $this->cookie_domain, + 0 + ); + } + + // -------------------------------------------------------------------- + + /** + * Serialize an array + * + * This function first converts any slashes found in the array to a temporary + * marker, so when it gets unserialized the slashes will be preserved + * + * @access private + * @param array + * @return string + */ + function _serialize($data) + { + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = str_replace('\\', '{{slash}}', $val); + } + } + else + { + $data = str_replace('\\', '{{slash}}', $data); + } + + return serialize($data); + } + + // -------------------------------------------------------------------- + + /** + * Unserialize + * + * This function unserializes a data string, then converts any + * temporary slash markers back to actual slashes + * + * @access private + * @param array + * @return string + */ + function _unserialize($data) + { + $data = @unserialize(strip_slashes($data)); + + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = str_replace('{{slash}}', '\\', $val); + } + + return $data; + } + + return str_replace('{{slash}}', '\\', $data); + } + + // -------------------------------------------------------------------- + + /** + * Garbage collection + * + * This deletes expired session rows from database + * if the probability percentage is met + * + * @access public + * @return void + */ + function _sess_gc() + { + if ($this->sess_use_database != TRUE) + { + return; + } + + srand(time()); + if ((rand() % 100) < $this->gc_probability) + { + $expire = $this->now - $this->sess_expiration; + + $this->CI->db->where("last_activity < {$expire}"); + $this->CI->db->delete($this->sess_table_name); + + log_message('debug', 'Session garbage collection performed.'); + } + } + + +} +// END Session Class + +/* End of file Session.php */ +/* Location: ./system/libraries/Session.php */ \ No newline at end of file diff --git a/libraries/Sha1.php b/libraries/Sha1.php new file mode 100644 index 0000000..282cdb2 --- /dev/null +++ b/libraries/Sha1.php @@ -0,0 +1,251 @@ +> 6) + 1; + + for ($i = 0; $i < $n * 16; $i++) + { + $x[$i] = 0; + } + + for ($i = 0; $i < strlen($str); $i++) + { + $x[$i >> 2] |= ord(substr($str, $i, 1)) << (24 - ($i % 4) * 8); + } + + $x[$i >> 2] |= 0x80 << (24 - ($i % 4) * 8); + + $x[$n * 16 - 1] = strlen($str) * 8; + + $a = 1732584193; + $b = -271733879; + $c = -1732584194; + $d = 271733878; + $e = -1009589776; + + for ($i = 0; $i < sizeof($x); $i += 16) + { + $olda = $a; + $oldb = $b; + $oldc = $c; + $oldd = $d; + $olde = $e; + + for($j = 0; $j < 80; $j++) + { + if ($j < 16) + { + $w[$j] = $x[$i + $j]; + } + else + { + $w[$j] = $this->_rol($w[$j - 3] ^ $w[$j - 8] ^ $w[$j - 14] ^ $w[$j - 16], 1); + } + + $t = $this->_safe_add($this->_safe_add($this->_rol($a, 5), $this->_ft($j, $b, $c, $d)), $this->_safe_add($this->_safe_add($e, $w[$j]), $this->_kt($j))); + + $e = $d; + $d = $c; + $c = $this->_rol($b, 30); + $b = $a; + $a = $t; + } + + $a = $this->_safe_add($a, $olda); + $b = $this->_safe_add($b, $oldb); + $c = $this->_safe_add($c, $oldc); + $d = $this->_safe_add($d, $oldd); + $e = $this->_safe_add($e, $olde); + } + + return $this->_hex($a).$this->_hex($b).$this->_hex($c).$this->_hex($d).$this->_hex($e); + } + + // -------------------------------------------------------------------- + + /** + * Convert a decimal to hex + * + * @access private + * @param string + * @return string + */ + function _hex($str) + { + $str = dechex($str); + + if (strlen($str) == 7) + { + $str = '0'.$str; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Return result based on iteration + * + * @access private + * @return string + */ + function _ft($t, $b, $c, $d) + { + if ($t < 20) + return ($b & $c) | ((~$b) & $d); + if ($t < 40) + return $b ^ $c ^ $d; + if ($t < 60) + return ($b & $c) | ($b & $d) | ($c & $d); + + return $b ^ $c ^ $d; + } + + // -------------------------------------------------------------------- + + /** + * Determine the additive constant + * + * @access private + * @return string + */ + function _kt($t) + { + if ($t < 20) + { + return 1518500249; + } + else if ($t < 40) + { + return 1859775393; + } + else if ($t < 60) + { + return -1894007588; + } + else + { + return -899497514; + } + } + + // -------------------------------------------------------------------- + + /** + * Add integers, wrapping at 2^32 + * + * @access private + * @return string + */ + function _safe_add($x, $y) + { + $lsw = ($x & 0xFFFF) + ($y & 0xFFFF); + $msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16); + + return ($msw << 16) | ($lsw & 0xFFFF); + } + + // -------------------------------------------------------------------- + + /** + * Bitwise rotate a 32-bit number + * + * @access private + * @return integer + */ + function _rol($num, $cnt) + { + return ($num << $cnt) | $this->_zero_fill($num, 32 - $cnt); + } + + // -------------------------------------------------------------------- + + /** + * Pad string with zero + * + * @access private + * @return string + */ + function _zero_fill($a, $b) + { + $bin = decbin($a); + + if (strlen($bin) < $b) + { + $bin = 0; + } + else + { + $bin = substr($bin, 0, strlen($bin) - $b); + } + + for ($i=0; $i < $b; $i++) + { + $bin = "0".$bin; + } + + return bindec($bin); + } +} +// END CI_SHA + +/* End of file Sha1.php */ +/* Location: ./system/libraries/Sha1.php */ \ No newline at end of file diff --git a/libraries/Table.php b/libraries/Table.php new file mode 100644 index 0000000..6940ece --- /dev/null +++ b/libraries/Table.php @@ -0,0 +1,440 @@ +template = $template; + } + + // -------------------------------------------------------------------- + + /** + * Set the table heading + * + * Can be passed as an array or discreet params + * + * @access public + * @param mixed + * @return void + */ + function set_heading() + { + $args = func_get_args(); + $this->heading = (is_array($args[0])) ? $args[0] : $args; + } + + // -------------------------------------------------------------------- + + /** + * Set columns. Takes a one-dimensional array as input and creates + * a multi-dimensional array with a depth equal to the number of + * columns. This allows a single array with many elements to be + * displayed in a table that has a fixed column count. + * + * @access public + * @param array + * @param int + * @return void + */ + function make_columns($array = array(), $col_limit = 0) + { + if ( ! is_array($array) OR count($array) == 0) + { + return FALSE; + } + + // Turn off the auto-heading feature since it's doubtful we + // will want headings from a one-dimensional array + $this->auto_heading = FALSE; + + if ($col_limit == 0) + { + return $array; + } + + $new = array(); + while(count($array) > 0) + { + $temp = array_splice($array, 0, $col_limit); + + if (count($temp) < $col_limit) + { + for ($i = count($temp); $i < $col_limit; $i++) + { + $temp[] = ' '; + } + } + + $new[] = $temp; + } + + return $new; + } + + // -------------------------------------------------------------------- + + /** + * Set "empty" cells + * + * Can be passed as an array or discreet params + * + * @access public + * @param mixed + * @return void + */ + function set_empty($value) + { + $this->empty_cells = $value; + } + + // -------------------------------------------------------------------- + + /** + * Add a table row + * + * Can be passed as an array or discreet params + * + * @access public + * @param mixed + * @return void + */ + function add_row() + { + $args = func_get_args(); + $this->rows[] = (is_array($args[0])) ? $args[0] : $args; + } + + // -------------------------------------------------------------------- + + /** + * Add a table caption + * + * @access public + * @param string + * @return void + */ + function set_caption($caption) + { + $this->caption = $caption; + } + + // -------------------------------------------------------------------- + + /** + * Generate the table + * + * @access public + * @param mixed + * @return string + */ + function generate($table_data = NULL) + { + // The table data can optionally be passed to this function + // either as a database result object or an array + if ( ! is_null($table_data)) + { + if (is_object($table_data)) + { + $this->_set_from_object($table_data); + } + elseif (is_array($table_data)) + { + $set_heading = (count($this->heading) == 0 AND $this->auto_heading == FALSE) ? FALSE : TRUE; + $this->_set_from_array($table_data, $set_heading); + } + } + + // Is there anything to display? No? Smite them! + if (count($this->heading) == 0 AND count($this->rows) == 0) + { + return 'Undefined table data'; + } + + // Compile and validate the template date + $this->_compile_template(); + + + // Build the table! + + $out = $this->template['table_open']; + $out .= $this->newline; + + // Add any caption here + if ($this->caption) + { + $out .= $this->newline; + $out .= '
' . $this->caption . '
', + + 'heading_row_start' => '', + 'heading_row_end' => '', + 'heading_cell_start' => '', + + 'row_start' => '', + 'row_end' => '', + 'cell_start' => '', + + 'row_alt_start' => '', + 'row_alt_end' => '', + 'cell_alt_start' => '', + + 'table_close' => '
', + 'heading_cell_end' => '
', + 'cell_end' => '
', + 'cell_alt_end' => '
' + ); + } + + +} + + +/* End of file Table.php */ +/* Location: ./system/libraries/Table.php */ \ No newline at end of file diff --git a/libraries/Trackback.php b/libraries/Trackback.php new file mode 100644 index 0000000..9988492 --- /dev/null +++ b/libraries/Trackback.php @@ -0,0 +1,550 @@ + '', 'title' => '', 'excerpt' => '', 'blog_name' => '', 'charset' => ''); + var $convert_ascii = TRUE; + var $response = ''; + var $error_msg = array(); + + /** + * Constructor + * + * @access public + */ + function CI_Trackback() + { + log_message('debug', "Trackback Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Send Trackback + * + * @access public + * @param array + * @return bool + */ + function send($tb_data) + { + if ( ! is_array($tb_data)) + { + $this->set_error('The send() method must be passed an array'); + return FALSE; + } + + // Pre-process the Trackback Data + foreach (array('url', 'title', 'excerpt', 'blog_name', 'ping_url') as $item) + { + if ( ! isset($tb_data[$item])) + { + $this->set_error('Required item missing: '.$item); + return FALSE; + } + + switch ($item) + { + case 'ping_url' : $$item = $this->extract_urls($tb_data[$item]); + break; + case 'excerpt' : $$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item])))); + break; + case 'url' : $$item = str_replace('-', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item])))); + break; + default : $$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))); + break; + } + + // Convert High ASCII Characters + if ($this->convert_ascii == TRUE) + { + if ($item == 'excerpt') + { + $$item = $this->convert_ascii($$item); + } + elseif ($item == 'title') + { + $$item = $this->convert_ascii($$item); + } + elseif($item == 'blog_name') + { + $$item = $this->convert_ascii($$item); + } + } + } + + // Build the Trackback data string + $charset = ( ! isset($tb_data['charset'])) ? $this->charset : $tb_data['charset']; + + $data = "url=".rawurlencode($url)."&title=".rawurlencode($title)."&blog_name=".rawurlencode($blog_name)."&excerpt=".rawurlencode($excerpt)."&charset=".rawurlencode($charset); + + // Send Trackback(s) + $return = TRUE; + if (count($ping_url) > 0) + { + foreach ($ping_url as $url) + { + if ($this->process($url, $data) == FALSE) + { + $return = FALSE; + } + } + } + + return $return; + } + + // -------------------------------------------------------------------- + + /** + * Receive Trackback Data + * + * This function simply validates the incoming TB data. + * It returns false on failure and true on success. + * If the data is valid it is set to the $this->data array + * so that it can be inserted into a database. + * + * @access public + * @return bool + */ + function receive() + { + foreach (array('url', 'title', 'blog_name', 'excerpt') as $val) + { + if ( ! isset($_POST[$val]) OR $_POST[$val] == '') + { + $this->set_error('The following required POST variable is missing: '.$val); + return FALSE; + } + + $this->data['charset'] = ( ! isset($_POST['charset'])) ? 'auto' : strtoupper(trim($_POST['charset'])); + + if ($val != 'url' && function_exists('mb_convert_encoding')) + { + $_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']); + } + + $_POST[$val] = ($val != 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]); + + if ($val == 'excerpt') + { + $_POST['excerpt'] = $this->limit_characters($_POST['excerpt']); + } + + $this->data[$val] = $_POST[$val]; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Send Trackback Error Message + * + * Allows custom errors to be set. By default it + * sends the "incomplete information" error, as that's + * the most common one. + * + * @access public + * @param string + * @return void + */ + function send_error($message = 'Incomplete Information') + { + echo "\n\n1\n".$message."\n"; + exit; + } + + // -------------------------------------------------------------------- + + /** + * Send Trackback Success Message + * + * This should be called when a trackback has been + * successfully received and inserted. + * + * @access public + * @return void + */ + function send_success() + { + echo "\n\n0\n"; + exit; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a particular item + * + * @access public + * @param string + * @return string + */ + function data($item) + { + return ( ! isset($this->data[$item])) ? '' : $this->data[$item]; + } + + // -------------------------------------------------------------------- + + /** + * Process Trackback + * + * Opens a socket connection and passes the data to + * the server. Returns true on success, false on failure + * + * @access public + * @param string + * @param string + * @return bool + */ + function process($url, $data) + { + $target = parse_url($url); + + // Open the socket + if ( ! $fp = @fsockopen($target['host'], 80)) + { + $this->set_error('Invalid Connection: '.$url); + return FALSE; + } + + // Build the path + $ppath = ( ! isset($target['path'])) ? $url : $target['path']; + + $path = (isset($target['query']) && $target['query'] != "") ? $ppath.'?'.$target['query'] : $ppath; + + // Add the Trackback ID to the data string + if ($id = $this->get_id($url)) + { + $data = "tb_id=".$id."&".$data; + } + + // Transfer the data + fputs ($fp, "POST " . $path . " HTTP/1.0\r\n" ); + fputs ($fp, "Host: " . $target['host'] . "\r\n" ); + fputs ($fp, "Content-type: application/x-www-form-urlencoded\r\n" ); + fputs ($fp, "Content-length: " . strlen($data) . "\r\n" ); + fputs ($fp, "Connection: close\r\n\r\n" ); + fputs ($fp, $data); + + // Was it successful? + $this->response = ""; + + while( ! feof($fp)) + { + $this->response .= fgets($fp, 128); + } + @fclose($fp); + + if ( ! eregi("0", $this->response)) + { + $message = 'An unknown error was encountered'; + + if (preg_match("/(.*?)<\/message>/is", $this->response, $match)) + { + $message = trim($match['1']); + } + + $this->set_error($message); + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Extract Trackback URLs + * + * This function lets multiple trackbacks be sent. + * It takes a string of URLs (separated by comma or + * space) and puts each URL into an array + * + * @access public + * @param string + * @return string + */ + function extract_urls($urls) + { + // Remove the pesky white space and replace with a comma. + $urls = preg_replace("/\s*(\S+)\s*/", "\\1,", $urls); + + // If they use commas get rid of the doubles. + $urls = str_replace(",,", ",", $urls); + + // Remove any comma that might be at the end + if (substr($urls, -1) == ",") + { + $urls = substr($urls, 0, -1); + } + + // Break into an array via commas + $urls = preg_split('/[,]/', $urls); + + // Removes duplicates + $urls = array_unique($urls); + + array_walk($urls, array($this, 'validate_url')); + + return $urls; + } + + // -------------------------------------------------------------------- + + /** + * Validate URL + * + * Simply adds "http://" if missing + * + * @access public + * @param string + * @return string + */ + function validate_url($url) + { + $url = trim($url); + + if (substr($url, 0, 4) != "http") + { + $url = "http://".$url; + } + } + + // -------------------------------------------------------------------- + + /** + * Find the Trackback URL's ID + * + * @access public + * @param string + * @return string + */ + function get_id($url) + { + $tb_id = ""; + + if (strstr($url, '?')) + { + $tb_array = explode('/', $url); + $tb_end = $tb_array[count($tb_array)-1]; + + if ( ! is_numeric($tb_end)) + { + $tb_end = $tb_array[count($tb_array)-2]; + } + + $tb_array = explode('=', $tb_end); + $tb_id = $tb_array[count($tb_array)-1]; + } + else + { + if (ereg("/$", $url)) + { + $url = substr($url, 0, -1); + } + + $tb_array = explode('/', $url); + $tb_id = $tb_array[count($tb_array)-1]; + + if ( ! is_numeric($tb_id)) + { + $tb_id = $tb_array[count($tb_array)-2]; + } + } + + if ( ! preg_match ("/^([0-9]+)$/", $tb_id)) + { + return false; + } + else + { + return $tb_id; + } + } + + // -------------------------------------------------------------------- + + /** + * Convert Reserved XML characters to Entities + * + * @access public + * @param string + * @return string + */ + function convert_xml($str) + { + $temp = '__TEMP_AMPERSANDS__'; + + $str = preg_replace("/&#(\d+);/", "$temp\\1;", $str); + $str = preg_replace("/&(\w+);/", "$temp\\1;", $str); + + $str = str_replace(array("&","<",">","\"", "'", "-"), + array("&", "<", ">", """, "'", "-"), + $str); + + $str = preg_replace("/$temp(\d+);/","&#\\1;",$str); + $str = preg_replace("/$temp(\w+);/","&\\1;", $str); + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Character limiter + * + * Limits the string based on the character count. Will preserve complete words. + * + * @access public + * @param string + * @param integer + * @param string + * @return string + */ + function limit_characters($str, $n = 500, $end_char = '…') + { + if (strlen($str) < $n) + { + return $str; + } + + $str = preg_replace("/\s+/", ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str)); + + if (strlen($str) <= $n) + { + return $str; + } + + $out = ""; + foreach (explode(' ', trim($str)) as $val) + { + $out .= $val.' '; + if (strlen($out) >= $n) + { + return trim($out).$end_char; + } + } + } + + // -------------------------------------------------------------------- + + /** + * High ASCII to Entities + * + * Converts Hight ascii text and MS Word special chars + * to character entities + * + * @access public + * @param string + * @return string + */ + function convert_ascii($str) + { + $count = 1; + $out = ''; + $temp = array(); + + for ($i = 0, $s = strlen($str); $i < $s; $i++) + { + $ordinal = ord($str[$i]); + + if ($ordinal < 128) + { + $out .= $str[$i]; + } + else + { + if (count($temp) == 0) + { + $count = ($ordinal < 224) ? 2 : 3; + } + + $temp[] = $ordinal; + + if (count($temp) == $count) + { + $number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64); + + $out .= '&#'.$number.';'; + $count = 1; + $temp = array(); + } + } + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * Set error message + * + * @access public + * @param string + * @return void + */ + function set_error($msg) + { + log_message('error', $msg); + $this->error_msg[] = $msg; + } + + // -------------------------------------------------------------------- + + /** + * Show error messages + * + * @access public + * @param string + * @param string + * @return string + */ + function display_errors($open = '

', $close = '

') + { + $str = ''; + foreach ($this->error_msg as $val) + { + $str .= $open.$val.$close; + } + + return $str; + } + +} +// END Trackback Class + +/* End of file Trackback.php */ +/* Location: ./system/libraries/Trackback.php */ \ No newline at end of file diff --git a/libraries/Typography.php b/libraries/Typography.php new file mode 100644 index 0000000..0ee6a24 --- /dev/null +++ b/libraries/Typography.php @@ -0,0 +1,342 @@ + tags + var $block_elements = 'address|blockquote|div|dl|fieldset|form|h\d|hr|noscript|object|ol|p|pre|script|table|ul'; + + // Elements that should not have

and
tags within them. + var $skip_elements = 'p|pre|ol|ul|dl|object|table'; + + // Tags we want the parser to completely ignore when splitting the string. + var $inline_elements = 'a|abbr|acronym|b|bdo|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|samp|select|span|strong|sub|sup|textarea|var'; + + // whether or not to protect quotes within { curly braces } + var $protect_braced_quotes = FALSE; + + /** + * Nothing to do here... + * + */ + function CI_Typography() + { + } + + /** + * Auto Typography + * + * This function converts text, making it typographically correct: + * - Converts double spaces into paragraphs. + * - Converts single line breaks into
tags + * - Converts single and double quotes into correctly facing curly quote entities. + * - Converts three dots into ellipsis. + * - Converts double dashes into em-dashes. + * - Converts two spaces into entities + * + * @access public + * @param string + * @param bool whether to strip javascript event handlers for security + * @param bool whether to reduce more then two consecutive newlines to two + * @return string + */ + function auto_typography($str, $strip_js_event_handlers = TRUE, $reduce_linebreaks = FALSE) + { + if ($str == '') + { + return ''; + } + + // Standardize Newlines to make matching easier + if (strpos($str, "\r") !== FALSE) + { + $str = str_replace(array("\r\n", "\r"), "\n", $str); + } + + // Reduce line breaks. If there are more than two consecutive linebreaks + // we'll compress them down to a maximum of two since there's no benefit to more. + if ($reduce_linebreaks === TRUE) + { + $str = preg_replace("/\n\n+/", "\n\n", $str); + } + + // Do we allow JavaScript event handlers? If not, we strip them from within all tags + if ($strip_js_event_handlers === TRUE) + { + $str = preg_replace("#<([^><]+?)([^a-z_\-]on\w*|xmlns)(\s*=\s*[^><]*)([><]*)#i", "<\\1\\4", $str); + } + + // Convert quotes within tags to temporary markers. We don't want quotes converted + // within tags so we'll temporarily convert them to {@DQ} and {@SQ} + if (preg_match_all("#\<.+?>#si", $str, $matches)) + { + for ($i = 0; $i < count($matches['0']); $i++) + { + $str = str_replace($matches['0'][$i], + str_replace(array("'",'"'), array('{@SQ}', '{@DQ}'), $matches['0'][$i]), + $str); + } + } + + if ($this->protect_braced_quotes === TRUE) + { + if (preg_match_all("#\{.+?}#si", $str, $matches)) + { + for ($i = 0; $i < count($matches['0']); $i++) + { + $str = str_replace($matches['0'][$i], + str_replace(array("'",'"'), array('{@SQ}', '{@DQ}'), $matches['0'][$i]), + $str); + } + } + } + + // Convert "ignore" tags to temporary marker. The parser splits out the string at every tag + // it encounters. Certain inline tags, like image tags, links, span tags, etc. will be + // adversely affected if they are split out so we'll convert the opening bracket < temporarily to: {@TAG} + $str = preg_replace("#<(/*)(".$this->inline_elements.")([ >])#i", "{@TAG}\\1\\2\\3", $str); + + // Split the string at every tag. This expression creates an array with this prototype: + // + // [array] + // { + // [0] = + // [1] = Content... + // [2] = + // Etc... + // } + $chunks = preg_split('/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + + // Build our finalized string. We cycle through the array, skipping tags, and processing the contained text + $str = ''; + $process = TRUE; + $paragraph = FALSE; + foreach ($chunks as $chunk) + { + // Are we dealing with a tag? If so, we'll skip the processing for this cycle. + // Well also set the "process" flag which allows us to skip

 tags and a few other things.
+			if (preg_match("#<(/*)(".$this->block_elements.").*?\>#", $chunk, $match))
+			{
+				if (preg_match("#".$this->skip_elements."#", $match[2]))
+				{
+					$process =  ($match[1] == '/') ? TRUE : FALSE;
+				}
+				
+				$str .= $chunk;
+				continue;
+			}
+
+			if ($process == FALSE)
+			{
+				$str .= $chunk;
+				continue;
+			}
+			
+			//  Convert Newlines into 

and
tags + $str .= $this->_format_newlines($chunk); + } + + // is the whole of the content inside a block level element? + if ( ! preg_match("/^<(?:".$this->block_elements.")/i", $str, $match)) + { + $str = "

{$str}

"; + } + + // Convert quotes, elipsis, and em-dashes + $str = $this->format_characters($str); + + // Final clean up + $table = array( + + // If the user submitted their own paragraph tags within the text + // we will retain them instead of using our tags. + '/()

/' => '$1', // )+#' => '

', + '/(

)+/' => '

', + + // Clean up stray paragraph tags that appear before block level elements + '#

<('.$this->block_elements.')#' => '<$1', + + // Replace the temporary markers we added earlier + '/\{@TAG\}/' => '<', + '/\{@DQ\}/' => '"', + '/\{@SQ\}/' => "'" + + ); + + // Do we need to reduce empty lines? + if ($reduce_linebreaks === TRUE) + { + $table['#

\n*

#'] = ''; + } + else + { + // If we have empty paragraph tags we add a non-breaking space + // otherwise most browsers won't treat them as true paragraphs + $table['#

#'] = '

 

'; + } + + return preg_replace(array_keys($table), $table, $str); + + } + + // -------------------------------------------------------------------- + + /** + * Format Characters + * + * This function mainly converts double and single quotes + * to curly entities, but it also converts em-dashes, + * double spaces, and ampersands + * + * @access public + * @param string + * @return string + */ + function format_characters($str) + { + static $table; + + if ( ! isset($table)) + { + $table = array( + // nested smart quotes, opening and closing + // note that rules for grammar (English) allow only for two levels deep + // and that single quotes are _supposed_ to always be on the outside + // but we'll accommodate both + '/(^|\W|\s)\'"/' => '$1‘“', + '/\'"(\s|\W|$)/' => '’”$1', + '/(^|\W|\s)"\'/' => '$1“‘', + '/"\'(\s|\W|$)/' => '”’$1', + + // single quote smart quotes + '/\'(\s|\W|$)/' => '’$1', + '/(^|\W|\s)\'/' => '$1‘', + + // double quote smart quotes + '/"(\s|\W|$)/' => '”$1', + '/(^|\W|\s)"/' => '$1“', + + // apostrophes + "/(\w)'(\w)/" => '$1’$2', + + // Em dash and ellipses dots + '/\s?\-\-\s?/' => '—', + '/(\w)\.{3}/' => '$1…', + + // double space after sentences + '/(\W) /' => '$1  ', + + // ampersands, if not a character entity + '/&(?!#?[a-zA-Z0-9]{2,};)/' => '&' + ); + } + + return preg_replace(array_keys($table), $table, $str); + } + + // -------------------------------------------------------------------- + + /** + * Format Newlines + * + * Converts newline characters into either

tags or
+ * + * @access public + * @param string + * @return string + */ + function _format_newlines($str) + { + if ($str == '') + { + return $str; + } + + if (strpos($str, "\n") === FALSE) + { + return $str; + } + + // Convert two consecutive newlines to paragraphs + $str = str_replace("\n\n", "

\n\n

", $str); + + // Convert single spaces to
tags + $str = preg_replace("/([^\n])(\n)([^\n])/", "\\1
\\2\\3", $str); + + // Wrap the whole enchilada in enclosing paragraphs + if ($str != "\n") + { + $str = '

'.$str.'

'; + } + + // Remove empty paragraphs if they are on the first line, as this + // is a potential unintended consequence of the previous code + $str = preg_replace("/

<\/p>(.*)/", "\\1", $str, 1); + + return $str; + } + + // ------------------------------------------------------------------------ + + /** + * Convert newlines to HTML line breaks except within PRE tags + * + * @access public + * @param string + * @return string + */ + function nl2br_except_pre($str) + { + $ex = explode("pre>",$str); + $ct = count($ex); + + $newstr = ""; + for ($i = 0; $i < $ct; $i++) + { + if (($i % 2) == 0) + { + $newstr .= nl2br($ex[$i]); + } + else + { + $newstr .= $ex[$i]; + } + + if ($ct - 1 != $i) + $newstr .= "pre>"; + } + + return $newstr; + } + +} +// END Typography Class + +/* End of file Typography.php */ +/* Location: ./system/libraries/Typography.php */ \ No newline at end of file diff --git a/libraries/URI.php b/libraries/URI.php new file mode 100644 index 0000000..0e4ff50 --- /dev/null +++ b/libraries/URI.php @@ -0,0 +1,584 @@ +config =& load_class('Config'); + log_message('debug', "URI Class Initialized"); + } + + + // -------------------------------------------------------------------- + + /** + * Get the URI String + * + * @access private + * @return string + */ + function _fetch_uri_string() + { + if (strtoupper($this->config->item('uri_protocol')) == 'AUTO') + { + // If the URL has a question mark then it's simplest to just + // build the URI string from the zero index of the $_GET array. + // This avoids having to deal with $_SERVER variables, which + // can be unreliable in some environments + if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') + { + $this->uri_string = key($_GET); + return; + } + + // Is there a PATH_INFO variable? + // Note: some servers seem to have trouble with getenv() so we'll test it two ways + $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); + if (trim($path, '/') != '' && $path != "/".SELF) + { + $this->uri_string = $path; + return; + } + + // No PATH_INFO?... What about QUERY_STRING? + $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); + if (trim($path, '/') != '') + { + $this->uri_string = $path; + return; + } + + // No QUERY_STRING?... Maybe the ORIG_PATH_INFO variable exists? + $path = (isset($_SERVER['ORIG_PATH_INFO'])) ? $_SERVER['ORIG_PATH_INFO'] : @getenv('ORIG_PATH_INFO'); + if (trim($path, '/') != '' && $path != "/".SELF) + { + // remove path and script information so we have good URI data + $this->uri_string = str_replace($_SERVER['SCRIPT_NAME'], '', $path); + return; + } + + // We've exhausted all our options... + $this->uri_string = ''; + } + else + { + $uri = strtoupper($this->config->item('uri_protocol')); + + if ($uri == 'REQUEST_URI') + { + $this->uri_string = $this->_parse_request_uri(); + return; + } + + $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); + } + + // If the URI contains only a slash we'll kill it + if ($this->uri_string == '/') + { + $this->uri_string = ''; + } + } + + // -------------------------------------------------------------------- + + /** + * Parse the REQUEST_URI + * + * Due to the way REQUEST_URI works it usually contains path info + * that makes it unusable as URI data. We'll trim off the unnecessary + * data, hopefully arriving at a valid URI that we can use. + * + * @access private + * @return string + */ + function _parse_request_uri() + { + if ( ! isset($_SERVER['REQUEST_URI']) OR $_SERVER['REQUEST_URI'] == '') + { + return ''; + } + + $request_uri = preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $_SERVER['REQUEST_URI'])); + + if ($request_uri == '' OR $request_uri == SELF) + { + return ''; + } + + $fc_path = FCPATH; + if (strpos($request_uri, '?') !== FALSE) + { + $fc_path .= '?'; + } + + $parsed_uri = explode("/", $request_uri); + + $i = 0; + foreach(explode("/", $fc_path) as $segment) + { + if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i]) + { + $i++; + } + } + + $parsed_uri = implode("/", array_slice($parsed_uri, $i)); + + if ($parsed_uri != '') + { + $parsed_uri = '/'.$parsed_uri; + } + + return $parsed_uri; + } + + // -------------------------------------------------------------------- + + /** + * Filter segments for malicious characters + * + * @access private + * @param string + * @return string + */ + function _filter_uri($str) + { + if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE) + { + if ( ! preg_match("|^[".preg_quote($this->config->item('permitted_uri_chars'))."]+$|i", $str)) + { + exit('The URI you submitted has disallowed characters.'); + } + } + + // Convert programatic characters to entities + $bad = array('$', '(', ')', '%28', '%29'); + $good = array('$', '(', ')', '(', ')'); + + return str_replace($bad, $good, $str); + } + + // -------------------------------------------------------------------- + + /** + * Remove the suffix from the URL if needed + * + * @access private + * @return void + */ + function _remove_url_suffix() + { + if ($this->config->item('url_suffix') != "") + { + $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string); + } + } + + // -------------------------------------------------------------------- + + /** + * Explode the URI Segments. The individual segments will + * be stored in the $this->segments array. + * + * @access private + * @return void + */ + function _explode_segments() + { + foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val) + { + // Filter segments for security + $val = trim($this->_filter_uri($val)); + + if ($val != '') + { + $this->segments[] = $val; + } + } + } + + // -------------------------------------------------------------------- + /** + * Re-index Segments + * + * This function re-indexes the $this->segment array so that it + * starts at 1 rather than 0. Doing so makes it simpler to + * use functions like $this->uri->segment(n) since there is + * a 1:1 relationship between the segment array and the actual segments. + * + * @access private + * @return void + */ + function _reindex_segments() + { + array_unshift($this->segments, NULL); + array_unshift($this->rsegments, NULL); + unset($this->segments[0]); + unset($this->rsegments[0]); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a URI Segment + * + * This function returns the URI segment based on the number provided. + * + * @access public + * @param integer + * @param bool + * @return string + */ + function segment($n, $no_result = FALSE) + { + return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n]; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a URI "routed" Segment + * + * This function returns the re-routed URI segment (assuming routing rules are used) + * based on the number provided. If there is no routing this function returns the + * same result as $this->segment() + * + * @access public + * @param integer + * @param bool + * @return string + */ + function rsegment($n, $no_result = FALSE) + { + return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n]; + } + + // -------------------------------------------------------------------- + + /** + * Generate a key value pair from the URI string + * + * This function generates and associative array of URI data starting + * at the supplied segment. For example, if this is your URI: + * + * example.com/user/search/name/joe/location/UK/gender/male + * + * You can use this function to generate an array with this prototype: + * + * array ( + * name => joe + * location => UK + * gender => male + * ) + * + * @access public + * @param integer the starting segment number + * @param array an array of default values + * @return array + */ + function uri_to_assoc($n = 3, $default = array()) + { + return $this->_uri_to_assoc($n, $default, 'segment'); + } + /** + * Identical to above only it uses the re-routed segment array + * + */ + function ruri_to_assoc($n = 3, $default = array()) + { + return $this->_uri_to_assoc($n, $default, 'rsegment'); + } + + // -------------------------------------------------------------------- + + /** + * Generate a key value pair from the URI string or Re-routed URI string + * + * @access private + * @param integer the starting segment number + * @param array an array of default values + * @param string which array we should use + * @return array + */ + function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') + { + if ($which == 'segment') + { + $total_segments = 'total_segments'; + $segment_array = 'segment_array'; + } + else + { + $total_segments = 'total_rsegments'; + $segment_array = 'rsegment_array'; + } + + if ( ! is_numeric($n)) + { + return $default; + } + + if (isset($this->keyval[$n])) + { + return $this->keyval[$n]; + } + + if ($this->$total_segments() < $n) + { + if (count($default) == 0) + { + return array(); + } + + $retval = array(); + foreach ($default as $val) + { + $retval[$val] = FALSE; + } + return $retval; + } + + $segments = array_slice($this->$segment_array(), ($n - 1)); + + $i = 0; + $lastval = ''; + $retval = array(); + foreach ($segments as $seg) + { + if ($i % 2) + { + $retval[$lastval] = $seg; + } + else + { + $retval[$seg] = FALSE; + $lastval = $seg; + } + + $i++; + } + + if (count($default) > 0) + { + foreach ($default as $val) + { + if ( ! array_key_exists($val, $retval)) + { + $retval[$val] = FALSE; + } + } + } + + // Cache the array for reuse + $this->keyval[$n] = $retval; + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Generate a URI string from an associative array + * + * + * @access public + * @param array an associative array of key/values + * @return array + */ + function assoc_to_uri($array) + { + $temp = array(); + foreach ((array)$array as $key => $val) + { + $temp[] = $key; + $temp[] = $val; + } + + return implode('/', $temp); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a URI Segment and add a trailing slash + * + * @access public + * @param integer + * @param string + * @return string + */ + function slash_segment($n, $where = 'trailing') + { + return $this->_slash_segment($n, $where, 'segment'); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a URI Segment and add a trailing slash + * + * @access public + * @param integer + * @param string + * @return string + */ + function slash_rsegment($n, $where = 'trailing') + { + return $this->_slash_segment($n, $where, 'rsegment'); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a URI Segment and add a trailing slash - helper function + * + * @access private + * @param integer + * @param string + * @param string + * @return string + */ + function _slash_segment($n, $where = 'trailing', $which = 'segment') + { + if ($where == 'trailing') + { + $trailing = '/'; + $leading = ''; + } + elseif ($where == 'leading') + { + $leading = '/'; + $trailing = ''; + } + else + { + $leading = '/'; + $trailing = '/'; + } + return $leading.$this->$which($n).$trailing; + } + + // -------------------------------------------------------------------- + + /** + * Segment Array + * + * @access public + * @return array + */ + function segment_array() + { + return $this->segments; + } + + // -------------------------------------------------------------------- + + /** + * Routed Segment Array + * + * @access public + * @return array + */ + function rsegment_array() + { + return $this->rsegments; + } + + // -------------------------------------------------------------------- + + /** + * Total number of segments + * + * @access public + * @return integer + */ + function total_segments() + { + return count($this->segments); + } + + // -------------------------------------------------------------------- + + /** + * Total number of routed segments + * + * @access public + * @return integer + */ + function total_rsegments() + { + return count($this->rsegments); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the entire URI string + * + * @access public + * @return string + */ + function uri_string() + { + return $this->uri_string; + } + + + // -------------------------------------------------------------------- + + /** + * Fetch the entire Re-routed URI string + * + * @access public + * @return string + */ + function ruri_string() + { + return '/'.implode('/', $this->rsegment_array()).'/'; + } + +} +// END URI Class + +/* End of file URI.php */ +/* Location: ./system/libraries/URI.php */ \ No newline at end of file diff --git a/libraries/Unit_test.php b/libraries/Unit_test.php new file mode 100644 index 0000000..5ed048b --- /dev/null +++ b/libraries/Unit_test.php @@ -0,0 +1,347 @@ +active == FALSE) + { + return FALSE; + } + + if (in_array($expected, array('is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE)) + { + $expected = str_replace('is_float', 'is_double', $expected); + $result = ($expected($test)) ? TRUE : FALSE; + $extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected)); + } + else + { + if ($this->strict == TRUE) + $result = ($test === $expected) ? TRUE : FALSE; + else + $result = ($test == $expected) ? TRUE : FALSE; + + $extype = gettype($expected); + } + + $back = $this->_backtrace(); + + $report[] = array ( + 'test_name' => $test_name, + 'test_datatype' => gettype($test), + 'res_datatype' => $extype, + 'result' => ($result === TRUE) ? 'passed' : 'failed', + 'file' => $back['file'], + 'line' => $back['line'] + ); + + $this->results[] = $report; + + return($this->report($this->result($report))); + } + + // -------------------------------------------------------------------- + + /** + * Generate a report + * + * Displays a table with the test data + * + * @access public + * @return string + */ + function report($result = array()) + { + if (count($result) == 0) + { + $result = $this->result(); + } + + $CI =& get_instance(); + $CI->load->language('unit_test'); + + $this->_parse_template(); + + $r = ''; + foreach ($result as $res) + { + $table = ''; + + foreach ($res as $key => $val) + { + + if ($key == $CI->lang->line('ut_result')) + { + if ($val == $CI->lang->line('ut_passed')) + { + $val = ''.$val.''; + } + elseif ($val == $CI->lang->line('ut_failed')) + { + $val = ''.$val.''; + } + } + + $temp = $this->_template_rows; + $temp = str_replace('{item}', $key, $temp); + $temp = str_replace('{result}', $val, $temp); + $table .= $temp; + } + + $r .= str_replace('{rows}', $table, $this->_template); + } + + return $r; + } + + // -------------------------------------------------------------------- + + /** + * Use strict comparison + * + * Causes the evaluation to use === rather than == + * + * @access public + * @param bool + * @return null + */ + function use_strict($state = TRUE) + { + $this->strict = ($state == FALSE) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Make Unit testing active + * + * Enables/disables unit testing + * + * @access public + * @param bool + * @return null + */ + function active($state = TRUE) + { + $this->active = ($state == FALSE) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Result Array + * + * Returns the raw result data + * + * @access public + * @return array + */ + function result($results = array()) + { + $CI =& get_instance(); + $CI->load->language('unit_test'); + + if (count($results) == 0) + { + $results = $this->results; + } + + $retval = array(); + foreach ($results as $result) + { + $temp = array(); + foreach ($result as $key => $val) + { + if (is_array($val)) + { + foreach ($val as $k => $v) + { + if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$v)))) + { + $v = $line; + } + $temp[$CI->lang->line('ut_'.$k)] = $v; + } + } + else + { + if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val)))) + { + $val = $line; + } + $temp[$CI->lang->line('ut_'.$key)] = $val; + } + } + + $retval[] = $temp; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Set the template + * + * This lets us set the template to be used to display results + * + * @access public + * @param string + * @return void + */ + function set_template($template) + { + $this->_template = $template; + } + + // -------------------------------------------------------------------- + + /** + * Generate a backtrace + * + * This lets us show file names and line numbers + * + * @access private + * @return array + */ + function _backtrace() + { + if (function_exists('debug_backtrace')) + { + $back = debug_backtrace(); + + $file = ( ! isset($back['1']['file'])) ? '' : $back['1']['file']; + $line = ( ! isset($back['1']['line'])) ? '' : $back['1']['line']; + + return array('file' => $file, 'line' => $line); + } + return array('file' => 'Unknown', 'line' => 'Unknown'); + } + + // -------------------------------------------------------------------- + + /** + * Get Default Template + * + * @access private + * @return string + */ + function _default_template() + { + $this->_template = "\n".''; + $this->_template .= '{rows}'; + $this->_template .= "\n".'
'; + + $this->_template_rows = "\n\t".''; + $this->_template_rows .= "\n\t\t".'{item}'; + $this->_template_rows .= "\n\t\t".'{result}'; + $this->_template_rows .= "\n\t".''; + } + + // -------------------------------------------------------------------- + + /** + * Parse Template + * + * Harvests the data within the template {pseudo-variables} + * + * @access private + * @return void + */ + function _parse_template() + { + if ( ! is_null($this->_template_rows)) + { + return; + } + + if (is_null($this->_template)) + { + $this->_default_template(); + return; + } + + if ( ! preg_match("/\{rows\}(.*?)\{\/rows\}/si", $this->_template, $match)) + { + $this->_default_template(); + return; + } + + $this->_template_rows = $match['1']; + $this->_template = str_replace($match['0'], '{rows}', $this->_template); + } + +} +// END Unit_test Class + +/** + * Helper functions to test boolean true/false + * + * + * @access private + * @return bool + */ +function is_true($test) +{ + return (is_bool($test) AND $test === TRUE) ? TRUE : FALSE; +} +function is_false($test) +{ + return (is_bool($test) AND $test === FALSE) ? TRUE : FALSE; +} + + +/* End of file Unit_test.php */ +/* Location: ./system/libraries/Unit_test.php */ \ No newline at end of file diff --git a/libraries/Upload.php b/libraries/Upload.php new file mode 100644 index 0000000..3832dab --- /dev/null +++ b/libraries/Upload.php @@ -0,0 +1,931 @@ + 0) + { + $this->initialize($props); + } + + log_message('debug', "Upload Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize preferences + * + * @access public + * @param array + * @return void + */ + function initialize($config = array()) + { + $defaults = array( + 'max_size' => 0, + 'max_width' => 0, + 'max_height' => 0, + 'max_filename' => 0, + 'allowed_types' => "", + 'file_temp' => "", + 'file_name' => "", + 'orig_name' => "", + 'file_type' => "", + 'file_size' => "", + 'file_ext' => "", + 'upload_path' => "", + 'overwrite' => FALSE, + 'encrypt_name' => FALSE, + 'is_image' => FALSE, + 'image_width' => '', + 'image_height' => '', + 'image_type' => '', + 'image_size_str' => '', + 'error_msg' => array(), + 'mimes' => array(), + 'remove_spaces' => TRUE, + 'xss_clean' => FALSE, + 'temp_prefix' => "temp_file_" + ); + + + foreach ($defaults as $key => $val) + { + if (isset($config[$key])) + { + $method = 'set_'.$key; + if (method_exists($this, $method)) + { + $this->$method($config[$key]); + } + else + { + $this->$key = $config[$key]; + } + } + else + { + $this->$key = $val; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Perform the file upload + * + * @access public + * @return bool + */ + function do_upload($field = 'userfile') + { + // Is $_FILES[$field] set? If not, no reason to continue. + if ( ! isset($_FILES[$field])) + { + $this->set_error('upload_no_file_selected'); + return FALSE; + } + + // Is the upload path valid? + if ( ! $this->validate_upload_path()) + { + // errors will already be set by validate_upload_path() so just return FALSE + return FALSE; + } + + // Was the file able to be uploaded? If not, determine the reason why. + if ( ! is_uploaded_file($_FILES[$field]['tmp_name'])) + { + $error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error']; + + switch($error) + { + case 1: // UPLOAD_ERR_INI_SIZE + $this->set_error('upload_file_exceeds_limit'); + break; + case 2: // UPLOAD_ERR_FORM_SIZE + $this->set_error('upload_file_exceeds_form_limit'); + break; + case 3: // UPLOAD_ERR_PARTIAL + $this->set_error('upload_file_partial'); + break; + case 4: // UPLOAD_ERR_NO_FILE + $this->set_error('upload_no_file_selected'); + break; + case 6: // UPLOAD_ERR_NO_TMP_DIR + $this->set_error('upload_no_temp_directory'); + break; + case 7: // UPLOAD_ERR_CANT_WRITE + $this->set_error('upload_unable_to_write_file'); + break; + case 8: // UPLOAD_ERR_EXTENSION + $this->set_error('upload_stopped_by_extension'); + break; + default : $this->set_error('upload_no_file_selected'); + break; + } + + return FALSE; + } + + // Set the uploaded data as class variables + $this->file_temp = $_FILES[$field]['tmp_name']; + $this->file_name = $this->_prep_filename($_FILES[$field]['name']); + $this->file_size = $_FILES[$field]['size']; + $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']); + $this->file_type = strtolower($this->file_type); + $this->file_ext = $this->get_extension($_FILES[$field]['name']); + + // Convert the file size to kilobytes + if ($this->file_size > 0) + { + $this->file_size = round($this->file_size/1024, 2); + } + + // Is the file type allowed to be uploaded? + if ( ! $this->is_allowed_filetype()) + { + $this->set_error('upload_invalid_filetype'); + return FALSE; + } + + // Is the file size within the allowed maximum? + if ( ! $this->is_allowed_filesize()) + { + $this->set_error('upload_invalid_filesize'); + return FALSE; + } + + // Are the image dimensions within the allowed size? + // Note: This can fail if the server has an open_basdir restriction. + if ( ! $this->is_allowed_dimensions()) + { + $this->set_error('upload_invalid_dimensions'); + return FALSE; + } + + // Sanitize the file name for security + $this->file_name = $this->clean_file_name($this->file_name); + + // Truncate the file name if it's too long + if ($this->max_filename > 0) + { + $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename); + } + + // Remove white spaces in the name + if ($this->remove_spaces == TRUE) + { + $this->file_name = preg_replace("/\s+/", "_", $this->file_name); + } + + /* + * Validate the file name + * This function appends an number onto the end of + * the file if one with the same name already exists. + * If it returns false there was a problem. + */ + $this->orig_name = $this->file_name; + + if ($this->overwrite == FALSE) + { + $this->file_name = $this->set_filename($this->upload_path, $this->file_name); + + if ($this->file_name === FALSE) + { + return FALSE; + } + } + + /* + * Move the file to the final destination + * To deal with different server configurations + * we'll attempt to use copy() first. If that fails + * we'll use move_uploaded_file(). One of the two should + * reliably work in most environments + */ + if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name)) + { + if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)) + { + $this->set_error('upload_destination_error'); + return FALSE; + } + } + + /* + * Run the file through the XSS hacking filter + * This helps prevent malicious code from being + * embedded within a file. Scripts can easily + * be disguised as images or other file types. + */ + if ($this->xss_clean == TRUE) + { + $this->do_xss_clean(); + } + + /* + * Set the finalized image dimensions + * This sets the image width/height (assuming the + * file was an image). We use this information + * in the "data" function. + */ + $this->set_image_properties($this->upload_path.$this->file_name); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Finalized Data Array + * + * Returns an associative array containing all of the information + * related to the upload, allowing the developer easy access in one array. + * + * @access public + * @return array + */ + function data() + { + return array ( + 'file_name' => $this->file_name, + 'file_type' => $this->file_type, + 'file_path' => $this->upload_path, + 'full_path' => $this->upload_path.$this->file_name, + 'raw_name' => str_replace($this->file_ext, '', $this->file_name), + 'orig_name' => $this->orig_name, + 'file_ext' => $this->file_ext, + 'file_size' => $this->file_size, + 'is_image' => $this->is_image(), + 'image_width' => $this->image_width, + 'image_height' => $this->image_height, + 'image_type' => $this->image_type, + 'image_size_str' => $this->image_size_str, + ); + } + + // -------------------------------------------------------------------- + + /** + * Set Upload Path + * + * @access public + * @param string + * @return void + */ + function set_upload_path($path) + { + // Make sure it has a trailing slash + $this->upload_path = rtrim($path, '/').'/'; + } + + // -------------------------------------------------------------------- + + /** + * Set the file name + * + * This function takes a filename/path as input and looks for the + * existence of a file with the same name. If found, it will append a + * number to the end of the filename to avoid overwriting a pre-existing file. + * + * @access public + * @param string + * @param string + * @return string + */ + function set_filename($path, $filename) + { + if ($this->encrypt_name == TRUE) + { + mt_srand(); + $filename = md5(uniqid(mt_rand())).$this->file_ext; + } + + if ( ! file_exists($path.$filename)) + { + return $filename; + } + + $filename = str_replace($this->file_ext, '', $filename); + + $new_filename = ''; + for ($i = 1; $i < 100; $i++) + { + if ( ! file_exists($path.$filename.$i.$this->file_ext)) + { + $new_filename = $filename.$i.$this->file_ext; + break; + } + } + + if ($new_filename == '') + { + $this->set_error('upload_bad_filename'); + return FALSE; + } + else + { + return $new_filename; + } + } + + // -------------------------------------------------------------------- + + /** + * Set Maximum File Size + * + * @access public + * @param integer + * @return void + */ + function set_max_filesize($n) + { + $this->max_size = ((int) $n < 0) ? 0: (int) $n; + } + + // -------------------------------------------------------------------- + + /** + * Set Maximum File Name Length + * + * @access public + * @param integer + * @return void + */ + function set_max_filename($n) + { + $this->max_filename = ((int) $n < 0) ? 0: (int) $n; + } + + // -------------------------------------------------------------------- + + /** + * Set Maximum Image Width + * + * @access public + * @param integer + * @return void + */ + function set_max_width($n) + { + $this->max_width = ((int) $n < 0) ? 0: (int) $n; + } + + // -------------------------------------------------------------------- + + /** + * Set Maximum Image Height + * + * @access public + * @param integer + * @return void + */ + function set_max_height($n) + { + $this->max_height = ((int) $n < 0) ? 0: (int) $n; + } + + // -------------------------------------------------------------------- + + /** + * Set Allowed File Types + * + * @access public + * @param string + * @return void + */ + function set_allowed_types($types) + { + $this->allowed_types = explode('|', $types); + } + + // -------------------------------------------------------------------- + + /** + * Set Image Properties + * + * Uses GD to determine the width/height/type of image + * + * @access public + * @param string + * @return void + */ + function set_image_properties($path = '') + { + if ( ! $this->is_image()) + { + return; + } + + if (function_exists('getimagesize')) + { + if (FALSE !== ($D = @getimagesize($path))) + { + $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); + + $this->image_width = $D['0']; + $this->image_height = $D['1']; + $this->image_type = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']]; + $this->image_size_str = $D['3']; // string containing height and width + } + } + } + + // -------------------------------------------------------------------- + + /** + * Set XSS Clean + * + * Enables the XSS flag so that the file that was uploaded + * will be run through the XSS filter. + * + * @access public + * @param bool + * @return void + */ + function set_xss_clean($flag = FALSE) + { + $this->xss_clean = ($flag == TRUE) ? TRUE : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Validate the image + * + * @access public + * @return bool + */ + function is_image() + { + // IE will sometimes return odd mime-types during upload, so here we just standardize all + // jpegs or pngs to the same file type. + + $png_mimes = array('image/x-png'); + $jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg'); + + if (in_array($this->file_type, $png_mimes)) + { + $this->file_type = 'image/png'; + } + + if (in_array($this->file_type, $jpeg_mimes)) + { + $this->file_type = 'image/jpeg'; + } + + $img_mimes = array( + 'image/gif', + 'image/jpeg', + 'image/png', + ); + + return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Verify that the filetype is allowed + * + * @access public + * @return bool + */ + function is_allowed_filetype() + { + if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types)) + { + $this->set_error('upload_no_file_types'); + return FALSE; + } + + foreach ($this->allowed_types as $val) + { + $mime = $this->mimes_types(strtolower($val)); + + if (is_array($mime)) + { + if (in_array($this->file_type, $mime, TRUE)) + { + return TRUE; + } + } + else + { + if ($mime == $this->file_type) + { + return TRUE; + } + } + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Verify that the file is within the allowed size + * + * @access public + * @return bool + */ + function is_allowed_filesize() + { + if ($this->max_size != 0 AND $this->file_size > $this->max_size) + { + return FALSE; + } + else + { + return TRUE; + } + } + + // -------------------------------------------------------------------- + + /** + * Verify that the image is within the allowed width/height + * + * @access public + * @return bool + */ + function is_allowed_dimensions() + { + if ( ! $this->is_image()) + { + return TRUE; + } + + if (function_exists('getimagesize')) + { + $D = @getimagesize($this->file_temp); + + if ($this->max_width > 0 AND $D['0'] > $this->max_width) + { + return FALSE; + } + + if ($this->max_height > 0 AND $D['1'] > $this->max_height) + { + return FALSE; + } + + return TRUE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Validate Upload Path + * + * Verifies that it is a valid upload path with proper permissions. + * + * + * @access public + * @return bool + */ + function validate_upload_path() + { + if ($this->upload_path == '') + { + $this->set_error('upload_no_filepath'); + return FALSE; + } + + if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE) + { + $this->upload_path = str_replace("\\", "/", realpath($this->upload_path)); + } + + if ( ! @is_dir($this->upload_path)) + { + $this->set_error('upload_no_filepath'); + return FALSE; + } + + if ( ! is_really_writable($this->upload_path)) + { + $this->set_error('upload_not_writable'); + return FALSE; + } + + $this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/", $this->upload_path); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Extract the file extension + * + * @access public + * @param string + * @return string + */ + function get_extension($filename) + { + $x = explode('.', $filename); + return '.'.end($x); + } + + // -------------------------------------------------------------------- + + /** + * Clean the file name for security + * + * @access public + * @param string + * @return string + */ + function clean_file_name($filename) + { + $bad = array( + "", + "'", + "<", + ">", + '"', + '&', + '$', + '=', + ';', + '?', + '/', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); + + $filename = str_replace($bad, '', $filename); + + return stripslashes($filename); + } + + // -------------------------------------------------------------------- + + /** + * Limit the File Name Length + * + * @access public + * @param string + * @return string + */ + function limit_filename_length($filename, $length) + { + if (strlen($filename) < $length) + { + return $filename; + } + + $ext = ''; + if (strpos($filename, '.') !== FALSE) + { + $parts = explode('.', $filename); + $ext = '.'.array_pop($parts); + $filename = implode('.', $parts); + } + + return substr($filename, 0, ($length - strlen($ext))).$ext; + } + + // -------------------------------------------------------------------- + + /** + * Runs the file through the XSS clean function + * + * This prevents people from embedding malicious code in their files. + * I'm not sure that it won't negatively affect certain files in unexpected ways, + * but so far I haven't found that it causes trouble. + * + * @access public + * @return void + */ + function do_xss_clean() + { + $file = $this->upload_path.$this->file_name; + + if (filesize($file) == 0) + { + return FALSE; + } + + if (($data = @file_get_contents($file)) === FALSE) + { + return FALSE; + } + + if ( ! $fp = @fopen($file, FOPEN_READ_WRITE)) + { + return FALSE; + } + + $CI =& get_instance(); + $data = $CI->input->xss_clean($data); + + flock($fp, LOCK_EX); + fwrite($fp, $data); + flock($fp, LOCK_UN); + fclose($fp); + } + + // -------------------------------------------------------------------- + + /** + * Set an error message + * + * @access public + * @param string + * @return void + */ + function set_error($msg) + { + $CI =& get_instance(); + $CI->lang->load('upload'); + + if (is_array($msg)) + { + foreach ($msg as $val) + { + $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + else + { + $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + + // -------------------------------------------------------------------- + + /** + * Display the error message + * + * @access public + * @param string + * @param string + * @return string + */ + function display_errors($open = '

', $close = '

') + { + $str = ''; + foreach ($this->error_msg as $val) + { + $str .= $open.$val.$close; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * List of Mime Types + * + * This is a list of mime types. We use it to validate + * the "allowed types" set by the developer + * + * @access public + * @param string + * @return string + */ + function mimes_types($mime) + { + global $mimes; + + if (count($this->mimes) == 0) + { + if (@require_once(APPPATH.'config/mimes'.EXT)) + { + $this->mimes = $mimes; + unset($mimes); + } + } + + return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime]; + } + + // -------------------------------------------------------------------- + + /** + * Prep Filename + * + * Prevents possible script execution from Apache's handling of files multiple extensions + * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext + * + * @access private + * @param string + * @return string + */ + function _prep_filename($filename) + { + if (strpos($filename, '.') === FALSE) + { + return $filename; + } + + $parts = explode('.', $filename); + $ext = array_pop($parts); + $filename = array_shift($parts); + + foreach ($parts as $part) + { + if ($this->mimes_types(strtolower($part)) === FALSE) + { + $filename .= '.'.$part.'_'; + } + else + { + $filename .= '.'.$part; + } + } + + $filename .= '.'.$ext; + + return $filename; + } + + // -------------------------------------------------------------------- + +} +// END Upload Class + +/* End of file Upload.php */ +/* Location: ./system/libraries/Upload.php */ \ No newline at end of file diff --git a/libraries/User_agent.php b/libraries/User_agent.php new file mode 100644 index 0000000..c7cf870 --- /dev/null +++ b/libraries/User_agent.php @@ -0,0 +1,502 @@ +agent = trim($_SERVER['HTTP_USER_AGENT']); + } + + if ( ! is_null($this->agent)) + { + if ($this->_load_agent_file()) + { + $this->_compile_data(); + } + } + + log_message('debug', "User Agent Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Compile the User Agent Data + * + * @access private + * @return bool + */ + function _load_agent_file() + { + if ( ! @include(APPPATH.'config/user_agents'.EXT)) + { + return FALSE; + } + + $return = FALSE; + + if (isset($platforms)) + { + $this->platforms = $platforms; + unset($platforms); + $return = TRUE; + } + + if (isset($browsers)) + { + $this->browsers = $browsers; + unset($browsers); + $return = TRUE; + } + + if (isset($mobiles)) + { + $this->mobiles = $mobiles; + unset($mobiles); + $return = TRUE; + } + + if (isset($robots)) + { + $this->robots = $robots; + unset($robots); + $return = TRUE; + } + + return $return; + } + + // -------------------------------------------------------------------- + + /** + * Compile the User Agent Data + * + * @access private + * @return bool + */ + function _compile_data() + { + $this->_set_platform(); + + foreach (array('_set_browser', '_set_robot', '_set_mobile') as $function) + { + if ($this->$function() === TRUE) + { + break; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Set the Platform + * + * @access private + * @return mixed + */ + function _set_platform() + { + if (is_array($this->platforms) AND count($this->platforms) > 0) + { + foreach ($this->platforms as $key => $val) + { + if (preg_match("|".preg_quote($key)."|i", $this->agent)) + { + $this->platform = $val; + return TRUE; + } + } + } + $this->platform = 'Unknown Platform'; + } + + // -------------------------------------------------------------------- + + /** + * Set the Browser + * + * @access private + * @return bool + */ + function _set_browser() + { + if (is_array($this->browsers) AND count($this->browsers) > 0) + { + foreach ($this->browsers as $key => $val) + { + if (preg_match("|".preg_quote($key).".*?([0-9\.]+)|i", $this->agent, $match)) + { + $this->is_browser = TRUE; + $this->version = $match[1]; + $this->browser = $val; + $this->_set_mobile(); + return TRUE; + } + } + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set the Robot + * + * @access private + * @return bool + */ + function _set_robot() + { + if (is_array($this->robots) AND count($this->robots) > 0) + { + foreach ($this->robots as $key => $val) + { + if (preg_match("|".preg_quote($key)."|i", $this->agent)) + { + $this->is_robot = TRUE; + $this->robot = $val; + return TRUE; + } + } + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set the Mobile Device + * + * @access private + * @return bool + */ + function _set_mobile() + { + if (is_array($this->mobiles) AND count($this->mobiles) > 0) + { + foreach ($this->mobiles as $key => $val) + { + if (FALSE !== (strpos(strtolower($this->agent), $key))) + { + $this->is_mobile = TRUE; + $this->mobile = $val; + return TRUE; + } + } + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set the accepted languages + * + * @access private + * @return void + */ + function _set_languages() + { + if ((count($this->languages) == 0) AND isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) AND $_SERVER['HTTP_ACCEPT_LANGUAGE'] != '') + { + $languages = preg_replace('/(;q=[0-9\.]+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))); + + $this->languages = explode(',', $languages); + } + + if (count($this->languages) == 0) + { + $this->languages = array('Undefined'); + } + } + + // -------------------------------------------------------------------- + + /** + * Set the accepted character sets + * + * @access private + * @return void + */ + function _set_charsets() + { + if ((count($this->charsets) == 0) AND isset($_SERVER['HTTP_ACCEPT_CHARSET']) AND $_SERVER['HTTP_ACCEPT_CHARSET'] != '') + { + $charsets = preg_replace('/(;q=.+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))); + + $this->charsets = explode(',', $charsets); + } + + if (count($this->charsets) == 0) + { + $this->charsets = array('Undefined'); + } + } + + // -------------------------------------------------------------------- + + /** + * Is Browser + * + * @access public + * @return bool + */ + function is_browser() + { + return $this->is_browser; + } + + // -------------------------------------------------------------------- + + /** + * Is Robot + * + * @access public + * @return bool + */ + function is_robot() + { + return $this->is_robot; + } + + // -------------------------------------------------------------------- + + /** + * Is Mobile + * + * @access public + * @return bool + */ + function is_mobile() + { + return $this->is_mobile; + } + + // -------------------------------------------------------------------- + + /** + * Is this a referral from another site? + * + * @access public + * @return bool + */ + function is_referral() + { + return ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == '') ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Agent String + * + * @access public + * @return string + */ + function agent_string() + { + return $this->agent; + } + + // -------------------------------------------------------------------- + + /** + * Get Platform + * + * @access public + * @return string + */ + function platform() + { + return $this->platform; + } + + // -------------------------------------------------------------------- + + /** + * Get Browser Name + * + * @access public + * @return string + */ + function browser() + { + return $this->browser; + } + + // -------------------------------------------------------------------- + + /** + * Get the Browser Version + * + * @access public + * @return string + */ + function version() + { + return $this->version; + } + + // -------------------------------------------------------------------- + + /** + * Get The Robot Name + * + * @access public + * @return string + */ + function robot() + { + return $this->robot; + } + // -------------------------------------------------------------------- + + /** + * Get the Mobile Device + * + * @access public + * @return string + */ + function mobile() + { + return $this->mobile; + } + + // -------------------------------------------------------------------- + + /** + * Get the referrer + * + * @access public + * @return bool + */ + function referrer() + { + return ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == '') ? '' : trim($_SERVER['HTTP_REFERER']); + } + + // -------------------------------------------------------------------- + + /** + * Get the accepted languages + * + * @access public + * @return array + */ + function languages() + { + if (count($this->languages) == 0) + { + $this->_set_languages(); + } + + return $this->languages; + } + + // -------------------------------------------------------------------- + + /** + * Get the accepted Character Sets + * + * @access public + * @return array + */ + function charsets() + { + if (count($this->charsets) == 0) + { + $this->_set_charsets(); + } + + return $this->charsets; + } + + // -------------------------------------------------------------------- + + /** + * Test for a particular language + * + * @access public + * @return bool + */ + function accept_lang($lang = 'en') + { + return (in_array(strtolower($lang), $this->languages(), TRUE)) ? TRUE : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Test for a particular character set + * + * @access public + * @return bool + */ + function accept_charset($charset = 'utf-8') + { + return (in_array(strtolower($charset), $this->charsets(), TRUE)) ? TRUE : FALSE; + } + + +} + + +/* End of file User_agent.php */ +/* Location: ./system/libraries/User_agent.php */ \ No newline at end of file diff --git a/libraries/Validation.php b/libraries/Validation.php new file mode 100644 index 0000000..a463202 --- /dev/null +++ b/libraries/Validation.php @@ -0,0 +1,875 @@ +'; + var $_error_suffix = '

'; + + + + /** + * Constructor + * + */ + function CI_Validation() + { + $this->CI =& get_instance(); + + if (function_exists('mb_internal_encoding')) + { + mb_internal_encoding($this->CI->config->item('charset')); + } + + log_message('debug', "Validation Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Set Fields + * + * This function takes an array of field names as input + * and generates class variables with the same name, which will + * either be blank or contain the $_POST value corresponding to it + * + * @access public + * @param string + * @param string + * @return void + */ + function set_fields($data = '', $field = '') + { + if ($data == '') + { + if (count($this->_fields) == 0) + { + return FALSE; + } + } + else + { + if ( ! is_array($data)) + { + $data = array($data => $field); + } + + if (count($data) > 0) + { + $this->_fields = $data; + } + } + + foreach($this->_fields as $key => $val) + { + $this->$key = ( ! isset($_POST[$key])) ? '' : $this->prep_for_form($_POST[$key]); + + $error = $key.'_error'; + if ( ! isset($this->$error)) + { + $this->$error = ''; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Set Rules + * + * This function takes an array of field names and validation + * rules as input ad simply stores is for use later. + * + * @access public + * @param mixed + * @param string + * @return void + */ + function set_rules($data, $rules = '') + { + if ( ! is_array($data)) + { + if ($rules == '') + return; + + $data = array($data => $rules); + } + + foreach ($data as $key => $val) + { + $this->_rules[$key] = $val; + } + } + + // -------------------------------------------------------------------- + + /** + * Set Error Message + * + * Lets users set their own error messages on the fly. Note: The key + * name has to match the function name that it corresponds to. + * + * @access public + * @param string + * @param string + * @return string + */ + function set_message($lang, $val = '') + { + if ( ! is_array($lang)) + { + $lang = array($lang => $val); + } + + $this->_error_messages = array_merge($this->_error_messages, $lang); + } + + // -------------------------------------------------------------------- + + /** + * Set The Error Delimiter + * + * Permits a prefix/suffix to be added to each error message + * + * @access public + * @param string + * @param string + * @return void + */ + function set_error_delimiters($prefix = '

', $suffix = '

') + { + $this->_error_prefix = $prefix; + $this->_error_suffix = $suffix; + } + + // -------------------------------------------------------------------- + + /** + * Run the Validator + * + * This function does all the work. + * + * @access public + * @return bool + */ + function run() + { + // Do we even have any data to process? Mm? + if (count($_POST) == 0 OR count($this->_rules) == 0) + { + return FALSE; + } + + // Load the language file containing error messages + $this->CI->lang->load('validation'); + + // Cycle through the rules and test for errors + foreach ($this->_rules as $field => $rules) + { + //Explode out the rules! + $ex = explode('|', $rules); + + // Is the field required? If not, if the field is blank we'll move on to the next test + if ( ! in_array('required', $ex, TRUE)) + { + if ( ! isset($_POST[$field]) OR $_POST[$field] == '') + { + continue; + } + } + + /* + * Are we dealing with an "isset" rule? + * + * Before going further, we'll see if one of the rules + * is to check whether the item is set (typically this + * applies only to checkboxes). If so, we'll + * test for it here since there's not reason to go + * further + */ + if ( ! isset($_POST[$field])) + { + if (in_array('isset', $ex, TRUE) OR in_array('required', $ex)) + { + if ( ! isset($this->_error_messages['isset'])) + { + if (FALSE === ($line = $this->CI->lang->line('isset'))) + { + $line = 'The field was not set'; + } + } + else + { + $line = $this->_error_messages['isset']; + } + + // Build the error message + $mfield = ( ! isset($this->_fields[$field])) ? $field : $this->_fields[$field]; + $message = sprintf($line, $mfield); + + // Set the error variable. Example: $this->username_error + $error = $field.'_error'; + $this->$error = $this->_error_prefix.$message.$this->_error_suffix; + $this->_error_array[] = $message; + } + + continue; + } + + /* + * Set the current field + * + * The various prepping functions need to know the + * current field name so they can do this: + * + * $_POST[$this->_current_field] == 'bla bla'; + */ + $this->_current_field = $field; + + // Cycle through the rules! + foreach ($ex As $rule) + { + // Is the rule a callback? + $callback = FALSE; + if (substr($rule, 0, 9) == 'callback_') + { + $rule = substr($rule, 9); + $callback = TRUE; + } + + // Strip the parameter (if exists) from the rule + // Rules can contain a parameter: max_length[5] + $param = FALSE; + if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match)) + { + $rule = $match[1]; + $param = $match[2]; + } + + // Call the function that corresponds to the rule + if ($callback === TRUE) + { + if ( ! method_exists($this->CI, $rule)) + { + continue; + } + + $result = $this->CI->$rule($_POST[$field], $param); + + // If the field isn't required and we just processed a callback we'll move on... + if ( ! in_array('required', $ex, TRUE) AND $result !== FALSE) + { + continue 2; + } + + } + else + { + if ( ! method_exists($this, $rule)) + { + /* + * Run the native PHP function if called for + * + * If our own wrapper function doesn't exist we see + * if a native PHP function does. Users can use + * any native PHP function call that has one param. + */ + if (function_exists($rule)) + { + $_POST[$field] = $rule($_POST[$field]); + $this->$field = $_POST[$field]; + } + + continue; + } + + $result = $this->$rule($_POST[$field], $param); + } + + // Did the rule test negatively? If so, grab the error. + if ($result === FALSE) + { + if ( ! isset($this->_error_messages[$rule])) + { + if (FALSE === ($line = $this->CI->lang->line($rule))) + { + $line = 'Unable to access an error message corresponding to your field name.'; + } + } + else + { + $line = $this->_error_messages[$rule]; + } + + // Build the error message + $mfield = ( ! isset($this->_fields[$field])) ? $field : $this->_fields[$field]; + $mparam = ( ! isset($this->_fields[$param])) ? $param : $this->_fields[$param]; + $message = sprintf($line, $mfield, $mparam); + + // Set the error variable. Example: $this->username_error + $error = $field.'_error'; + $this->$error = $this->_error_prefix.$message.$this->_error_suffix; + + // Add the error to the error array + $this->_error_array[] = $message; + continue 2; + } + } + + } + + $total_errors = count($this->_error_array); + + /* + * Recompile the class variables + * + * If any prepping functions were called the $_POST data + * might now be different then the corresponding class + * variables so we'll set them anew. + */ + if ($total_errors > 0) + { + $this->_safe_form_data = TRUE; + } + + $this->set_fields(); + + // Did we end up with any errors? + if ($total_errors == 0) + { + return TRUE; + } + + // Generate the error string + foreach ($this->_error_array as $val) + { + $this->error_string .= $this->_error_prefix.$val.$this->_error_suffix."\n"; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Required + * + * @access public + * @param string + * @return bool + */ + function required($str) + { + if ( ! is_array($str)) + { + return (trim($str) == '') ? FALSE : TRUE; + } + else + { + return ( ! empty($str)); + } + } + + // -------------------------------------------------------------------- + + /** + * Match one field to another + * + * @access public + * @param string + * @param field + * @return bool + */ + function matches($str, $field) + { + if ( ! isset($_POST[$field])) + { + return FALSE; + } + + return ($str !== $_POST[$field]) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Minimum Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function min_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) < $val) ? FALSE : TRUE; + } + + return (strlen($str) < $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Max Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function max_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) > $val) ? FALSE : TRUE; + } + + return (strlen($str) > $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Exact Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function exact_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) != $val) ? FALSE : TRUE; + } + + return (strlen($str) != $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Email + * + * @access public + * @param string + * @return bool + */ + function valid_email($str) + { + return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Emails + * + * @access public + * @param string + * @return bool + */ + function valid_emails($str) + { + if (strpos($str, ',') === FALSE) + { + return $this->valid_email(trim($str)); + } + + foreach(explode(',', $str) as $email) + { + if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * @access public + * @param string + * @return string + */ + function valid_ip($ip) + { + return $this->CI->input->valid_ip($ip); + } + + // -------------------------------------------------------------------- + + /** + * Alpha + * + * @access public + * @param string + * @return bool + */ + function alpha($str) + { + return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric + * + * @access public + * @param string + * @return bool + */ + function alpha_numeric($str) + { + return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric with underscores and dashes + * + * @access public + * @param string + * @return bool + */ + function alpha_dash($str) + { + return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Numeric + * + * @access public + * @param string + * @return bool + */ + function numeric($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str); + + } + + // -------------------------------------------------------------------- + + /** + * Is Numeric + * + * @access public + * @param string + * @return bool + */ + function is_numeric($str) + { + return ( ! is_numeric($str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Integer + * + * @access public + * @param string + * @return bool + */ + function integer($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number (0,1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural($str) + { + return (bool)preg_match( '/^[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number, but not a zero (1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural_no_zero($str) + { + if ( ! preg_match( '/^[0-9]+$/', $str)) + { + return FALSE; + } + + if ($str == 0) + { + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Base64 + * + * Tests a string for characters outside of the Base64 alphabet + * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 + * + * @access public + * @param string + * @return bool + */ + function valid_base64($str) + { + return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Set Select + * + * Enables pull-down lists to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_select($field = '', $value = '') + { + if ($field == '' OR $value == '' OR ! isset($_POST[$field])) + { + return ''; + } + + if ($_POST[$field] == $value) + { + return ' selected="selected"'; + } + } + + // -------------------------------------------------------------------- + + /** + * Set Radio + * + * Enables radio buttons to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_radio($field = '', $value = '') + { + if ($field == '' OR $value == '' OR ! isset($_POST[$field])) + { + return ''; + } + + if ($_POST[$field] == $value) + { + return ' checked="checked"'; + } + } + + // -------------------------------------------------------------------- + + /** + * Set Checkbox + * + * Enables checkboxes to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_checkbox($field = '', $value = '') + { + if ($field == '' OR $value == '' OR ! isset($_POST[$field])) + { + return ''; + } + + if ($_POST[$field] == $value) + { + return ' checked="checked"'; + } + } + + // -------------------------------------------------------------------- + + /** + * Prep data for form + * + * This function allows HTML to be safely shown in a form. + * Special characters are converted. + * + * @access public + * @param string + * @return string + */ + function prep_for_form($data = '') + { + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = $this->prep_for_form($val); + } + + return $data; + } + + if ($this->_safe_form_data == FALSE OR $data == '') + { + return $data; + } + + return str_replace(array("'", '"', '<', '>'), array("'", """, '<', '>'), stripslashes($data)); + } + + // -------------------------------------------------------------------- + + /** + * Prep URL + * + * @access public + * @param string + * @return string + */ + function prep_url($str = '') + { + if ($str == 'http://' OR $str == '') + { + $_POST[$this->_current_field] = ''; + return; + } + + if (substr($str, 0, 7) != 'http://' && substr($str, 0, 8) != 'https://') + { + $str = 'http://'.$str; + } + + $_POST[$this->_current_field] = $str; + } + + // -------------------------------------------------------------------- + + /** + * Strip Image Tags + * + * @access public + * @param string + * @return string + */ + function strip_image_tags($str) + { + $_POST[$this->_current_field] = $this->CI->input->strip_image_tags($str); + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * @access public + * @param string + * @return string + */ + function xss_clean($str) + { + $_POST[$this->_current_field] = $this->CI->input->xss_clean($str); + } + + // -------------------------------------------------------------------- + + /** + * Convert PHP tags to entities + * + * @access public + * @param string + * @return string + */ + function encode_php_tags($str) + { + $_POST[$this->_current_field] = str_replace(array(''), array('<?php', '<?PHP', '<?', '?>'), $str); + } + +} +// END Validation Class + +/* End of file Validation.php */ +/* Location: ./system/libraries/Validation.php */ \ No newline at end of file diff --git a/libraries/Xmlrpc.php b/libraries/Xmlrpc.php new file mode 100644 index 0000000..c9e7972 --- /dev/null +++ b/libraries/Xmlrpc.php @@ -0,0 +1,1421 @@ +xmlrpcName = $this->xmlrpcName; + $this->xmlrpc_backslash = chr(92).chr(92); + + // Types for info sent back and forth + $this->xmlrpcTypes = array( + $this->xmlrpcI4 => '1', + $this->xmlrpcInt => '1', + $this->xmlrpcBoolean => '1', + $this->xmlrpcString => '1', + $this->xmlrpcDouble => '1', + $this->xmlrpcDateTime => '1', + $this->xmlrpcBase64 => '1', + $this->xmlrpcArray => '2', + $this->xmlrpcStruct => '3' + ); + + // Array of Valid Parents for Various XML-RPC elements + $this->valid_parents = array('BOOLEAN' => array('VALUE'), + 'I4' => array('VALUE'), + 'INT' => array('VALUE'), + 'STRING' => array('VALUE'), + 'DOUBLE' => array('VALUE'), + 'DATETIME.ISO8601' => array('VALUE'), + 'BASE64' => array('VALUE'), + 'ARRAY' => array('VALUE'), + 'STRUCT' => array('VALUE'), + 'PARAM' => array('PARAMS'), + 'METHODNAME' => array('METHODCALL'), + 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), + 'MEMBER' => array('STRUCT'), + 'NAME' => array('MEMBER'), + 'DATA' => array('ARRAY'), + 'FAULT' => array('METHODRESPONSE'), + 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT') + ); + + + // XML-RPC Responses + $this->xmlrpcerr['unknown_method'] = '1'; + $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server'; + $this->xmlrpcerr['invalid_return'] = '2'; + $this->xmlrpcstr['invalid_return'] = 'The XML data receieved was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.'; + $this->xmlrpcerr['incorrect_params'] = '3'; + $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method'; + $this->xmlrpcerr['introspect_unknown'] = '4'; + $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown"; + $this->xmlrpcerr['http_error'] = '5'; + $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server."; + $this->xmlrpcerr['no_data'] = '6'; + $this->xmlrpcstr['no_data'] ='No data received from server.'; + + $this->initialize($config); + + log_message('debug', "XML-RPC Class Initialized"); + } + + + //------------------------------------- + // Initialize Prefs + //------------------------------------- + + function initialize($config = array()) + { + if (sizeof($config) > 0) + { + foreach ($config as $key => $val) + { + if (isset($this->$key)) + { + $this->$key = $val; + } + } + } + } + // END + + //------------------------------------- + // Take URL and parse it + //------------------------------------- + + function server($url, $port=80) + { + if (substr($url, 0, 4) != "http") + { + $url = "http://".$url; + } + + $parts = parse_url($url); + + $path = ( ! isset($parts['path'])) ? '/' : $parts['path']; + + if (isset($parts['query']) && $parts['query'] != '') + { + $path .= '?'.$parts['query']; + } + + $this->client = new XML_RPC_Client($path, $parts['host'], $port); + } + // END + + //------------------------------------- + // Set Timeout + //------------------------------------- + + function timeout($seconds=5) + { + if ( ! is_null($this->client) && is_int($seconds)) + { + $this->client->timeout = $seconds; + } + } + // END + + //------------------------------------- + // Set Methods + //------------------------------------- + + function method($function) + { + $this->method = $function; + } + // END + + //------------------------------------- + // Take Array of Data and Create Objects + //------------------------------------- + + function request($incoming) + { + if ( ! is_array($incoming)) + { + // Send Error + } + + $this->data = array(); + + foreach($incoming as $key => $value) + { + $this->data[$key] = $this->values_parsing($value); + } + } + // END + + + //------------------------------------- + // Set Debug + //------------------------------------- + + function set_debug($flag = TRUE) + { + $this->debug = ($flag == TRUE) ? TRUE : FALSE; + } + + //------------------------------------- + // Values Parsing + //------------------------------------- + + function values_parsing($value, $return = FALSE) + { + if (is_array($value) && isset($value['0'])) + { + if ( ! isset($value['1']) OR ! isset($this->xmlrpcTypes[strtolower($value['1'])])) + { + if (is_array($value[0])) + { + $temp = new XML_RPC_Values($value['0'], 'array'); + } + else + { + $temp = new XML_RPC_Values($value['0'], 'string'); + } + } + elseif(is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array')) + { + while (list($k) = each($value['0'])) + { + $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE); + } + + $temp = new XML_RPC_Values($value['0'], $value['1']); + } + else + { + $temp = new XML_RPC_Values($value['0'], $value['1']); + } + } + else + { + $temp = new XML_RPC_Values($value, 'string'); + } + + return $temp; + } + // END + + + //------------------------------------- + // Sends XML-RPC Request + //------------------------------------- + + function send_request() + { + $this->message = new XML_RPC_Message($this->method,$this->data); + $this->message->debug = $this->debug; + + if ( ! $this->result = $this->client->send($this->message)) + { + $this->error = $this->result->errstr; + return FALSE; + } + elseif( ! is_object($this->result->val)) + { + $this->error = $this->result->errstr; + return FALSE; + } + + $this->response = $this->result->decode(); + + return TRUE; + } + // END + + //------------------------------------- + // Returns Error + //------------------------------------- + + function display_error() + { + return $this->error; + } + // END + + //------------------------------------- + // Returns Remote Server Response + //------------------------------------- + + function display_response() + { + return $this->response; + } + // END + + //------------------------------------- + // Sends an Error Message for Server Request + //------------------------------------- + + function send_error_message($number, $message) + { + return new XML_RPC_Response('0',$number, $message); + } + // END + + + //------------------------------------- + // Send Response for Server Request + //------------------------------------- + + function send_response($response) + { + // $response should be array of values, which will be parsed + // based on their data and type into a valid group of XML-RPC values + + $response = $this->values_parsing($response); + + return new XML_RPC_Response($response); + } + // END + +} // END XML_RPC Class + + + +/** + * XML-RPC Client class + * + * @category XML-RPC + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + */ +class XML_RPC_Client extends CI_Xmlrpc +{ + var $path = ''; + var $server = ''; + var $port = 80; + var $errno = ''; + var $errstring = ''; + var $timeout = 5; + var $no_multicall = false; + + function XML_RPC_Client($path, $server, $port=80) + { + parent::CI_Xmlrpc(); + + $this->port = $port; + $this->server = $server; + $this->path = $path; + } + + function send($msg) + { + if (is_array($msg)) + { + // Multi-call disabled + $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']); + return $r; + } + + return $this->sendPayload($msg); + } + + function sendPayload($msg) + { + $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout); + + if ( ! is_resource($fp)) + { + error_log($this->xmlrpcstr['http_error']); + $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']); + return $r; + } + + if(empty($msg->payload)) + { + // $msg = XML_RPC_Messages + $msg->createPayload(); + } + + $r = "\r\n"; + $op = "POST {$this->path} HTTP/1.0$r"; + $op .= "Host: {$this->server}$r"; + $op .= "Content-Type: text/xml$r"; + $op .= "User-Agent: {$this->xmlrpcName}$r"; + $op .= "Content-Length: ".strlen($msg->payload). "$r$r"; + $op .= $msg->payload; + + + if ( ! fputs($fp, $op, strlen($op))) + { + error_log($this->xmlrpcstr['http_error']); + $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']); + return $r; + } + $resp = $msg->parseResponse($fp); + fclose($fp); + return $resp; + } + +} // end class XML_RPC_Client + + +/** + * XML-RPC Response class + * + * @category XML-RPC + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + */ +class XML_RPC_Response +{ + var $val = 0; + var $errno = 0; + var $errstr = ''; + var $headers = array(); + + function XML_RPC_Response($val, $code = 0, $fstr = '') + { + if ($code != 0) + { + // error + $this->errno = $code; + $this->errstr = htmlentities($fstr); + } + else if ( ! is_object($val)) + { + // programmer error, not an object + error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response. Defaulting to empty value."); + $this->val = new XML_RPC_Values(); + } + else + { + $this->val = $val; + } + } + + function faultCode() + { + return $this->errno; + } + + function faultString() + { + return $this->errstr; + } + + function value() + { + return $this->val; + } + + function prepare_response() + { + $result = "\n"; + if ($this->errno) + { + $result .= ' + + + + faultCode + ' . $this->errno . ' + + + faultString + ' . $this->errstr . ' + + + +'; + } + else + { + $result .= "\n\n" . + $this->val->serialize_class() . + "\n"; + } + $result .= "\n"; + return $result; + } + + function decode($array=FALSE) + { + $CI =& get_instance(); + + if ($array !== FALSE && is_array($array)) + { + while (list($key) = each($array)) + { + if (is_array($array[$key])) + { + $array[$key] = $this->decode($array[$key]); + } + else + { + $array[$key] = $CI->input->xss_clean($array[$key]); + } + } + + $result = $array; + } + else + { + $result = $this->xmlrpc_decoder($this->val); + + if (is_array($result)) + { + $result = $this->decode($result); + } + else + { + $result = $CI->input->xss_clean($result); + } + } + + return $result; + } + + + + //------------------------------------- + // XML-RPC Object to PHP Types + //------------------------------------- + + function xmlrpc_decoder($xmlrpc_val) + { + $kind = $xmlrpc_val->kindOf(); + + if($kind == 'scalar') + { + return $xmlrpc_val->scalarval(); + } + elseif($kind == 'array') + { + reset($xmlrpc_val->me); + list($a,$b) = each($xmlrpc_val->me); + $size = sizeof($b); + + $arr = array(); + + for($i = 0; $i < $size; $i++) + { + $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]); + } + return $arr; + } + elseif($kind == 'struct') + { + reset($xmlrpc_val->me['struct']); + $arr = array(); + + while(list($key,$value) = each($xmlrpc_val->me['struct'])) + { + $arr[$key] = $this->xmlrpc_decoder($value); + } + return $arr; + } + } + + + //------------------------------------- + // ISO-8601 time to server or UTC time + //------------------------------------- + + function iso8601_decode($time, $utc=0) + { + // return a timet in the localtime, or UTC + $t = 0; + if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs)) + { + if ($utc == 1) + $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + else + $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + } + return $t; + } + +} // End Response Class + + + +/** + * XML-RPC Message class + * + * @category XML-RPC + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + */ +class XML_RPC_Message extends CI_Xmlrpc +{ + var $payload; + var $method_name; + var $params = array(); + var $xh = array(); + + function XML_RPC_Message($method, $pars=0) + { + parent::CI_Xmlrpc(); + + $this->method_name = $method; + if (is_array($pars) && sizeof($pars) > 0) + { + for($i=0; $iparams[] = $pars[$i]; + } + } + } + + //------------------------------------- + // Create Payload to Send + //------------------------------------- + + function createPayload() + { + $this->payload = "\r\n\r\n"; + $this->payload .= '' . $this->method_name . "\r\n"; + $this->payload .= "\r\n"; + + for($i=0; $iparams); $i++) + { + // $p = XML_RPC_Values + $p = $this->params[$i]; + $this->payload .= "\r\n".$p->serialize_class()."\r\n"; + } + + $this->payload .= "\r\n\r\n"; + } + + //------------------------------------- + // Parse External XML-RPC Server's Response + //------------------------------------- + + function parseResponse($fp) + { + $data = ''; + + while($datum = fread($fp, 4096)) + { + $data .= $datum; + } + + //------------------------------------- + // DISPLAY HTTP CONTENT for DEBUGGING + //------------------------------------- + + if ($this->debug === TRUE) + { + echo "
";
+			echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
+			echo "
"; + } + + //------------------------------------- + // Check for data + //------------------------------------- + + if($data == "") + { + error_log($this->xmlrpcstr['no_data']); + $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']); + return $r; + } + + + //------------------------------------- + // Check for HTTP 200 Response + //------------------------------------- + + if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data)) + { + $errstr= substr($data, 0, strpos($data, "\n")-1); + $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')'); + return $r; + } + + //------------------------------------- + // Create and Set Up XML Parser + //------------------------------------- + + $parser = xml_parser_create($this->xmlrpc_defencoding); + + $this->xh[$parser] = array(); + $this->xh[$parser]['isf'] = 0; + $this->xh[$parser]['ac'] = ''; + $this->xh[$parser]['headers'] = array(); + $this->xh[$parser]['stack'] = array(); + $this->xh[$parser]['valuestack'] = array(); + $this->xh[$parser]['isf_reason'] = 0; + + xml_set_object($parser, $this); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); + xml_set_element_handler($parser, 'open_tag', 'closing_tag'); + xml_set_character_data_handler($parser, 'character_data'); + //xml_set_default_handler($parser, 'default_handler'); + + + //------------------------------------- + // GET HEADERS + //------------------------------------- + + $lines = explode("\r\n", $data); + while (($line = array_shift($lines))) + { + if (strlen($line) < 1) + { + break; + } + $this->xh[$parser]['headers'][] = $line; + } + $data = implode("\r\n", $lines); + + + //------------------------------------- + // PARSE XML DATA + //------------------------------------- + + if ( ! xml_parse($parser, $data, sizeof($data))) + { + $errstr = sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser)), + xml_get_current_line_number($parser)); + //error_log($errstr); + $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); + xml_parser_free($parser); + return $r; + } + xml_parser_free($parser); + + // --------------------------------------- + // Got Ourselves Some Badness, It Seems + // --------------------------------------- + + if ($this->xh[$parser]['isf'] > 1) + { + if ($this->debug === TRUE) + { + echo "---Invalid Return---\n"; + echo $this->xh[$parser]['isf_reason']; + echo "---Invalid Return---\n\n"; + } + + $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']); + return $r; + } + elseif ( ! is_object($this->xh[$parser]['value'])) + { + $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']); + return $r; + } + + //------------------------------------- + // DISPLAY XML CONTENT for DEBUGGING + //------------------------------------- + + if ($this->debug === TRUE) + { + echo "
";
+			
+			if (count($this->xh[$parser]['headers'] > 0))
+			{
+				echo "---HEADERS---\n";
+				foreach ($this->xh[$parser]['headers'] as $header)
+				{
+					echo "$header\n";
+				}
+				echo "---END HEADERS---\n\n";
+			}
+			
+			echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
+			
+			echo "---PARSED---\n" ;
+			var_dump($this->xh[$parser]['value']);
+			echo "\n---END PARSED---
"; + } + + //------------------------------------- + // SEND RESPONSE + //------------------------------------- + + $v = $this->xh[$parser]['value']; + + if ($this->xh[$parser]['isf']) + { + $errno_v = $v->me['struct']['faultCode']; + $errstr_v = $v->me['struct']['faultString']; + $errno = $errno_v->scalarval(); + + if ($errno == 0) + { + // FAULT returned, errno needs to reflect that + $errno = -1; + } + + $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval()); + } + else + { + $r = new XML_RPC_Response($v); + } + + $r->headers = $this->xh[$parser]['headers']; + return $r; + } + + // ------------------------------------ + // Begin Return Message Parsing section + // ------------------------------------ + + // quick explanation of components: + // ac - used to accumulate values + // isf - used to indicate a fault + // lv - used to indicate "looking for a value": implements + // the logic to allow values with no types to be strings + // params - used to store parameters in method calls + // method - used to store method name + // stack - array with parent tree of the xml element, + // used to validate the nesting of elements + + //------------------------------------- + // Start Element Handler + //------------------------------------- + + function open_tag($the_parser, $name, $attrs) + { + // If invalid nesting, then return + if ($this->xh[$the_parser]['isf'] > 1) return; + + // Evaluate and check for correct nesting of XML elements + + if (count($this->xh[$the_parser]['stack']) == 0) + { + if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') + { + $this->xh[$the_parser]['isf'] = 2; + $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing'; + return; + } + } + else + { + // not top level element: see if parent is OK + if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE)) + { + $this->xh[$the_parser]['isf'] = 2; + $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0]; + return; + } + } + + switch($name) + { + case 'STRUCT': + case 'ARRAY': + // Creates array for child elements + + $cur_val = array('value' => array(), + 'type' => $name); + + array_unshift($this->xh[$the_parser]['valuestack'], $cur_val); + break; + case 'METHODNAME': + case 'NAME': + $this->xh[$the_parser]['ac'] = ''; + break; + case 'FAULT': + $this->xh[$the_parser]['isf'] = 1; + break; + case 'PARAM': + $this->xh[$the_parser]['value'] = null; + break; + case 'VALUE': + $this->xh[$the_parser]['vt'] = 'value'; + $this->xh[$the_parser]['ac'] = ''; + $this->xh[$the_parser]['lv'] = 1; + break; + case 'I4': + case 'INT': + case 'STRING': + case 'BOOLEAN': + case 'DOUBLE': + case 'DATETIME.ISO8601': + case 'BASE64': + if ($this->xh[$the_parser]['vt'] != 'value') + { + //two data elements inside a value: an error occurred! + $this->xh[$the_parser]['isf'] = 2; + $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value"; + return; + } + + $this->xh[$the_parser]['ac'] = ''; + break; + case 'MEMBER': + // Set name of to nothing to prevent errors later if no is found + $this->xh[$the_parser]['valuestack'][0]['name'] = ''; + + // Set NULL value to check to see if value passed for this param/member + $this->xh[$the_parser]['value'] = null; + break; + case 'DATA': + case 'METHODCALL': + case 'METHODRESPONSE': + case 'PARAMS': + // valid elements that add little to processing + break; + default: + /// An Invalid Element is Found, so we have trouble + $this->xh[$the_parser]['isf'] = 2; + $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name"; + break; + } + + // Add current element name to stack, to allow validation of nesting + array_unshift($this->xh[$the_parser]['stack'], $name); + + if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0; + } + // END + + + //------------------------------------- + // End Element Handler + //------------------------------------- + + function closing_tag($the_parser, $name) + { + if ($this->xh[$the_parser]['isf'] > 1) return; + + // Remove current element from stack and set variable + // NOTE: If the XML validates, then we do not have to worry about + // the opening and closing of elements. Nesting is checked on the opening + // tag so we be safe there as well. + + $curr_elem = array_shift($this->xh[$the_parser]['stack']); + + switch($name) + { + case 'STRUCT': + case 'ARRAY': + $cur_val = array_shift($this->xh[$the_parser]['valuestack']); + $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values']; + $this->xh[$the_parser]['vt'] = strtolower($name); + break; + case 'NAME': + $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac']; + break; + case 'BOOLEAN': + case 'I4': + case 'INT': + case 'STRING': + case 'DOUBLE': + case 'DATETIME.ISO8601': + case 'BASE64': + $this->xh[$the_parser]['vt'] = strtolower($name); + + if ($name == 'STRING') + { + $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; + } + elseif ($name=='DATETIME.ISO8601') + { + $this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime; + $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; + } + elseif ($name=='BASE64') + { + $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']); + } + elseif ($name=='BOOLEAN') + { + // Translated BOOLEAN values to TRUE AND FALSE + if ($this->xh[$the_parser]['ac'] == '1') + { + $this->xh[$the_parser]['value'] = TRUE; + } + else + { + $this->xh[$the_parser]['value'] = FALSE; + } + } + elseif ($name=='DOUBLE') + { + // we have a DOUBLE + // we must check that only 0123456789-. are characters here + if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac'])) + { + $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND'; + } + else + { + $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac']; + } + } + else + { + // we have an I4/INT + // we must check that only 0123456789- are characters here + if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac'])) + { + $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND'; + } + else + { + $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac']; + } + } + $this->xh[$the_parser]['ac'] = ''; + $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value + break; + case 'VALUE': + // This if() detects if no scalar was inside + if ($this->xh[$the_parser]['vt']=='value') + { + $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; + $this->xh[$the_parser]['vt'] = $this->xmlrpcString; + } + + // build the XML-RPC value out of the data received, and substitute it + $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']); + + if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY') + { + // Array + $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp; + } + else + { + // Struct + $this->xh[$the_parser]['value'] = $temp; + } + break; + case 'MEMBER': + $this->xh[$the_parser]['ac']=''; + + // If value add to array in the stack for the last element built + if ($this->xh[$the_parser]['value']) + { + $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value']; + } + break; + case 'DATA': + $this->xh[$the_parser]['ac']=''; + break; + case 'PARAM': + if ($this->xh[$the_parser]['value']) + { + $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value']; + } + break; + case 'METHODNAME': + $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']); + break; + case 'PARAMS': + case 'FAULT': + case 'METHODCALL': + case 'METHORESPONSE': + // We're all good kids with nuthin' to do + break; + default: + // End of an Invalid Element. Taken care of during the opening tag though + break; + } + } + + //------------------------------------- + // Parses Character Data + //------------------------------------- + + function character_data($the_parser, $data) + { + if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already + + // If a value has not been found + if ($this->xh[$the_parser]['lv'] != 3) + { + if ($this->xh[$the_parser]['lv'] == 1) + { + $this->xh[$the_parser]['lv'] = 2; // Found a value + } + + if( ! @isset($this->xh[$the_parser]['ac'])) + { + $this->xh[$the_parser]['ac'] = ''; + } + + $this->xh[$the_parser]['ac'] .= $data; + } + } + + + function addParam($par) { $this->params[]=$par; } + + function output_parameters($array=FALSE) + { + $CI =& get_instance(); + + if ($array !== FALSE && is_array($array)) + { + while (list($key) = each($array)) + { + if (is_array($array[$key])) + { + $array[$key] = $this->output_parameters($array[$key]); + } + else + { + $array[$key] = $CI->input->xss_clean($array[$key]); + } + } + + $parameters = $array; + } + else + { + $parameters = array(); + + for ($i = 0; $i < sizeof($this->params); $i++) + { + $a_param = $this->decode_message($this->params[$i]); + + if (is_array($a_param)) + { + $parameters[] = $this->output_parameters($a_param); + } + else + { + $parameters[] = $CI->input->xss_clean($a_param); + } + } + } + + return $parameters; + } + + + function decode_message($param) + { + $kind = $param->kindOf(); + + if($kind == 'scalar') + { + return $param->scalarval(); + } + elseif($kind == 'array') + { + reset($param->me); + list($a,$b) = each($param->me); + + $arr = array(); + + for($i = 0; $i < sizeof($b); $i++) + { + $arr[] = $this->decode_message($param->me['array'][$i]); + } + + return $arr; + } + elseif($kind == 'struct') + { + reset($param->me['struct']); + + $arr = array(); + + while(list($key,$value) = each($param->me['struct'])) + { + $arr[$key] = $this->decode_message($value); + } + + return $arr; + } + } + +} // End XML_RPC_Messages class + + + +/** + * XML-RPC Values class + * + * @category XML-RPC + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + */ +class XML_RPC_Values extends CI_Xmlrpc +{ + var $me = array(); + var $mytype = 0; + + function XML_RPC_Values($val=-1, $type='') + { + parent::CI_Xmlrpc(); + + if ($val != -1 OR $type != '') + { + $type = $type == '' ? 'string' : $type; + + if ($this->xmlrpcTypes[$type] == 1) + { + $this->addScalar($val,$type); + } + elseif ($this->xmlrpcTypes[$type] == 2) + { + $this->addArray($val); + } + elseif ($this->xmlrpcTypes[$type] == 3) + { + $this->addStruct($val); + } + } + } + + function addScalar($val, $type='string') + { + $typeof = $this->xmlrpcTypes[$type]; + + if ($this->mytype==1) + { + echo 'XML_RPC_Values: scalar can have only one value
'; + return 0; + } + + if ($typeof != 1) + { + echo 'XML_RPC_Values: not a scalar type (${typeof})
'; + return 0; + } + + if ($type == $this->xmlrpcBoolean) + { + if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false'))) + { + $val = 1; + } + else + { + $val=0; + } + } + + if ($this->mytype == 2) + { + // adding to an array here + $ar = $this->me['array']; + $ar[] = new XML_RPC_Values($val, $type); + $this->me['array'] = $ar; + } + else + { + // a scalar, so set the value and remember we're scalar + $this->me[$type] = $val; + $this->mytype = $typeof; + } + return 1; + } + + function addArray($vals) + { + if ($this->mytype != 0) + { + echo 'XML_RPC_Values: already initialized as a [' . $this->kindOf() . ']
'; + return 0; + } + + $this->mytype = $this->xmlrpcTypes['array']; + $this->me['array'] = $vals; + return 1; + } + + function addStruct($vals) + { + if ($this->mytype != 0) + { + echo 'XML_RPC_Values: already initialized as a [' . $this->kindOf() . ']
'; + return 0; + } + $this->mytype = $this->xmlrpcTypes['struct']; + $this->me['struct'] = $vals; + return 1; + } + + function kindOf() + { + switch($this->mytype) + { + case 3: + return 'struct'; + break; + case 2: + return 'array'; + break; + case 1: + return 'scalar'; + break; + default: + return 'undef'; + } + } + + function serializedata($typ, $val) + { + $rs = ''; + + switch($this->xmlrpcTypes[$typ]) + { + case 3: + // struct + $rs .= "\n"; + reset($val); + while(list($key2, $val2) = each($val)) + { + $rs .= "\n{$key2}\n"; + $rs .= $this->serializeval($val2); + $rs .= "\n"; + } + $rs .= ''; + break; + case 2: + // array + $rs .= "\n\n"; + for($i=0; $i < sizeof($val); $i++) + { + $rs .= $this->serializeval($val[$i]); + } + $rs.="\n\n"; + break; + case 1: + // others + switch ($typ) + { + case $this->xmlrpcBase64: + $rs .= "<{$typ}>" . base64_encode($val) . "\n"; + break; + case $this->xmlrpcBoolean: + $rs .= "<{$typ}>" . ($val ? '1' : '0') . "\n"; + break; + case $this->xmlrpcString: + $rs .= "<{$typ}>" . htmlspecialchars($val). "\n"; + break; + default: + $rs .= "<{$typ}>{$val}\n"; + break; + } + default: + break; + } + return $rs; + } + + function serialize_class() + { + return $this->serializeval($this); + } + + function serializeval($o) + { + $ar = $o->me; + reset($ar); + + list($typ, $val) = each($ar); + $rs = "\n".$this->serializedata($typ, $val)."\n"; + return $rs; + } + + function scalarval() + { + reset($this->me); + list($a,$b) = each($this->me); + return $b; + } + + + //------------------------------------- + // Encode time in ISO-8601 form. + //------------------------------------- + + // Useful for sending time in XML-RPC + + function iso8601_encode($time, $utc=0) + { + if ($utc == 1) + { + $t = strftime("%Y%m%dT%H:%M:%S", $time); + } + else + { + if (function_exists('gmstrftime')) + $t = gmstrftime("%Y%m%dT%H:%M:%S", $time); + else + $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z')); + } + return $t; + } + +} +// END XML_RPC_Values Class + +/* End of file Xmlrpc.php */ +/* Location: ./system/libraries/Xmlrpc.php */ \ No newline at end of file diff --git a/libraries/Xmlrpcs.php b/libraries/Xmlrpcs.php new file mode 100644 index 0000000..a210183 --- /dev/null +++ b/libraries/Xmlrpcs.php @@ -0,0 +1,536 @@ +set_system_methods(); + + if (isset($config['functions']) && is_array($config['functions'])) + { + $this->methods = array_merge($this->methods, $config['functions']); + } + + log_message('debug', "XML-RPC Server Class Initialized"); + } + + //------------------------------------- + // Initialize Prefs and Serve + //------------------------------------- + + function initialize($config=array()) + { + if (isset($config['functions']) && is_array($config['functions'])) + { + $this->methods = array_merge($this->methods, $config['functions']); + } + + if (isset($config['debug'])) + { + $this->debug = $config['debug']; + } + + if (isset($config['object']) && is_object($config['object'])) + { + $this->object = $config['object']; + } + } + + //------------------------------------- + // Setting of System Methods + //------------------------------------- + + function set_system_methods () + { + $this->methods = array( + 'system.listMethods' => array( + 'function' => 'this.listMethods', + 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)), + 'docstring' => 'Returns an array of available methods on this server'), + 'system.methodHelp' => array( + 'function' => 'this.methodHelp', + 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)), + 'docstring' => 'Returns a documentation string for the specified method'), + 'system.methodSignature' => array( + 'function' => 'this.methodSignature', + 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)), + 'docstring' => 'Returns an array describing the return type and required parameters of a method'), + 'system.multicall' => array( + 'function' => 'this.multicall', + 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)), + 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details') + ); + } + + + //------------------------------------- + // Main Server Function + //------------------------------------- + + function serve() + { + $r = $this->parseRequest(); + $payload = 'xmlrpc_defencoding.'"?'.'>'."\n"; + $payload .= $this->debug_msg; + $payload .= $r->prepare_response(); + + header("Content-Type: text/xml"); + header("Content-Length: ".strlen($payload)); + echo $payload; + } + + //------------------------------------- + // Add Method to Class + //------------------------------------- + + function add_to_map($methodname,$function,$sig,$doc) + { + $this->methods[$methodname] = array( + 'function' => $function, + 'signature' => $sig, + 'docstring' => $doc + ); + } + + + //------------------------------------- + // Parse Server Request + //------------------------------------- + + function parseRequest($data='') + { + global $HTTP_RAW_POST_DATA; + + //------------------------------------- + // Get Data + //------------------------------------- + + if ($data == '') + { + $data = $HTTP_RAW_POST_DATA; + } + + //------------------------------------- + // Set up XML Parser + //------------------------------------- + + $parser = xml_parser_create($this->xmlrpc_defencoding); + $parser_object = new XML_RPC_Message("filler"); + + $parser_object->xh[$parser] = array(); + $parser_object->xh[$parser]['isf'] = 0; + $parser_object->xh[$parser]['isf_reason'] = ''; + $parser_object->xh[$parser]['params'] = array(); + $parser_object->xh[$parser]['stack'] = array(); + $parser_object->xh[$parser]['valuestack'] = array(); + $parser_object->xh[$parser]['method'] = ''; + + xml_set_object($parser, $parser_object); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); + xml_set_element_handler($parser, 'open_tag', 'closing_tag'); + xml_set_character_data_handler($parser, 'character_data'); + //xml_set_default_handler($parser, 'default_handler'); + + + //------------------------------------- + // PARSE + PROCESS XML DATA + //------------------------------------- + + if ( ! xml_parse($parser, $data, 1)) + { + // return XML error as a faultCode + $r = new XML_RPC_Response(0, + $this->xmlrpcerrxml + xml_get_error_code($parser), + sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser)), + xml_get_current_line_number($parser))); + xml_parser_free($parser); + } + elseif($parser_object->xh[$parser]['isf']) + { + return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); + } + else + { + xml_parser_free($parser); + + $m = new XML_RPC_Message($parser_object->xh[$parser]['method']); + $plist=''; + + for($i=0; $i < sizeof($parser_object->xh[$parser]['params']); $i++) + { + if ($this->debug === TRUE) + { + $plist .= "$i - " . print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n"; + } + + $m->addParam($parser_object->xh[$parser]['params'][$i]); + } + + if ($this->debug === TRUE) + { + echo "
";
+				echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";
+				echo "
"; + } + + $r = $this->_execute($m); + } + + //------------------------------------- + // SET DEBUGGING MESSAGE + //------------------------------------- + + if ($this->debug === TRUE) + { + $this->debug_msg = "\n"; + } + + return $r; + } + + //------------------------------------- + // Executes the Method + //------------------------------------- + + function _execute($m) + { + $methName = $m->method_name; + + // Check to see if it is a system call + $system_call = (strncmp($methName, 'system', 5) == 0) ? TRUE : FALSE; + + //------------------------------------- + // Valid Method + //------------------------------------- + + if ( ! isset($this->methods[$methName]['function'])) + { + return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); + } + + //------------------------------------- + // Check for Method (and Object) + //------------------------------------- + + $method_parts = explode(".", $this->methods[$methName]['function']); + $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? TRUE : FALSE; + + if ($system_call === TRUE) + { + if ( ! is_callable(array($this,$method_parts['1']))) + { + return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); + } + } + else + { + if ($objectCall && ! is_callable(array($method_parts['0'],$method_parts['1']))) + { + return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); + } + elseif ( ! $objectCall && ! is_callable($this->methods[$methName]['function'])) + { + return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); + } + } + + //------------------------------------- + // Checking Methods Signature + //------------------------------------- + + if (isset($this->methods[$methName]['signature'])) + { + $sig = $this->methods[$methName]['signature']; + for($i=0; $iparams)+1) + { + for($n=0; $n < sizeof($m->params); $n++) + { + $p = $m->params[$n]; + $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf(); + + if ($pt != $current_sig[$n+1]) + { + $pno = $n+1; + $wanted = $current_sig[$n+1]; + + return new XML_RPC_Response(0, + $this->xmlrpcerr['incorrect_params'], + $this->xmlrpcstr['incorrect_params'] . + ": Wanted {$wanted}, got {$pt} at param {$pno})"); + } + } + } + } + } + + //------------------------------------- + // Calls the Function + //------------------------------------- + + if ($objectCall === TRUE) + { + if ($method_parts[0] == "this" && $system_call == TRUE) + { + return call_user_func(array($this, $method_parts[1]), $m); + } + else + { + if ($this->object === FALSE) + { + $CI =& get_instance(); + return $CI->$method_parts['1']($m); + } + else + { + return $this->object->$method_parts['1']($m); + //return call_user_func(array(&$method_parts['0'],$method_parts['1']), $m); + } + } + } + else + { + return call_user_func($this->methods[$methName]['function'], $m); + } + } + + + //------------------------------------- + // Server Function: List Methods + //------------------------------------- + + function listMethods($m) + { + $v = new XML_RPC_Values(); + $output = array(); + + foreach($this->methods as $key => $value) + { + $output[] = new XML_RPC_Values($key, 'string'); + } + + foreach($this->system_methods as $key => $value) + { + $output[]= new XML_RPC_Values($key, 'string'); + } + + $v->addArray($output); + return new XML_RPC_Response($v); + } + + //------------------------------------- + // Server Function: Return Signature for Method + //------------------------------------- + + function methodSignature($m) + { + $parameters = $m->output_parameters(); + $method_name = $parameters[0]; + + if (isset($this->methods[$method_name])) + { + if ($this->methods[$method_name]['signature']) + { + $sigs = array(); + $signature = $this->methods[$method_name]['signature']; + + for($i=0; $i < sizeof($signature); $i++) + { + $cursig = array(); + $inSig = $signature[$i]; + for($j=0; $jxmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); + } + return $r; + } + + //------------------------------------- + // Server Function: Doc String for Method + //------------------------------------- + + function methodHelp($m) + { + $parameters = $m->output_parameters(); + $method_name = $parameters[0]; + + if (isset($this->methods[$method_name])) + { + $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : ''; + + return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string')); + } + else + { + return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); + } + } + + //------------------------------------- + // Server Function: Multi-call + //------------------------------------- + + function multicall($m) + { + // Disabled + return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); + + $parameters = $m->output_parameters(); + $calls = $parameters[0]; + + $result = array(); + + foreach ($calls as $value) + { + //$attempt = $this->_execute(new XML_RPC_Message($value[0], $value[1])); + + $m = new XML_RPC_Message($value[0]); + $plist=''; + + for($i=0; $i < sizeof($value[1]); $i++) + { + $m->addParam(new XML_RPC_Values($value[1][$i], 'string')); + } + + $attempt = $this->_execute($m); + + if ($attempt->faultCode() != 0) + { + return $attempt; + } + + $result[] = new XML_RPC_Values(array($attempt->value()), 'array'); + } + + return new XML_RPC_Response(new XML_RPC_Values($result, 'array')); + } + + + //------------------------------------- + // Multi-call Function: Error Handling + //------------------------------------- + + function multicall_error($err) + { + $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString(); + $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode(); + + $struct['faultCode'] = new XML_RPC_Values($code, 'int'); + $struct['faultString'] = new XML_RPC_Values($str, 'string'); + + return new XML_RPC_Values($struct, 'struct'); + } + + + //------------------------------------- + // Multi-call Function: Processes method + //------------------------------------- + + function do_multicall($call) + { + if ($call->kindOf() != 'struct') + return $this->multicall_error('notstruct'); + elseif ( ! $methName = $call->me['struct']['methodName']) + return $this->multicall_error('nomethod'); + + list($scalar_type,$scalar_value)=each($methName->me); + $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type; + + if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string') + return $this->multicall_error('notstring'); + elseif ($scalar_value == 'system.multicall') + return $this->multicall_error('recursion'); + elseif ( ! $params = $call->me['struct']['params']) + return $this->multicall_error('noparams'); + elseif ($params->kindOf() != 'array') + return $this->multicall_error('notarray'); + + list($a,$b)=each($params->me); + $numParams = sizeof($b); + + $msg = new XML_RPC_Message($scalar_value); + for ($i = 0; $i < $numParams; $i++) + { + $msg->params[] = $params->me['array'][$i]; + } + + $result = $this->_execute($msg); + + if ($result->faultCode() != 0) + { + return $this->multicall_error($result); + } + + return new XML_RPC_Values(array($result->value()), 'array'); + } + +} +// END XML_RPC_Server class + + +/* End of file Xmlrpcs.php */ +/* Location: ./system/libraries/Xmlrpcs.php */ \ No newline at end of file diff --git a/libraries/Zip.php b/libraries/Zip.php new file mode 100644 index 0000000..c15255a --- /dev/null +++ b/libraries/Zip.php @@ -0,0 +1,359 @@ +_add_dir($dir); + } + } + + // -------------------------------------------------------------------- + + /** + * Add Directory + * + * @access private + * @param string the directory name + * @return void + */ + function _add_dir($dir) + { + $dir = str_replace("\\", "/", $dir); + + $this->zipdata .= + "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .pack('V', 0) // crc32 + .pack('V', 0) // compressed filesize + .pack('V', 0) // uncompressed filesize + .pack('v', strlen($dir)) // length of pathname + .pack('v', 0) // extra field length + .$dir + // below is "data descriptor" segment + .pack('V', 0) // crc32 + .pack('V', 0) // compressed filesize + .pack('V', 0); // uncompressed filesize + + $this->directory .= + "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .pack('V',0) // crc32 + .pack('V',0) // compressed filesize + .pack('V',0) // uncompressed filesize + .pack('v', strlen($dir)) // length of pathname + .pack('v', 0) // extra field length + .pack('v', 0) // file comment length + .pack('v', 0) // disk number start + .pack('v', 0) // internal file attributes + .pack('V', 16) // external file attributes - 'directory' bit set + .pack('V', $this->offset) // relative offset of local header + .$dir; + + $this->offset = strlen($this->zipdata); + $this->entries++; + } + + // -------------------------------------------------------------------- + + /** + * Add Data to Zip + * + * Lets you add files to the archive. If the path is included + * in the filename it will be placed within a directory. Make + * sure you use add_dir() first to create the folder. + * + * @access public + * @param mixed + * @param string + * @return void + */ + function add_data($filepath, $data = NULL) + { + if (is_array($filepath)) + { + foreach ($filepath as $path => $data) + { + $this->_add_data($path, $data); + } + } + else + { + $this->_add_data($filepath, $data); + } + } + + // -------------------------------------------------------------------- + + /** + * Add Data to Zip + * + * @access private + * @param string the file name/path + * @param string the data to be encoded + * @return void + */ + function _add_data($filepath, $data) + { + $filepath = str_replace("\\", "/", $filepath); + + $uncompressed_size = strlen($data); + $crc32 = crc32($data); + + $gzdata = gzcompress($data); + $gzdata = substr($gzdata, 2, -4); + $compressed_size = strlen($gzdata); + + $this->zipdata .= + "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00" + .pack('V', $crc32) + .pack('V', $compressed_size) + .pack('V', $uncompressed_size) + .pack('v', strlen($filepath)) // length of filename + .pack('v', 0) // extra field length + .$filepath + .$gzdata; // "file data" segment + + $this->directory .= + "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00" + .pack('V', $crc32) + .pack('V', $compressed_size) + .pack('V', $uncompressed_size) + .pack('v', strlen($filepath)) // length of filename + .pack('v', 0) // extra field length + .pack('v', 0) // file comment length + .pack('v', 0) // disk number start + .pack('v', 0) // internal file attributes + .pack('V', 32) // external file attributes - 'archive' bit set + .pack('V', $this->offset) // relative offset of local header + .$filepath; + + $this->offset = strlen($this->zipdata); + $this->entries++; + $this->file_num++; + } + + // -------------------------------------------------------------------- + + /** + * Read the contents of a file and add it to the zip + * + * @access public + * @return bool + */ + function read_file($path, $preserve_filepath = FALSE) + { + if ( ! file_exists($path)) + { + return FALSE; + } + + if (FALSE !== ($data = file_get_contents($path))) + { + $name = str_replace("\\", "/", $path); + + if ($preserve_filepath === FALSE) + { + $name = preg_replace("|.*/(.+)|", "\\1", $name); + } + + $this->add_data($name, $data); + return TRUE; + } + return FALSE; + } + + // ------------------------------------------------------------------------ + + /** + * Read a directory and add it to the zip. + * + * This function recursively reads a folder and everything it contains (including + * sub-folders) and creates a zip based on it. Whatever directory structure + * is in the original file path will be recreated in the zip file. + * + * @access public + * @param string path to source + * @return bool + */ + function read_dir($path) + { + if ($fp = @opendir($path)) + { + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($path.$file) && substr($file, 0, 1) != '.') + { + $this->read_dir($path.$file."/"); + } + elseif (substr($file, 0, 1) != ".") + { + if (FALSE !== ($data = file_get_contents($path.$file))) + { + $this->add_data(str_replace("\\", "/", $path).$file, $data); + } + } + } + return TRUE; + } + } + + // -------------------------------------------------------------------- + + /** + * Get the Zip file + * + * @access public + * @return binary string + */ + function get_zip() + { + // Is there any data to return? + if ($this->entries == 0) + { + return FALSE; + } + + $zip_data = $this->zipdata; + $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"; + $zip_data .= pack('v', $this->entries); // total # of entries "on this disk" + $zip_data .= pack('v', $this->entries); // total # of entries overall + $zip_data .= pack('V', strlen($this->directory)); // size of central dir + $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir + $zip_data .= "\x00\x00"; // .zip file comment length + + return $zip_data; + } + + // -------------------------------------------------------------------- + + /** + * Write File to the specified directory + * + * Lets you write a file + * + * @access public + * @param string the file name + * @return bool + */ + function archive($filepath) + { + if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE))) + { + return FALSE; + } + + flock($fp, LOCK_EX); + fwrite($fp, $this->get_zip()); + flock($fp, LOCK_UN); + fclose($fp); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Download + * + * @access public + * @param string the file name + * @param string the data to be encoded + * @return bool + */ + function download($filename = 'backup.zip') + { + if ( ! preg_match("|.+?\.zip$|", $filename)) + { + $filename .= '.zip'; + } + + $zip_content =& $this->get_zip(); + + $CI =& get_instance(); + $CI->load->helper('download'); + + force_download($filename, $zip_content); + } + + // -------------------------------------------------------------------- + + /** + * Initialize Data + * + * Lets you clear current zip data. Useful if you need to create + * multiple zips with different data. + * + * @access public + * @return void + */ + function clear_data() + { + $this->zipdata = ''; + $this->directory = ''; + $this->entries = 0; + $this->file_num = 0; + $this->offset = 0; + } + +} + +/* End of file Zip.php */ +/* Location: ./system/libraries/Zip.php */ \ No newline at end of file diff --git a/libraries/index.html b/libraries/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/libraries/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/logs/index.html b/logs/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/logs/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/plugins/captcha_pi.php b/plugins/captcha_pi.php new file mode 100644 index 0000000..0484a6f --- /dev/null +++ b/plugins/captcha_pi.php @@ -0,0 +1,356 @@ +load->plugin('captcha'); + +Once loaded you can generate a captcha like this: + + $vals = array( + 'word' => 'Random word', + 'img_path' => './captcha/', + 'img_url' => 'http://example.com/captcha/', + 'font_path' => './system/fonts/texb.ttf', + 'img_width' => '150', + 'img_height' => 30, + 'expiration' => 7200 + ); + + $cap = create_captcha($vals); + echo $cap['image']; + + +NOTES: + + The captcha function requires the GD image library. + + Only the img_path and img_url are required. + + If a "word" is not supplied, the function will generate a random + ASCII string. You might put together your own word library that + you can draw randomly from. + + If you do not specify a path to a TRUE TYPE font, the native ugly GD + font will be used. + + The "captcha" folder must be writable (666, or 777) + + The "expiration" (in seconds) signifies how long an image will + remain in the captcha folder before it will be deleted. The default + is two hours. + +RETURNED DATA + +The create_captcha() function returns an associative array with this data: + + [array] + ( + 'image' => IMAGE TAG + 'time' => TIMESTAMP (in microtime) + 'word' => CAPTCHA WORD + ) + +The "image" is the actual image tag: + + +The "time" is the micro timestamp used as the image name without the file +extension. It will be a number like this: 1139612155.3422 + +The "word" is the word that appears in the captcha image, which if not +supplied to the function, will be a random string. + + +ADDING A DATABASE + +In order for the captcha function to prevent someone from posting, you will need +to add the information returned from create_captcha() function to your database. +Then, when the data from the form is submitted by the user you will need to verify +that the data exists in the database and has not expired. + +Here is a table prototype: + + CREATE TABLE captcha ( + captcha_id bigint(13) unsigned NOT NULL auto_increment, + captcha_time int(10) unsigned NOT NULL, + ip_address varchar(16) default '0' NOT NULL, + word varchar(20) NOT NULL, + PRIMARY KEY `captcha_id` (`captcha_id`), + KEY `word` (`word`) + ) + + +Here is an example of usage with a DB. + +On the page where the captcha will be shown you'll have something like this: + + $this->load->plugin('captcha'); + $vals = array( + 'img_path' => './captcha/', + 'img_url' => 'http://example.com/captcha/' + ); + + $cap = create_captcha($vals); + + $data = array( + 'captcha_id' => '', + 'captcha_time' => $cap['time'], + 'ip_address' => $this->input->ip_address(), + 'word' => $cap['word'] + ); + + $query = $this->db->insert_string('captcha', $data); + $this->db->query($query); + + echo 'Submit the word you see below:'; + echo $cap['image']; + echo ''; + + +Then, on the page that accepts the submission you'll have something like this: + + // First, delete old captchas + $expiration = time()-7200; // Two hour limit + $DB->query("DELETE FROM captcha WHERE captcha_time < ".$expiration); + + // Then see if a captcha exists: + $sql = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND date > ?"; + $binds = array($_POST['captcha'], $this->input->ip_address(), $expiration); + $query = $this->db->query($sql, $binds); + $row = $query->row(); + + if ($row->count == 0) + { + echo "You must submit the word that appears in the image"; + } + +*/ + + + +/** +|========================================================== +| Create Captcha +|========================================================== +| +*/ +function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = '') +{ + $defaults = array('word' => '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200); + + foreach ($defaults as $key => $val) + { + if ( ! is_array($data)) + { + if ( ! isset($$key) OR $$key == '') + { + $$key = $val; + } + } + else + { + $$key = ( ! isset($data[$key])) ? $val : $data[$key]; + } + } + + if ($img_path == '' OR $img_url == '') + { + return FALSE; + } + + if ( ! @is_dir($img_path)) + { + return FALSE; + } + + if ( ! is_really_writable($img_path)) + { + return FALSE; + } + + if ( ! extension_loaded('gd')) + { + return FALSE; + } + + // ----------------------------------- + // Remove old images + // ----------------------------------- + + list($usec, $sec) = explode(" ", microtime()); + $now = ((float)$usec + (float)$sec); + + $current_dir = @opendir($img_path); + + while($filename = @readdir($current_dir)) + { + if ($filename != "." and $filename != ".." and $filename != "index.html") + { + $name = str_replace(".jpg", "", $filename); + + if (($name + $expiration) < $now) + { + @unlink($img_path.$filename); + } + } + } + + @closedir($current_dir); + + // ----------------------------------- + // Do we have a "word" yet? + // ----------------------------------- + + if ($word == '') + { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $str = ''; + for ($i = 0; $i < 8; $i++) + { + $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1); + } + + $word = $str; + } + + // ----------------------------------- + // Determine angle and position + // ----------------------------------- + + $length = strlen($word); + $angle = ($length >= 6) ? rand(-($length-6), ($length-6)) : 0; + $x_axis = rand(6, (360/$length)-16); + $y_axis = ($angle >= 0 ) ? rand($img_height, $img_width) : rand(6, $img_height); + + // ----------------------------------- + // Create image + // ----------------------------------- + + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + if (function_exists('imagecreatetruecolor')) + { + $im = imagecreatetruecolor($img_width, $img_height); + } + else + { + $im = imagecreate($img_width, $img_height); + } + + // ----------------------------------- + // Assign colors + // ----------------------------------- + + $bg_color = imagecolorallocate ($im, 255, 255, 255); + $border_color = imagecolorallocate ($im, 153, 102, 102); + $text_color = imagecolorallocate ($im, 204, 153, 153); + $grid_color = imagecolorallocate($im, 255, 182, 182); + $shadow_color = imagecolorallocate($im, 255, 240, 240); + + // ----------------------------------- + // Create the rectangle + // ----------------------------------- + + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color); + + // ----------------------------------- + // Create the spiral pattern + // ----------------------------------- + + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + + for ($i = 0; $i < ($circles * $points) - 1; $i++) + { + $theta = $theta + $thetac; + $rad = $radius * ($i / $points ); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta = $theta + $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta )) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $grid_color); + $theta = $theta - $thetac; + } + + // ----------------------------------- + // Write the text + // ----------------------------------- + + $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE; + + if ($use_font == FALSE) + { + $font_size = 5; + $x = rand(0, $img_width/($length/3)); + $y = 0; + } + else + { + $font_size = 16; + $x = rand(0, $img_width/($length/1.5)); + $y = $font_size+2; + } + + for ($i = 0; $i < strlen($word); $i++) + { + if ($use_font == FALSE) + { + $y = rand(0 , $img_height/2); + imagestring($im, $font_size, $x, $y, substr($word, $i, 1), $text_color); + $x += ($font_size*2); + } + else + { + $y = rand($img_height/2, $img_height-3); + imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1)); + $x += $font_size; + } + } + + + // ----------------------------------- + // Create the border + // ----------------------------------- + + imagerectangle($im, 0, 0, $img_width-1, $img_height-1, $border_color); + + // ----------------------------------- + // Generate the image + // ----------------------------------- + + $img_name = $now.'.jpg'; + + ImageJPEG($im, $img_path.$img_name); + + $img = "\""; + + ImageDestroy($im); + + return array('word' => $word, 'time' => $now, 'image' => $img); +} + + +/* End of file captcha_pi.php */ +/* Location: ./system/plugins/captcha_pi.php */ \ No newline at end of file diff --git a/plugins/index.html b/plugins/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/plugins/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/plugins/js_calendar_pi.php b/plugins/js_calendar_pi.php new file mode 100644 index 0000000..e165c54 --- /dev/null +++ b/plugins/js_calendar_pi.php @@ -0,0 +1,629 @@ +load->plugin('js_calendar'); + +Once loaded you'll add the calendar script to the of your page like this: + + + +The above function will be passed the name of your form. + +Then to show the actual calendar you'll do this: + + +
+ +

Today

+
+ + +Note: The first parameter is the name of the field containing your date, the second parameter contains the "now" time, +and the third tells the calendar whether to highlight the current day or not. + +Lastly, you'll need some CSS for your calendar: + +.calendar { + border: 1px #6975A3 solid; + background-color: transparent; +} +.calheading { + background-color: #7C8BC0; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + font-weight: bold; + text-align: center; +} +.calnavleft { + background-color: #7C8BC0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + font-weight: bold; + color: #fff; + padding: 4px; + cursor: pointer; +} +.calnavright { + background-color: #7C8BC0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + font-weight: bold; + color: #fff; + text-align: right; + padding: 4px; + cursor: pointer; +} +.caldayheading { + background-color: #000; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + text-align: center; + padding: 6px 2px 6px 2px; +} +.caldaycells{ + color: #000; + background-color: #D1D7E6; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + text-align: center; + padding: 4px; + border: 1px #E0E5F1 solid; + cursor: pointer; +} +.caldaycellhover{ + color: #fff; + background-color: #B3BCD4; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + text-align: center; + padding: 4px; + border: 1px #B3BCD4 solid; + cursor: pointer; +} +.caldayselected{ + background-color: #737FAC; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + font-weight: bold; + text-align: center; + border: 1px #566188 solid; + padding: 3px; + cursor: pointer; +} +.calblanktop { + background-color: #fff; + padding: 4px; +} +.calblankbot { + background-color: #fff; + padding: 4px; +} + + +*/ + +function js_calendar_script($form_name = 'entryform') +{ +$CI =& get_instance(); +$CI->load->language('calendar'); +ob_start(); +?> + + + var '.$field_id.' = new calendar("'.$field_id.'", '.$time.', '.(($highlight == TRUE) ? 'true' : 'false').'); + document.write('.$field_id.'.write()); + '; +} + + +/* End of file js_calendar_pi.php */ +/* Location: ./system/plugins/js_calendar_pi.php */ \ No newline at end of file diff --git a/rounded.css b/rounded.css new file mode 100644 index 0000000..4825add --- /dev/null +++ b/rounded.css @@ -0,0 +1,59 @@ + +div.rounded { + margin: 0pt auto; + width: 200px; + background-color: rgb(153, 170, 187); + font-family: Georgia; + font-variant: small-caps; + font-weight: bold; + font-size: 2em; + line-height: 4em; + font-size-adjust: none; + font-stretch: normal; + color: rgb(255, 255, 255); + text-align: center +} + +div.plroundedconfirm { + margin: 0pt auto; + width: 700px; + background-color: #ddeeee; + font-family: Georgia; + /*font-variant: small-caps;*/ + /*font-weight: bold;*/ + /*font-size: 2em;*/ + /*line-height: 2em;*/ + font-size-adjust: none; + font-stretch: normal; + color: rgb(0, 0, 0); + text-align: left; + } + +div.plroundedupdate { + margin: 0pt auto; + width: 700px; + background-color: #ddeedd; + font-family: Georgia; + /*font-variant: small-caps;*/ + /*font-weight: bold;*/ + /*font-size: 2em;*/ + /*line-height: 2em;*/ + font-size-adjust: none; + font-stretch: normal; + color: rgb(0, 0, 0); + text-align: left; + } + +div.plroundedwhite { + margin: 5px auto; + width: 670px; + background-color: #ffffff; + /*font-variant: small-caps;*/ + /*font-weight: bold;*/ + /*font-size: 2em;*/ + /*line-height: 2em;*/ + font-size-adjust: none; + font-stretch: normal; + color: rgb(0, 0, 0); + text-align: left; + } diff --git a/rounded.js b/rounded.js new file mode 100644 index 0000000..546502a --- /dev/null +++ b/rounded.js @@ -0,0 +1,187 @@ +// Contributors +// Ilkka Huotari at http://www.editsite.net +// Mathieu 'p01' HENRI at http://www.p01.org/ +// http://seky.nahory.net/2005/04/rounded-corners/ +// Steven Wittens at http://www.acko.net/anti-aliased-nifty-corners +// Original Nifty Corners by Alessandro Fulciniti at http://pro.html.it/esempio/nifty/ +function NiftyCheck() { + if(!document.getElementById || !document.createElement) { + return false; + } + var b = navigator.userAgent.toLowerCase(); + if (b.indexOf("msie 5") > 0 && b.indexOf("opera") == -1) { + return false; + } + return true; +} + +function Rounded(className, sizex, sizey, sizex_b, sizey_b) { + var bk; + if (!NiftyCheck()) return; + if (typeof(sizex_b) == 'undefined') + sizex_b = sizex; + if (typeof(sizey_b) == 'undefined') + sizey_b = sizey; + var v = getElements(className); + var l = v.length; + for (var i = 0; i < l; i++) { + color = get_current_style(v[i],"background-color","transparent"); + bk = get_current_style(v[i].parentNode,"background-color","transparent"); + AddRounded(v[i], bk, color, sizex, sizey, true); + AddRounded(v[i], bk, color, sizex_b, sizey_b, false); + } +} + +Math.sqr = function (x) { + return x*x; +}; + +function Blend(a, b, alpha) { + + var ca = Array( + parseInt('0x' + a.substring(1, 3)), + parseInt('0x' + a.substring(3, 5)), + parseInt('0x' + a.substring(5, 7)) + ); + var cb = Array( + parseInt('0x' + b.substring(1, 3)), + parseInt('0x' + b.substring(3, 5)), + parseInt('0x' + b.substring(5, 7)) + ); + return '#' + ('0'+Math.round(ca[0] + (cb[0] - ca[0])*alpha).toString(16)).slice(-2).toString(16) + + ('0'+Math.round(ca[1] + (cb[1] - ca[1])*alpha).toString(16)).slice(-2).toString(16) + + ('0'+Math.round(ca[2] + (cb[2] - ca[2])*alpha).toString(16)).slice(-2).toString(16); + + return '#' + ('0'+Math.round(ca[0] + (cb[0] - ca[0])*alpha).toString(16)).slice(-2).toString(16) + + ('0'+Math.round(ca[1] + (cb[1] - ca[1])*alpha).toString(16)).slice(-2).toString(16) + + ('0'+Math.round(ca[2] + (cb[2] - ca[2])*alpha).toString(16)).slice(-2).toString(16); +} + +function AddRounded(el, bk, color, sizex, sizey, top) { + if (!sizex && !sizey) + return; + var i, j; + var d = document.createElement("div"); + d.style.backgroundColor = bk; + var lastarc = 0; + for (i = 1; i <= sizey; i++) { + var coverage, arc2, arc3; + // Find intersection of arc with bottom of pixel row + arc = Math.sqrt(1.0 - Math.sqr(1.0 - i / sizey)) * sizex; + // Calculate how many pixels are bg, fg and blended. + var n_bg = sizex - Math.ceil(arc); + var n_fg = Math.floor(lastarc); + var n_aa = sizex - n_bg - n_fg; + // Create pixel row wrapper + var x = document.createElement("div"); + var y = d; + x.style.margin = "0px " + n_bg + "px"; + x.style.height='1px'; + x.style.overflow='hidden'; + // Make a wrapper per anti-aliased pixel (at least one) + for (j = 1; j <= n_aa; j++) { + // Calculate coverage per pixel + // (approximates circle by a line within the pixel) + if (j == 1) { + if (j == n_aa) { + // Single pixel + coverage = ((arc + lastarc) * .5) - n_fg; + } + else { + // First in a run + arc2 = Math.sqrt(1.0 - Math.sqr((sizex - n_bg - j + 1) / sizex)) * sizey; + coverage = (arc2 - (sizey - i)) * (arc - n_fg - n_aa + 1) * .5; + // Coverage is incorrect. Why? + coverage = 0; + } + } + else if (j == n_aa) { + // Last in a run + arc2 = Math.sqrt(1.0 - Math.sqr((sizex - n_bg - j + 1) / sizex)) * sizey; + coverage = 1.0 - (1.0 - (arc2 - (sizey - i))) * (1.0 - (lastarc - n_fg)) * .5; + } + else { + // Middle of a run + arc3 = Math.sqrt(1.0 - Math.sqr((sizex - n_bg - j) / sizex)) * sizey; + arc2 = Math.sqrt(1.0 - Math.sqr((sizex - n_bg - j + 1) / sizex)) * sizey; + coverage = ((arc2 + arc3) * .5) - (sizey - i); + } + + x.style.backgroundColor = Blend(bk, color, coverage); + if (top) + y.appendChild(x); + else + y.insertBefore(x, y.firstChild); + y = x; + var x = document.createElement("div"); + x.style.height='1px'; + x.style.overflow='hidden'; + x.style.margin = "0px 1px"; + } + x.style.backgroundColor = color; + if (top) + y.appendChild(x); + else + y.insertBefore(x, y.firstChild); + lastarc = arc; + } + if (top) + el.insertBefore(d, el.firstChild); + else + el.appendChild(d); +} + +function getElements(className) { + var elements = []; + var el = document.getElementsByTagName('DIV'); + var regexp=new RegExp("\\b"+className+"\\b"); + for (var i = 0; i < el.length; i++) + { + if (regexp.test(el[i].className)) + elements.push(el[i]); + } + return elements; +} + +function get_current_style(element,property,not_accepted) +{ + var ee,i,val,apr; + try + { + var cs=document.defaultView.getComputedStyle(element,''); + val=cs.getPropertyValue(property); + } + catch(ee) + { + if(element.currentStyle) + { + apr=property.split("-"); + for(i=1;i -1 || val==not_accepted) && element.parentNode) + { + if(element.parentNode != document) + val=get_current_style(element.parentNode,property,not_accepted); + else + val = '#FFFFFF'; + } + if (val.indexOf("rgb") > -1 && val.indexOf("rgba") == -1) + val = rgb2hex(val); + if (val.length == 4) + val = '#'+val.substring(1,1)+val.substring(1,1)+val.substring(2,1)+val.substring(2,1)+val.substring(3,1)+val.substring(3,1); + return val; +} + +function rgb2hex(value) +{ + var x = 255; + var hex = ''; + var i; + var regexp=/([0-9]+)[, ]+([0-9]+)[, ]+([0-9]+)/; + var array=regexp.exec(value); + for(i=1;i<4;i++) hex += ('0'+parseInt(array[i]).toString(16)).slice(-2); + return '#'+hex; +} diff --git a/scaffolding/Scaffolding.php b/scaffolding/Scaffolding.php new file mode 100644 index 0000000..28172ca --- /dev/null +++ b/scaffolding/Scaffolding.php @@ -0,0 +1,291 @@ +CI =& get_instance(); + + $this->CI->load->database("", FALSE, TRUE); + $this->CI->load->library('pagination'); + + // Turn off caching + $this->CI->db->cache_off(); + + /** + * Set the current table name + * This is done when initializing scaffolding: + * $this->load->scaffolding('table_name') + * + */ + $this->current_table = $db_table; + + /** + * Set the path to the "view" files + * We'll manually override the "view" path so that + * the load->view function knows where to look. + */ + + $this->CI->load->_ci_view_path = BASEPATH.'scaffolding/views/'; + + // Set the base URL + $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'both'); + $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); + + // Set a few globals + $data = array( + 'image_url' => $this->CI->config->system_url().'scaffolding/images/', + 'base_uri' => $this->base_uri, + 'base_url' => $this->base_url, + 'title' => $this->current_table + ); + + $this->CI->load->vars($data); + + // Load the language file and create variables + $this->lang = $this->CI->load->scaffold_language('scaffolding', '', TRUE); + $this->CI->load->vars($this->lang); + + // Load the helper files we plan to use + $this->CI->load->helper(array('url', 'form')); + + + log_message('debug', 'Scaffolding Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * "Add" Page + * + * Shows a form representing the currently selected DB + * so that data can be inserted + * + * @access public + * @return string the HTML "add" page + */ + function add() + { + $data = array( + 'title' => ( ! isset($this->lang['scaff_add'])) ? 'Add Data' : $this->lang['scaff_add'], + 'fields' => $this->CI->db->field_data($this->current_table), + 'action' => $this->base_uri.'/insert' + ); + + $this->CI->load->view('add', $data); + } + + // -------------------------------------------------------------------- + + /** + * Insert the data + * + * @access public + * @return void redirects to the view page + */ + function insert() + { + if ($this->CI->db->insert($this->current_table, $_POST) === FALSE) + { + $this->add(); + } + else + { + redirect($this->base_uri.'/view/'); + } + } + + // -------------------------------------------------------------------- + + /** + * "View" Page + * + * Shows a table containing the data in the currently + * selected DB + * + * @access public + * @return string the HTML "view" page + */ + function view() + { + // Fetch the total number of DB rows + $total_rows = $this->CI->db->count_all($this->current_table); + + if ($total_rows < 1) + { + return $this->CI->load->view('no_data'); + } + + // Set the query limit/offset + $per_page = 20; + $offset = $this->CI->uri->segment(4, 0); + + // Run the query + $query = $this->CI->db->get($this->current_table, $per_page, $offset); + + // Now let's get the field names + $fields = $this->CI->db->list_fields($this->current_table); + + // We assume that the column in the first position is the primary field. + $primary = current($fields); + + // Pagination! + $this->CI->pagination->initialize( + array( + 'base_url' => $this->base_url.'/view', + 'total_rows' => $total_rows, + 'per_page' => $per_page, + 'uri_segment' => 4, + 'full_tag_open' => '

', + 'full_tag_close' => '

' + ) + ); + + $data = array( + 'title' => ( ! isset($this->lang['scaff_view'])) ? 'View Data' : $this->lang['scaff_view'], + 'query' => $query, + 'fields' => $fields, + 'primary' => $primary, + 'paginate' => $this->CI->pagination->create_links() + ); + + $this->CI->load->view('view', $data); + } + + // -------------------------------------------------------------------- + + /** + * "Edit" Page + * + * Shows a form representing the currently selected DB + * so that data can be edited + * + * @access public + * @return string the HTML "edit" page + */ + function edit() + { + if (FALSE === ($id = $this->CI->uri->segment(4))) + { + return $this->view(); + } + + // Fetch the primary field name + $primary = $this->CI->db->primary($this->current_table); + + // Run the query + $query = $this->CI->db->get_where($this->current_table, array($primary => $id)); + + $data = array( + 'title' => ( ! isset($this->lang['scaff_edit'])) ? 'Edit Data' : $this->lang['scaff_edit'], + 'fields' => $query->field_data(), + 'query' => $query->row(), + 'action' => $this->base_uri.'/update/'.$this->CI->uri->segment(4) + ); + + $this->CI->load->view('edit', $data); + } + + // -------------------------------------------------------------------- + + /** + * Update + * + * @access public + * @return void redirects to the view page + */ + function update() + { + // Fetch the primary key + $primary = $this->CI->db->primary($this->current_table); + + // Now do the query + $this->CI->db->update($this->current_table, $_POST, array($primary => $this->CI->uri->segment(4))); + + redirect($this->base_uri.'/view/'); + } + + // -------------------------------------------------------------------- + + /** + * Delete Confirmation + * + * @access public + * @return string the HTML "delete confirm" page + */ + function delete() + { + if ( ! isset($this->lang['scaff_del_confirm'])) + { + $message = 'Are you sure you want to delete the following row: '.$this->CI->uri->segment(4); + } + else + { + $message = $this->lang['scaff_del_confirm'].' '.$this->CI->uri->segment(4); + } + + $data = array( + 'title' => ( ! isset($this->lang['scaff_delete'])) ? 'Delete Data' : $this->lang['scaff_delete'], + 'message' => $message, + 'no' => anchor(array($this->base_uri, 'view'), ( ! isset($this->lang['scaff_no'])) ? 'No' : $this->lang['scaff_no']), + 'yes' => anchor(array($this->base_uri, 'do_delete', $this->CI->uri->segment(4)), ( ! isset($this->lang['scaff_yes'])) ? 'Yes' : $this->lang['scaff_yes']) + ); + + $this->CI->load->view('delete', $data); + } + + // -------------------------------------------------------------------- + + /** + * Delete + * + * @access public + * @return void redirects to the view page + */ + function do_delete() + { + // Fetch the primary key + $primary = $this->CI->db->primary($this->current_table); + + // Now do the query + $this->CI->db->where($primary, $this->CI->uri->segment(4)); + $this->CI->db->delete($this->current_table); + + header("Refresh:0;url=".site_url(array($this->base_uri, 'view'))); + exit; + } + +} + +/* End of file Scaffolding.php */ +/* Location: ./system/scaffolding/Scaffolding.php */ \ No newline at end of file diff --git a/scaffolding/images/background.jpg b/scaffolding/images/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d5bdcea1d5b12024146ac223011123defe25635 GIT binary patch literal 410 zcmex=Zx{q!N|UoI85kK@7(jvy|KBn2Ii}<%r81-( zU|?Wi{C}IFg@KWHz=QzIZ~)cL#0ZjRWI~c568t~FAjrW`%)rIWD9FGh$jB_n`2Pq) zA_F4>Ga~~sU}9zGVnh{UU|Ji|62?^ z%!~|7g3N*p_6!R@1#if+f3?o@oddtBHQVEVtDekz!gy;2vat-jdPjF}_dR#;-O&Ih kIE!tiQ=*U#D`$(x2K{A59S + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/scaffolding/images/logo.jpg b/scaffolding/images/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6cc9a76db0b1174c74379f7f20df290e4ec7f86 GIT binary patch literal 4518 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 + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/scaffolding/views/add.php b/scaffolding/views/add.php new file mode 100644 index 0000000..cbb12f6 --- /dev/null +++ b/scaffolding/views/add.php @@ -0,0 +1,32 @@ +load->view('header'); ?> + +

+ + + + + + + +primary_key == 1) continue; ?> + + + + + type == 'blob'): ?> + + + + + + + +
name; echo ' '.$field->default; ?>
+ + + + + +load->view('footer'); +/* End of file add.php */ +/* Location: ./system/scaffolding/views/add.php */ diff --git a/scaffolding/views/delete.php b/scaffolding/views/delete.php new file mode 100644 index 0000000..d195421 --- /dev/null +++ b/scaffolding/views/delete.php @@ -0,0 +1,9 @@ +load->view('header'); ?> + +

+ +

  |   + +load->view('footer'); +/* End of file delete.php */ +/* Location: ./system/scaffolding/views/delete.php */ diff --git a/scaffolding/views/edit.php b/scaffolding/views/edit.php new file mode 100644 index 0000000..fe553e5 --- /dev/null +++ b/scaffolding/views/edit.php @@ -0,0 +1,33 @@ +load->view('header'); ?> + + +

+ + + + + + + +primary_key == 1) continue; ?> + + + + + type == 'blob'): ?> + + + + + + + +
name; ?>
+ + + + + +load->view('footer'); +/* End of file edit.php */ +/* Location: ./system/scaffolding/views/edit.php */ \ No newline at end of file diff --git a/scaffolding/views/footer.php b/scaffolding/views/footer.php new file mode 100644 index 0000000..a287664 --- /dev/null +++ b/scaffolding/views/footer.php @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/scaffolding/views/header.php b/scaffolding/views/header.php new file mode 100644 index 0000000..50f234a --- /dev/null +++ b/scaffolding/views/header.php @@ -0,0 +1,29 @@ + + + + +<?php echo $title; ?> + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/scaffolding/views/index.html b/scaffolding/views/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/scaffolding/views/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/scaffolding/views/no_data.php b/scaffolding/views/no_data.php new file mode 100644 index 0000000..bc81e74 --- /dev/null +++ b/scaffolding/views/no_data.php @@ -0,0 +1,8 @@ +load->view('header'); ?> + +

+

+ +load->view('footer'); +/* End of file no_data.php */ +/* Location: ./system/scaffolding/views/no_data.php */ \ No newline at end of file diff --git a/scaffolding/views/stylesheet.css b/scaffolding/views/stylesheet.css new file mode 100644 index 0000000..ba6ee0a --- /dev/null +++ b/scaffolding/views/stylesheet.css @@ -0,0 +1,143 @@ +body { + margin: 0; + padding: 0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + color: #4F5155; + background: #fff url(background.jpg) repeat-x left top; +} + +a { + color: #8B0D00; + background-color: transparent; + text-decoration: none; + font-weight: bold; +} + +a:visited { + color: #8B0D00; + background-color: transparent; + text-decoration: none; +} + +a:hover { + color: #000; + text-decoration: none; + background-color: transparent; +} + + +#header { + margin: 0; + padding: 0; +} + +#header_left { + background-color: transparent; + float: left; + padding: 21px 0 0 32px; + margin: 0 +} + +#header_right { + background-color: transparent; + float: right; + text-align: right; + padding: 35px 50px 20px 0; + margin: 0 +} + +#footer { + margin: 20px 0 15px 0; + padding: 0; +} + +#footer p { + font-size: 10px; + color: #999; + text-align: center; +} + +#outer { + margin: 30px 40px 0 40px; +} + +img { + padding:0; + border: 0; + margin: 0; +} + +.nopad { + padding:0; + border: 0; + margin: 0; +} + +table { + background-color: #efefef; +} + +th { + background-color: #eee; + font-weight: bold; + padding: 6px; + text-align: left; +} + +td { + background-color: #fff; + padding: 6px; +} + + +form { + margin: 0; + padding: 0; +} + +.input { + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + width: 600px; + color: #333; + border: 1px solid #B3B4BD; + font-size: 11px; + height: 2em; + padding: 0; + margin: 0; +} + +.textarea { + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 12px; + width: 600px; + color: #333; + border: 1px solid #B3B4BD; + padding: 0; + margin: 0; +} + +.select { + background-color: #fff; + font-size: 11px; + font-weight: normal; + color: #333; + padding: 0; + margin: 0 0 3px 0; +} + +.checkbox { + background-color: transparent; + padding: 0; + border: 0; +} + +.submit { + background-color: #8B0D00; + color: #FFF; + font-weight: normal; + border: 1px solid #000; + margin: 6px 0 0 0; + padding: 1px 5px 1px 5px; +} diff --git a/scaffolding/views/view.php b/scaffolding/views/view.php new file mode 100644 index 0000000..a81241d --- /dev/null +++ b/scaffolding/views/view.php @@ -0,0 +1,27 @@ +load->view('header'); ?> + + + + + + + + + + +result() as $row): ?> + + + + + + + + +
EditDelete
 $primary), $scaff_edit); ?> $primary), $scaff_delete); ?>$field);?>
+ + + +load->view('footer'); +/* End of file view.php */ +/* Location: ./system/scaffolding/views/view.php */ \ No newline at end of file diff --git a/www-register-wizard.spec b/www-register-wizard.spec new file mode 100755 index 0000000..ceee8f9 --- /dev/null +++ b/www-register-wizard.spec @@ -0,0 +1,64 @@ +# +# $Id: PLCWWW.spec 12168 2009-02-22 23:26:24Z thierry $ +# +%define url $URL: https://svn.planet-lab.org/svn/www-register-wizard/trunk/www-register-wizard.spec $ + +%define name www-register-wizard +%define version 4.3 +%define taglevel 0 + +%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}} + +Summary: Registration Wizard for Nodes and PCUs +Name: %{name} +Version: %{version} +Release: %{release} +License: PlanetLab +Group: System Environment/Daemons +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildArch: noarch + +Vendor: PlanetLab +Packager: PlanetLab Central +Distribution: PlanetLab %{plrelease} +URL: %(echo %{url} | cut -d ' ' -f 2) + +# We use set everywhere +#Requires: httpd >= 2.0 +Requires: PLCWWW >= 4.2 +Requires: PLCAPI >= 4.2 + +%description + +The www-register-wizard provides a web interface for MyPLC that integrates and +serializes the steps required to register, configure and verify that a node is +running correctly. This is an improvement upon and replacement for the existing 'Add Node' and +'Add PCU' forms. + +%prep +%setup -q + +%build +echo "There is no build stage for this component." +echo "All files just need to be installed as is from the codebase." + +%install +rm -rf $RPM_BUILD_ROOT + +echo "* www-register-wizard: Installing www-register-wizard pages" + +mkdir -p $RPM_BUILD_ROOT/var/www/html +# let's be conservative and exclude codebase files, though there should not be any +rsync -a --exclude \*.spec --exclude .svn ./ $RPM_BUILD_ROOT/var/www/html/registerwizard + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +/var/www/html/registerwizard + +%changelog +* Fri Feb 27 2009 Stephen Soltesz - +- Initial creation of spec file. -- 2.45.2